| //===--- 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/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 + ")$"; |
| } |
| |
| /// 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 FixItHint(); |
| |
| 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 FixItHint(); |
| } |
| |
| } // 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 objc |
| } // namespace google |
| } // namespace tidy |
| } // namespace clang |