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

source code of clang-tools-extra/clang-tidy/google/FunctionNamingCheck.cpp