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