mgehre updated this revision to Diff 208505. mgehre marked 11 inline comments as done. mgehre added a comment.
- Address review comments Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D61749/new/ https://reviews.llvm.org/D61749 Files: clang-tools-extra/clang-tidy/readability/CMakeLists.txt clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp clang-tools-extra/clang-tidy/readability/StaticMethodCheck.cpp clang-tools-extra/clang-tidy/readability/StaticMethodCheck.h clang-tools-extra/docs/ReleaseNotes.rst clang-tools-extra/docs/clang-tidy/checks/list.rst clang-tools-extra/docs/clang-tidy/checks/readability-static-method.rst clang-tools-extra/test/clang-tidy/readability-static-method.cpp
Index: clang-tools-extra/test/clang-tidy/readability-static-method.cpp =================================================================== --- /dev/null +++ clang-tools-extra/test/clang-tidy/readability-static-method.cpp @@ -0,0 +1,195 @@ +// RUN: %check_clang_tidy %s readability-static-method %t + +class DoNotMakeEmptyStatic { + void emptyMethod() {} + void empty_method_out_of_line(); +}; + +void DoNotMakeEmptyStatic::empty_method_out_of_line() {} + +class A { + int field; + const int const_field; + static int static_field; + + void no_use() { + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: method 'no_use' can be made static + // CHECK-FIXES: {{^}} static void no_use() { + int i = 1; + } + + int read_field() { + return field; + } + + void write_field() { + field = 1; + } + + int call_non_const_member() { return read_field(); } + + int call_static_member() { + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: method 'call_static_member' can be made static + // CHECK-FIXES: {{^}} static int call_static_member() { + already_static(); + } + + int read_static() { + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: method 'read_static' can be made static + // CHECK-FIXES: {{^}} static int read_static() { + return static_field; + } + void write_static() { + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: method 'write_static' can be made static + // CHECK-FIXES: {{^}} static void write_static() { + static_field = 1; + } + + static int already_static() { return static_field; } + + int already_const() const { return field; } + + int already_const_convert_to_static() const { + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: method 'already_const_convert_to_static' can be made static + // CHECK-FIXES: {{^}} static int already_const_convert_to_static() { + return static_field; + } + + static int out_of_line_already_static(); + + void out_of_line_call_static(); + // CHECK-FIXES: {{^}} static void out_of_line_call_static(); + int out_of_line_const_to_static() const; + // CHECK-FIXES: {{^}} static int out_of_line_const_to_static() ; +}; + +int A::out_of_line_already_static() { return 0; } + +void A::out_of_line_call_static() { + // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: method 'out_of_line_call_static' can be made static + // CHECK-FIXES: {{^}}void A::out_of_line_call_static() { + already_static(); +} + +int A::out_of_line_const_to_static() const { + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: method 'out_of_line_const_to_static' can be made static + // CHECK-FIXES: {{^}}int A::out_of_line_const_to_static() { + return 0; +} + +struct KeepVirtual { + virtual int f() { return 0; } + virtual int h() const { return 0; } +}; + +struct KeepVirtualDerived : public KeepVirtual { + int f() { return 0; } + int h() const override { return 0; } +}; + +// Don't add 'static' to special member functions and operators. +struct KeepSpecial { + KeepSpecial() { int L = 0; } + ~KeepSpecial() { int L = 0; } + int operator+() { return 0; } + operator int() { return 0; } +}; + +void KeepLambdas() { + using FT = int (*)(); + auto F = static_cast<FT>([]() { return 0; }); + auto F2 = []() { return 0; }; +} + +template <class Base> +struct KeepWithTemplateBase : public Base { + int i; + // We cannot make these methods static because they might need to override + // a function from Base. + int static_f() { return 0; } +}; + +template <class T> +struct KeepTemplateClass { + int i; + // We cannot make these methods static because a specialization + // might use *this differently. + int static_f() { return 0; } +}; + +struct KeepTemplateMethod { + int i; + // We cannot make these methods static because a specialization + // might use *this differently. + template <class T> + static int static_f() { return 0; } +}; + +void instantiate() { + struct S {}; + KeepWithTemplateBase<S> I1; + I1.static_f(); + + KeepTemplateClass<int> I2; + I2.static_f(); + + KeepTemplateMethod I3; + I3.static_f<int>(); +} + +struct Trailing { + auto g() const -> int { + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: method 'g' can be made static + // CHECK-FIXES: {{^}} static auto g() -> int { + return 0; + } + + void vol() volatile { + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: method 'vol' can be made static + return; + } + + void ref() const & { + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: method 'ref' can be made static + return; + } + void refref() const && { + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: method 'refref' can be made static + return; + } +}; + +struct UnevaluatedContext { + void f() { sizeof(this); } + + void noex() noexcept(noexcept(this)); +}; + +struct NoFixitInMacro { +#define CONST const + int no_use_macro_const() CONST { + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: method 'no_use_macro_const' can be made static + return 0; + } + +#define ADD_CONST(F) F const + int ADD_CONST(no_use_macro2()) { + return 0; + } + +#define FUN no_use_macro() + int i; + int FUN { + return i; + } + +#define T(FunctionName, Keyword) \ + Keyword int FunctionName() { return 0; } +#define EMPTY + T(A, EMPTY) + T(B, static) + +#define T2(FunctionName) \ + int FunctionName() { return 0; } + T2(A2) +}; Index: clang-tools-extra/docs/clang-tidy/checks/readability-static-method.rst =================================================================== --- /dev/null +++ clang-tools-extra/docs/clang-tidy/checks/readability-static-method.rst @@ -0,0 +1,14 @@ +.. title:: clang-tidy - readability-static-method + +readability-static-method +========================= + +Finds non-static member functions that can be made ``static`` +because the functions don't use ``this``. + +After applying modifications as suggested by the check, runnnig the check again +might find more opportunities to mark member functions ``static``. + +After making a member function ``static``, you might want to run the check +`readability-static-accessed-through-instance` to replace calls like +``Instance.method()`` by ``Class::method()``. Index: clang-tools-extra/docs/clang-tidy/checks/list.rst =================================================================== --- clang-tools-extra/docs/clang-tidy/checks/list.rst +++ clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -275,6 +275,7 @@ readability-simplify-subscript-expr readability-static-accessed-through-instance readability-static-definition-in-anonymous-namespace + readability-static-method readability-string-compare readability-uniqueptr-delete-release readability-uppercase-literal-suffix Index: clang-tools-extra/docs/ReleaseNotes.rst =================================================================== --- clang-tools-extra/docs/ReleaseNotes.rst +++ clang-tools-extra/docs/ReleaseNotes.rst @@ -163,6 +163,11 @@ Rewrites function signatures to use a trailing return type. +- New :doc:`readability-static-method + <clang-tidy/checks/readability-static-method>` check. + + Finds non-static member functions that can be made ``static``. + Improvements to include-fixer ----------------------------- Index: clang-tools-extra/clang-tidy/readability/StaticMethodCheck.h =================================================================== --- /dev/null +++ clang-tools-extra/clang-tidy/readability/StaticMethodCheck.h @@ -0,0 +1,36 @@ +//===--- StaticMethodCheck.h - clang-tidy -----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_STATICMETHODCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_STATICMETHODCHECK_H + +#include "../ClangTidy.h" + +namespace clang { +namespace tidy { +namespace readability { + +/// This check finds C++ class methods than can be made static +/// because they don't use the 'this' pointer. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/readability-static-method.html +class StaticMethodCheck : public ClangTidyCheck { +public: + StaticMethodCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; +}; + +} // namespace readability +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_STATICMETHODCHECK_H Index: clang-tools-extra/clang-tidy/readability/StaticMethodCheck.cpp =================================================================== --- /dev/null +++ clang-tools-extra/clang-tidy/readability/StaticMethodCheck.cpp @@ -0,0 +1,166 @@ +//===--- StaticMethodCheck.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 "StaticMethodCheck.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 { + +class FindUsageOfThis : public RecursiveASTVisitor<FindUsageOfThis> { +public: + bool Used = false; + + bool VisitCXXThisExpr(const CXXThisExpr *E) { + Used = true; + return false; // Stop traversal. + } +}; + +void StaticMethodCheck::registerMatchers(MatchFinder *Finder) { + Finder->addMatcher( + cxxMethodDecl(unless(isExpansionInSystemHeader()), isDefinition()) + .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); + auto FTL = TSI->getTypeLoc().IgnoreParens().getAs<FunctionTypeLoc>(); + if (!FTL) + return {}; + + 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)}; +} + +// Returns `true` if `Range` is inside a macro definition. +static bool insideMacroDefinition(const MatchFinder::MatchResult &Result, + SourceRange Range) { + return !clang::Lexer::makeFileCharRange( + clang::CharSourceRange::getCharRange(Range), + *Result.SourceManager, Result.Context->getLangOpts()) + .isValid(); +} + +void StaticMethodCheck::check(const MatchFinder::MatchResult &Result) { + const auto *Definition = Result.Nodes.getNodeAs<CXXMethodDecl>("x"); + if (!Definition->isUserProvided()) + return; + if (Definition->isStatic()) + return; // Nothing we can improve. + if (Definition->isVirtual()) + return; + if (Definition->getParent()->hasAnyDependentBases()) + return; // Method might become virtual depending on template base class. + if (Definition->hasTrivialBody()) + return; + if (Definition->isOverloadedOperator()) + return; + if (Definition->getTemplateSpecializationKind() != TSK_Undeclared) + return; + if (Definition->getTemplatedKind() != FunctionDecl::TK_NonTemplate) + return; // We might not see all specializations. + if (isa<CXXConstructorDecl>(Definition) || + isa<CXXDestructorDecl>(Definition) || isa<CXXConversionDecl>(Definition)) + return; + if (Definition->isDependentContext()) + return; + if (Definition->getParent()->isLambda()) + return; + + if (insideMacroDefinition( + Result, + Definition->getTypeSourceInfo()->getTypeLoc().getSourceRange())) + return; + + const CXXMethodDecl *Declaration = Definition->getCanonicalDecl(); + + if (Declaration != Definition && + insideMacroDefinition( + Result, + Declaration->getTypeSourceInfo()->getTypeLoc().getSourceRange())) + return; + + FindUsageOfThis UsageOfThis; + // TraverseStmt does not modify its argument. + UsageOfThis.TraverseStmt(const_cast<Stmt *>(Definition->getBody())); + + if (UsageOfThis.Used) + return; + + // 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->isVolatile() || Definition->getRefQualifier() != RQ_None) + return; + + 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 Index: clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp =================================================================== --- clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp +++ clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp @@ -38,6 +38,7 @@ #include "SimplifySubscriptExprCheck.h" #include "StaticAccessedThroughInstanceCheck.h" #include "StaticDefinitionInAnonymousNamespaceCheck.h" +#include "StaticMethodCheck.h" #include "StringCompareCheck.h" #include "UniqueptrDeleteReleaseCheck.h" #include "UppercaseLiteralSuffixCheck.h" @@ -91,6 +92,8 @@ "readability-static-accessed-through-instance"); CheckFactories.registerCheck<StaticDefinitionInAnonymousNamespaceCheck>( "readability-static-definition-in-anonymous-namespace"); + CheckFactories.registerCheck<StaticMethodCheck>( + "readability-static-method"); CheckFactories.registerCheck<StringCompareCheck>( "readability-string-compare"); CheckFactories.registerCheck<readability::NamedParameterCheck>( Index: clang-tools-extra/clang-tidy/readability/CMakeLists.txt =================================================================== --- clang-tools-extra/clang-tidy/readability/CMakeLists.txt +++ clang-tools-extra/clang-tidy/readability/CMakeLists.txt @@ -32,6 +32,7 @@ SimplifySubscriptExprCheck.cpp StaticAccessedThroughInstanceCheck.cpp StaticDefinitionInAnonymousNamespaceCheck.cpp + StaticMethodCheck.cpp StringCompareCheck.cpp UniqueptrDeleteReleaseCheck.cpp UppercaseLiteralSuffixCheck.cpp
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits