Index: clang-tidy/google/CMakeLists.txt =================================================================== --- clang-tidy/google/CMakeLists.txt +++ clang-tidy/google/CMakeLists.txt @@ -6,6 +6,7 @@ DefaultArgumentsCheck.cpp ExplicitConstructorCheck.cpp ExplicitMakePairCheck.cpp + FunctionNamingCheck.cpp GlobalNamesInHeadersCheck.cpp GlobalVariableDeclarationCheck.cpp GoogleTidyModule.cpp Index: clang-tidy/google/FunctionNamingCheck.h =================================================================== --- /dev/null +++ clang-tidy/google/FunctionNamingCheck.h @@ -0,0 +1,42 @@ +//===--- FunctionNamingCheck.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_GOOGLE_OBJC_FUNCTION_NAMING_CHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_OBJC_FUNCTION_NAMING_CHECK_H + +#include "../ClangTidy.h" +#include "llvm/ADT/StringRef.h" + +namespace clang { +namespace tidy { +namespace google { +namespace objc { + +/// Finds function names that do not conform to the recommendations of the +/// Google Objective-C Style Guide. Function names should be in upper camel case +/// including capitalized acronyms and initialisms. Functions that are not of +/// static storage class must also have an appropriate prefix. The function +/// `main` is an exception. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/google-objc-function-naming.html +class FunctionNamingCheck : public ClangTidyCheck { +public: + FunctionNamingCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; +}; + +} // namespace objc +} // namespace google +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_OBJC_FUNCTION_NAMING_CHECK_H Index: clang-tidy/google/FunctionNamingCheck.cpp =================================================================== --- /dev/null +++ clang-tidy/google/FunctionNamingCheck.cpp @@ -0,0 +1,83 @@ +//===--- FunctionNamingCheck.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 "FunctionNamingCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "llvm/Support/Regex.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace google { +namespace objc { + +namespace { + +std::string validFunctionNameRegex(bool RequirePrefix) { + // Allow the following name patterns for all functions: + // • ABFoo (prefix + UpperCamelCase) + // • ABURL (prefix + capitalized acronym/initialism) + // + // If no prefix is required, additionally allow the following name patterns: + // • Foo (UpperCamelCase) + // • URL (capitalized acronym/initialism) + // + // The function name following the prefix can contain standard and + // non-standard capitalized character sequences including acronyms, + // initialisms, and prefixes of symbols (e.g., UIColorFromNSString). For this + // reason, the regex only verifies that the function name after the prefix + // begins with a capital letter followed by an arbitrary sequence of + // alphanumeric characters. + // + // If a prefix is required, the regex checks for a capital letter followed by + // another capital letter or number that is part of the prefix and another + // capital letter or number that begins the name following the prefix. + std::string FunctionNameMatcher = + std::string(RequirePrefix ? "[A-Z][A-Z0-9]+" : "") + "[A-Z][a-zA-Z0-9]*"; + return std::string("::(") + FunctionNameMatcher + ")$"; +} + +} // namespace + +void FunctionNamingCheck::registerMatchers(MatchFinder *Finder) { + // This check should only be applied to Objective-C sources. + if (!getLangOpts().ObjC1 && !getLangOpts().ObjC2) { + return; + } + Finder->addMatcher( + functionDecl( + isDefinition(), + unless(anyOf(isMain(), matchesName(validFunctionNameRegex(true)), + allOf(isStaticStorageClass(), + matchesName(validFunctionNameRegex(false)))))) + .bind("function"), + this); +} + +void FunctionNamingCheck::check(const MatchFinder::MatchResult &Result) { + const auto *MatchedDecl = Result.Nodes.getNodeAs("function"); + assert(MatchedDecl != nullptr); + + // Only function definitions other than main should be matched. + assert(MatchedDecl->hasBody()); + assert(!MatchedDecl->isMain()); + + // TODO: Generate a fixit for functions of static storage class. + diag(MatchedDecl->getLocation(), + "function name '%0' not using function naming conventions described by " + "Google Objective-C style guide") + << MatchedDecl->getName(); +} + +} // namespace objc +} // namespace google +} // namespace tidy +} // namespace clang Index: clang-tidy/google/GoogleTidyModule.cpp =================================================================== --- clang-tidy/google/GoogleTidyModule.cpp +++ clang-tidy/google/GoogleTidyModule.cpp @@ -18,6 +18,7 @@ #include "DefaultArgumentsCheck.h" #include "ExplicitConstructorCheck.h" #include "ExplicitMakePairCheck.h" +#include "FunctionNamingCheck.h" #include "GlobalNamesInHeadersCheck.h" #include "GlobalVariableDeclarationCheck.h" #include "IntegerTypesCheck.h" @@ -50,6 +51,8 @@ "google-global-names-in-headers"); CheckFactories.registerCheck( "google-objc-avoid-throwing-exception"); + CheckFactories.registerCheck( + "google-objc-function-naming"); CheckFactories.registerCheck( "google-objc-global-variable-declaration"); CheckFactories.registerCheck( Index: docs/ReleaseNotes.rst =================================================================== --- docs/ReleaseNotes.rst +++ docs/ReleaseNotes.rst @@ -93,6 +93,12 @@ Flags uses of ``absl::StrCat()`` to append to a ``std::string``. Suggests ``absl::StrAppend()`` should be used instead. +- New :doc:`google-objc-function-naming + ` check. + + Checks that function names in function definitions comply with the naming + conventions described in the Google Objective-C Style Guide. + - New :doc:`readability-magic-numbers ` check. Index: docs/clang-tidy/checks/google-objc-function-naming.rst =================================================================== --- /dev/null +++ docs/clang-tidy/checks/google-objc-function-naming.rst @@ -0,0 +1,29 @@ +.. title:: clang-tidy - google-objc-function-naming + +google-objc-function-naming +=========================== + +Finds function definitions in Objective-C files that do not follow the pattern +described in the Google Objective-C Style Guide. + +The corresponding style guide rule can be found here: +https://google.github.io/styleguide/objcguide.html#function-names + +All function names should be in upper camel case. Functions whose storage class +is not static should have an appropriate prefix. + +The following code sample does not follow this pattern: + +.. code-block:: objc + + static bool is_positive(int i) { return i > 0; } + bool IsNegative(int i) { return i < 0; } + +The sample above might be corrected to the following code: + +.. code-block:: objc + + static bool IsPositive(int i) { return i > 0; } + bool *ABCIsNegative(int i) { return i < 0; } + +The check does not currently recommend any fixes. Index: docs/clang-tidy/checks/list.rst =================================================================== --- docs/clang-tidy/checks/list.rst +++ docs/clang-tidy/checks/list.rst @@ -118,6 +118,7 @@ google-explicit-constructor google-global-names-in-headers google-objc-avoid-throwing-exception + google-objc-function-naming google-objc-global-variable-declaration google-readability-braces-around-statements (redirects to readability-braces-around-statements) google-readability-casting Index: test/clang-tidy/google-objc-function-naming.m =================================================================== --- /dev/null +++ test/clang-tidy/google-objc-function-naming.m @@ -0,0 +1,43 @@ +// RUN: %check_clang_tidy %s google-objc-function-naming %t + +typedef _Bool bool; + +static bool ispositive(int a) { return a > 0; } +// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: function name 'ispositive' not using function naming conventions described by Google Objective-C style guide + +static bool is_positive(int a) { return a > 0; } +// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: function name 'is_positive' not using function naming conventions described by Google Objective-C style guide + +static bool isPositive(int a) { return a > 0; } +// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: function name 'isPositive' not using function naming conventions described by Google Objective-C style guide + +static bool Is_Positive(int a) { return a > 0; } +// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: function name 'Is_Positive' not using function naming conventions described by Google Objective-C style guide + +static bool IsPositive(int a) { return a > 0; } + +static const char *md5(const char *str) { return 0; } +// CHECK-MESSAGES: :[[@LINE-1]]:20: warning: function name 'md5' not using function naming conventions described by Google Objective-C style guide + +static const char *MD5(const char *str) { return 0; } + +static const char *URL(void) { return "https://clang.llvm.org/"; } + +static const char *DEFURL(void) { return "https://clang.llvm.org/"; } + +static const char *DEFFooURL(void) { return "https://clang.llvm.org/"; } + +static const char *StringFromNSString(id str) { return ""; } + +bool ispalindrome(const char *str); + +void ABLog_String(const char *str) {} +// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: function name 'ABLog_String' not using function naming conventions described by Google Objective-C style guide + +void ABLogString(const char *str) {} + +const char *ABURL(void) { return "https://clang.llvm.org/"; } + +const char *ABFooURL(void) { return "https://clang.llvm.org/"; } + +int main(int argc, const char **argv) { return 0; } Index: unittests/clang-tidy/GoogleModuleTest.cpp =================================================================== --- unittests/clang-tidy/GoogleModuleTest.cpp +++ unittests/clang-tidy/GoogleModuleTest.cpp @@ -1,6 +1,7 @@ #include "ClangTidyTest.h" #include "google/ExplicitConstructorCheck.h" #include "google/GlobalNamesInHeadersCheck.h" +#include "google/FunctionNamingCheck.h" #include "gtest/gtest.h" using namespace clang::tidy::google; @@ -105,6 +106,28 @@ EXPECT_FALSE(runCheckOnCode("namespace {}", "foo.h")); } +TEST(ObjCFunctionNaming, AllowedStaticFunctionName) { + std::vector Errors; + runCheckOnCode( + "static void FooBar(void) {}", + &Errors, + "input.m"); + EXPECT_EQ(0ul, Errors.size()); +} + +TEST(ObjCFunctionNaming, LowerCamelCaseStaticFunctionName) { + std::vector Errors; + runCheckOnCode( + "static void fooBar(void) {}\n", + &Errors, + "input.m"); + EXPECT_EQ(1ul, Errors.size()); + EXPECT_EQ( + "function name 'fooBar' not using function naming conventions described by " + "Google Objective-C style guide", + Errors[0].Message.Message); +} + } // namespace test } // namespace tidy } // namespace clang