FunctionNamingCheck.cpp 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. //===--- FunctionNamingCheck.cpp - clang-tidy -----------------------------===//
  2. //
  3. // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
  4. // See https://llvm.org/LICENSE.txt for license information.
  5. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  6. //
  7. //===----------------------------------------------------------------------===//
  8. #include "FunctionNamingCheck.h"
  9. #include "clang/AST/ASTContext.h"
  10. #include "clang/ASTMatchers/ASTMatchFinder.h"
  11. #include "llvm/Support/Regex.h"
  12. using namespace clang::ast_matchers;
  13. namespace clang::tidy::google::objc {
  14. namespace {
  15. std::string validFunctionNameRegex(bool RequirePrefix) {
  16. // Allow the following name patterns for all functions:
  17. // • ABFoo (prefix + UpperCamelCase)
  18. // • ABURL (prefix + capitalized acronym/initialism)
  19. //
  20. // If no prefix is required, additionally allow the following name patterns:
  21. // • Foo (UpperCamelCase)
  22. // • URL (capitalized acronym/initialism)
  23. //
  24. // The function name following the prefix can contain standard and
  25. // non-standard capitalized character sequences including acronyms,
  26. // initialisms, and prefixes of symbols (e.g., UIColorFromNSString). For this
  27. // reason, the regex only verifies that the function name after the prefix
  28. // begins with a capital letter followed by an arbitrary sequence of
  29. // alphanumeric characters.
  30. //
  31. // If a prefix is required, the regex checks for a capital letter followed by
  32. // another capital letter or number that is part of the prefix and another
  33. // capital letter or number that begins the name following the prefix.
  34. std::string FunctionNameMatcher =
  35. std::string(RequirePrefix ? "[A-Z][A-Z0-9]+" : "") + "[A-Z][a-zA-Z0-9]*";
  36. return std::string("::(") + FunctionNameMatcher + ")$";
  37. }
  38. /// For now we will only fix functions of static storage class with names like
  39. /// 'functionName' or 'function_name' and convert them to 'FunctionName'. For
  40. /// other cases the user must determine an appropriate name on their own.
  41. FixItHint generateFixItHint(const FunctionDecl *Decl) {
  42. // A fixit can be generated for functions of static storage class but
  43. // otherwise the check cannot determine the appropriate function name prefix
  44. // to use.
  45. if (Decl->getStorageClass() != SC_Static)
  46. return FixItHint();
  47. StringRef Name = Decl->getName();
  48. std::string NewName = Decl->getName().str();
  49. size_t Index = 0;
  50. bool AtWordBoundary = true;
  51. while (Index < NewName.size()) {
  52. char Ch = NewName[Index];
  53. if (isalnum(Ch)) {
  54. // Capitalize the first letter after every word boundary.
  55. if (AtWordBoundary) {
  56. NewName[Index] = toupper(NewName[Index]);
  57. AtWordBoundary = false;
  58. }
  59. // Advance the index after every alphanumeric character.
  60. Index++;
  61. } else {
  62. // Strip out any characters other than alphanumeric characters.
  63. NewName.erase(Index, 1);
  64. AtWordBoundary = true;
  65. }
  66. }
  67. // Generate a fixit hint if the new name is different.
  68. if (NewName != Name)
  69. return FixItHint::CreateReplacement(
  70. CharSourceRange::getTokenRange(SourceRange(Decl->getLocation())),
  71. llvm::StringRef(NewName));
  72. return FixItHint();
  73. }
  74. } // namespace
  75. void FunctionNamingCheck::registerMatchers(MatchFinder *Finder) {
  76. // Enforce Objective-C function naming conventions on all functions except:
  77. // • Functions defined in system headers.
  78. // • C++ member functions.
  79. // • Namespaced functions.
  80. // • Implicitly defined functions.
  81. // • The main function.
  82. Finder->addMatcher(
  83. functionDecl(
  84. unless(anyOf(isExpansionInSystemHeader(), cxxMethodDecl(),
  85. hasAncestor(namespaceDecl()), isMain(), isImplicit(),
  86. matchesName(validFunctionNameRegex(true)),
  87. allOf(isStaticStorageClass(),
  88. matchesName(validFunctionNameRegex(false))))))
  89. .bind("function"),
  90. this);
  91. }
  92. void FunctionNamingCheck::check(const MatchFinder::MatchResult &Result) {
  93. const auto *MatchedDecl = Result.Nodes.getNodeAs<FunctionDecl>("function");
  94. bool IsGlobal = MatchedDecl->getStorageClass() != SC_Static;
  95. diag(MatchedDecl->getLocation(),
  96. "%select{static function|function in global namespace}1 named %0 must "
  97. "%select{be in|have an appropriate prefix followed by}1 Pascal case as "
  98. "required by Google Objective-C style guide")
  99. << MatchedDecl << IsGlobal << generateFixItHint(MatchedDecl);
  100. }
  101. } // namespace clang::tidy::google::objc