123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117 |
- //===--- 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::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 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 clang::tidy::google::objc
|