diff options
author | Matthias Gehre <M.Gehre@gmx.de> | 2019-07-16 21:19:00 +0000 |
---|---|---|
committer | Matthias Gehre <M.Gehre@gmx.de> | 2019-07-16 21:19:00 +0000 |
commit | ffca322266fcd431f7f17ae42ef3f665a3157d39 (patch) | |
tree | 46de7531ad2069cd4dd4a480d4f67fb11a10446a /clang-tools-extra/clang-tidy/readability/ConvertMemberFunctionsToStatic.cpp | |
parent | 40580d36c4de7dfbff897ab72cc4e535c33d09c5 (diff) | |
download | llvm-ffca322266fcd431f7f17ae42ef3f665a3157d39.zip llvm-ffca322266fcd431f7f17ae42ef3f665a3157d39.tar.gz llvm-ffca322266fcd431f7f17ae42ef3f665a3157d39.tar.bz2 |
[clang-tidy] initial version of readability-convert-member-functions-to-static
Summary:
Finds non-static member functions that can be made ``static``.
I have run this check (repeatedly) over llvm-project. It made 1708 member functions
``static``. Out of those, I had to exclude 22 via ``NOLINT`` because their address
was taken and stored in a variable of pointer-to-member type (e.g. passed to
llvm::StringSwitch).
It also made 243 member functions ``const``. (This is currently very conservative
to have no false-positives and can hopefully be extended in the future.)
You can find the results here: https://github.com/mgehre/llvm-project/commits/static_const_eval
Reviewers: alexfh, aaron.ballman
Subscribers: mgorny, xazax.hun, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D61749
llvm-svn: 366265
Diffstat (limited to 'clang-tools-extra/clang-tidy/readability/ConvertMemberFunctionsToStatic.cpp')
-rw-r--r-- | clang-tools-extra/clang-tidy/readability/ConvertMemberFunctionsToStatic.cpp | 172 |
1 files changed, 172 insertions, 0 deletions
diff --git a/clang-tools-extra/clang-tidy/readability/ConvertMemberFunctionsToStatic.cpp b/clang-tools-extra/clang-tidy/readability/ConvertMemberFunctionsToStatic.cpp new file mode 100644 index 0000000..f0e5b29 --- /dev/null +++ b/clang-tools-extra/clang-tidy/readability/ConvertMemberFunctionsToStatic.cpp @@ -0,0 +1,172 @@ +//===--- ConvertMemberFunctionsToStatic.cpp - clang-tidy ------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ConvertMemberFunctionsToStatic.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Basic/SourceLocation.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace readability { + +AST_MATCHER(CXXMethodDecl, isStatic) { return Node.isStatic(); } + +AST_MATCHER(CXXMethodDecl, hasTrivialBody) { return Node.hasTrivialBody(); } + +AST_MATCHER(CXXMethodDecl, isOverloadedOperator) { + return Node.isOverloadedOperator(); +} + +AST_MATCHER(CXXRecordDecl, hasAnyDependentBases) { + return Node.hasAnyDependentBases(); +} + +AST_MATCHER(CXXMethodDecl, isTemplate) { + return Node.getTemplatedKind() != FunctionDecl::TK_NonTemplate; +} + +AST_MATCHER(CXXMethodDecl, isDependentContext) { + return Node.isDependentContext(); +} + +AST_MATCHER(CXXMethodDecl, isInsideMacroDefinition) { + const ASTContext &Ctxt = Finder->getASTContext(); + return clang::Lexer::makeFileCharRange( + clang::CharSourceRange::getCharRange( + Node.getTypeSourceInfo()->getTypeLoc().getSourceRange()), + Ctxt.getSourceManager(), Ctxt.getLangOpts()) + .isInvalid(); +} + +AST_MATCHER_P(CXXMethodDecl, hasCanonicalDecl, + ast_matchers::internal::Matcher<CXXMethodDecl>, InnerMatcher) { + return InnerMatcher.matches(*Node.getCanonicalDecl(), Finder, Builder); +} + +AST_MATCHER(CXXMethodDecl, usesThis) { + class FindUsageOfThis : public RecursiveASTVisitor<FindUsageOfThis> { + public: + bool Used = false; + + bool VisitCXXThisExpr(const CXXThisExpr *E) { + Used = true; + return false; // Stop traversal. + } + } UsageOfThis; + + // TraverseStmt does not modify its argument. + UsageOfThis.TraverseStmt(const_cast<Stmt *>(Node.getBody())); + + return UsageOfThis.Used; +} + +void ConvertMemberFunctionsToStatic::registerMatchers(MatchFinder *Finder) { + Finder->addMatcher( + cxxMethodDecl( + isDefinition(), isUserProvided(), + unless(anyOf( + isExpansionInSystemHeader(), isVirtual(), isStatic(), + hasTrivialBody(), isOverloadedOperator(), cxxConstructorDecl(), + cxxDestructorDecl(), cxxConversionDecl(), isTemplate(), + isDependentContext(), + ofClass(anyOf( + isLambda(), + hasAnyDependentBases()) // Method might become virtual + // depending on template base class. + ), + isInsideMacroDefinition(), + hasCanonicalDecl(isInsideMacroDefinition()), usesThis()))) + .bind("x"), + this); +} + +/// \brief Obtain the original source code text from a SourceRange. +static StringRef getStringFromRange(SourceManager &SourceMgr, + const LangOptions &LangOpts, + SourceRange Range) { + if (SourceMgr.getFileID(Range.getBegin()) != + SourceMgr.getFileID(Range.getEnd())) + return {}; + + return Lexer::getSourceText(CharSourceRange(Range, true), SourceMgr, + LangOpts); +} + +static SourceRange getLocationOfConst(const TypeSourceInfo *TSI, + SourceManager &SourceMgr, + const LangOptions &LangOpts) { + assert(TSI); + const auto FTL = TSI->getTypeLoc().IgnoreParens().getAs<FunctionTypeLoc>(); + assert(FTL); + + SourceRange Range{FTL.getRParenLoc().getLocWithOffset(1), + FTL.getLocalRangeEnd()}; + // Inside Range, there might be other keywords and trailing return types. + // Find the exact position of "const". + StringRef Text = getStringFromRange(SourceMgr, LangOpts, Range); + size_t Offset = Text.find("const"); + if (Offset == StringRef::npos) + return {}; + + SourceLocation Start = Range.getBegin().getLocWithOffset(Offset); + return {Start, Start.getLocWithOffset(strlen("const") - 1)}; +} + +void ConvertMemberFunctionsToStatic::check( + const MatchFinder::MatchResult &Result) { + const auto *Definition = Result.Nodes.getNodeAs<CXXMethodDecl>("x"); + + // TODO: For out-of-line declarations, don't modify the source if the header + // is excluded by the -header-filter option. + DiagnosticBuilder Diag = + diag(Definition->getLocation(), "method %0 can be made static") + << Definition; + + // TODO: Would need to remove those in a fix-it. + if (Definition->getMethodQualifiers().hasVolatile() || + Definition->getMethodQualifiers().hasRestrict() || + Definition->getRefQualifier() != RQ_None) + return; + + const CXXMethodDecl *Declaration = Definition->getCanonicalDecl(); + + if (Definition->isConst()) { + // Make sure that we either remove 'const' on both declaration and + // definition or emit no fix-it at all. + SourceRange DefConst = getLocationOfConst(Definition->getTypeSourceInfo(), + *Result.SourceManager, + Result.Context->getLangOpts()); + + if (DefConst.isInvalid()) + return; + + if (Declaration != Definition) { + SourceRange DeclConst = getLocationOfConst( + Declaration->getTypeSourceInfo(), *Result.SourceManager, + Result.Context->getLangOpts()); + + if (DeclConst.isInvalid()) + return; + Diag << FixItHint::CreateRemoval(DeclConst); + } + + // Remove existing 'const' from both declaration and definition. + Diag << FixItHint::CreateRemoval(DefConst); + } + Diag << FixItHint::CreateInsertion(Declaration->getBeginLoc(), "static "); +} + +} // namespace readability +} // namespace tidy +} // namespace clang |