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