|  | //===--- FunctionNamingCheck.cpp - clang-tidy -----------------------------===// | 
|  | // | 
|  | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | 
|  | // See https://llvm.org/LICENSE.txt for license information. | 
|  | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | #include "FunctionNamingCheck.h" | 
|  | #include "clang/ASTMatchers/ASTMatchFinder.h" | 
|  | #include "llvm/Support/Regex.h" | 
|  |  | 
|  | using namespace clang::ast_matchers; | 
|  |  | 
|  | namespace clang::tidy::google::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 + ")$"; | 
|  | } | 
|  |  | 
|  | /// For now we will only fix functions of static storage class with names like | 
|  | /// 'functionName' or 'function_name' and convert them to 'FunctionName'. For | 
|  | /// other cases the user must determine an appropriate name on their own. | 
|  | FixItHint generateFixItHint(const FunctionDecl *Decl) { | 
|  | // A fixit can be generated for functions of static storage class but | 
|  | // otherwise the check cannot determine the appropriate function name prefix | 
|  | // to use. | 
|  | if (Decl->getStorageClass() != SC_Static) | 
|  | return {}; | 
|  |  | 
|  | StringRef Name = Decl->getName(); | 
|  | std::string NewName = Decl->getName().str(); | 
|  |  | 
|  | size_t Index = 0; | 
|  | bool AtWordBoundary = true; | 
|  | while (Index < NewName.size()) { | 
|  | char Ch = NewName[Index]; | 
|  | if (isalnum(Ch)) { | 
|  | // Capitalize the first letter after every word boundary. | 
|  | if (AtWordBoundary) { | 
|  | NewName[Index] = toupper(NewName[Index]); | 
|  | AtWordBoundary = false; | 
|  | } | 
|  |  | 
|  | // Advance the index after every alphanumeric character. | 
|  | Index++; | 
|  | } else { | 
|  | // Strip out any characters other than alphanumeric characters. | 
|  | NewName.erase(Index, 1); | 
|  | AtWordBoundary = true; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Generate a fixit hint if the new name is different. | 
|  | if (NewName != Name) | 
|  | return FixItHint::CreateReplacement( | 
|  | CharSourceRange::getTokenRange(SourceRange(Decl->getLocation())), | 
|  | llvm::StringRef(NewName)); | 
|  |  | 
|  | return {}; | 
|  | } | 
|  |  | 
|  | } // namespace | 
|  |  | 
|  | void FunctionNamingCheck::registerMatchers(MatchFinder *Finder) { | 
|  | // Enforce Objective-C function naming conventions on all functions except: | 
|  | // • Functions defined in system headers. | 
|  | // • C++ member functions. | 
|  | // • Namespaced functions. | 
|  | // • Implicitly defined functions. | 
|  | // • The main function. | 
|  | Finder->addMatcher( | 
|  | functionDecl( | 
|  | unless(anyOf(isExpansionInSystemHeader(), cxxMethodDecl(), | 
|  | hasAncestor(namespaceDecl()), isMain(), isImplicit(), | 
|  | matchesName(validFunctionNameRegex(true)), | 
|  | allOf(isStaticStorageClass(), | 
|  | matchesName(validFunctionNameRegex(false)))))) | 
|  | .bind("function"), | 
|  | this); | 
|  | } | 
|  |  | 
|  | void FunctionNamingCheck::check(const MatchFinder::MatchResult &Result) { | 
|  | const auto *MatchedDecl = Result.Nodes.getNodeAs<FunctionDecl>("function"); | 
|  |  | 
|  | bool IsGlobal = MatchedDecl->getStorageClass() != SC_Static; | 
|  | diag(MatchedDecl->getLocation(), | 
|  | "%select{static function|function in global namespace}1 named %0 must " | 
|  | "%select{be in|have an appropriate prefix followed by}1 Pascal case as " | 
|  | "required by Google Objective-C style guide") | 
|  | << MatchedDecl << IsGlobal << generateFixItHint(MatchedDecl); | 
|  | } | 
|  |  | 
|  | } // namespace clang::tidy::google::objc |