blob: cbcd718c6f03603f773e923a3b762c8a4d850237 [file] [log] [blame]
Ben Hamilton52161a52017-11-13 23:54:31 +00001//===--- PropertyDeclarationCheck.cpp - clang-tidy-------------------------===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
10#include "PropertyDeclarationCheck.h"
11#include "../utils/OptionsUtils.h"
12#include "clang/AST/ASTContext.h"
13#include "clang/ASTMatchers/ASTMatchFinder.h"
Ben Hamiltonf94c10d2018-01-22 15:45:25 +000014#include "llvm/ADT/STLExtras.h"
Ben Hamilton52161a52017-11-13 23:54:31 +000015#include "llvm/ADT/StringExtras.h"
16#include "llvm/Support/Regex.h"
17#include <algorithm>
18
19using namespace clang::ast_matchers;
20
21namespace clang {
22namespace tidy {
23namespace objc {
24
25namespace {
26/// The acronyms are from
27/// https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/CodingGuidelines/Articles/APIAbbreviations.html#//apple_ref/doc/uid/20001285-BCIHCGAE
Ben Hamilton55c3a322018-01-18 20:51:24 +000028///
29/// Keep this list sorted.
Ben Hamiltonf94c10d2018-01-22 15:45:25 +000030constexpr llvm::StringLiteral DefaultSpecialAcronyms[] = {
31 "ACL",
32 "API",
33 "ARGB",
34 "ASCII",
35 "BGRA",
36 "CMYK",
37 "DNS",
38 "FPS",
39 "FTP",
40 "GIF",
41 "GPS",
42 "HD",
43 "HDR",
44 "HTML",
45 "HTTP",
46 "HTTPS",
47 "HUD",
48 "ID",
49 "JPG",
50 "JS",
51 "LAN",
52 "LZW",
53 "MDNS",
54 "MIDI",
55 "OS",
56 "PDF",
57 "PIN",
58 "PNG",
59 "POI",
60 "PSTN",
61 "PTR",
62 "QA",
63 "QOS",
64 "RGB",
65 "RGBA",
66 "RGBX",
67 "ROM",
68 "RPC",
69 "RTF",
70 "RTL",
71 "SDK",
72 "SSO",
73 "TCP",
74 "TIFF",
75 "TTS",
76 "UI",
77 "URI",
78 "URL",
79 "VC",
80 "VOIP",
81 "VPN",
82 "VR",
83 "WAN",
84 "XML",
85};
Ben Hamilton52161a52017-11-13 23:54:31 +000086
87/// For now we will only fix 'CamelCase' property to
88/// 'camelCase'. For other cases the users need to
89/// come up with a proper name by their own.
90/// FIXME: provide fix for snake_case to snakeCase
91FixItHint generateFixItHint(const ObjCPropertyDecl *Decl) {
92 if (isupper(Decl->getName()[0])) {
93 auto NewName = Decl->getName().str();
94 NewName[0] = tolower(NewName[0]);
95 return FixItHint::CreateReplacement(
96 CharSourceRange::getTokenRange(SourceRange(Decl->getLocation())),
97 llvm::StringRef(NewName));
98 }
99 return FixItHint();
100}
101
Ben Hamiltonf94c10d2018-01-22 15:45:25 +0000102std::string validPropertyNameRegex(const std::vector<std::string> &EscapedAcronyms) {
Ben Hamilton52161a52017-11-13 23:54:31 +0000103 // Allow any of these names:
104 // foo
105 // fooBar
106 // url
107 // urlString
108 // URL
109 // URLString
Yan Zhang8c298d22018-01-17 00:19:35 +0000110 // bundleID
Ben Hamilton52161a52017-11-13 23:54:31 +0000111 return std::string("::((") +
Yan Zhang8c298d22018-01-17 00:19:35 +0000112 llvm::join(EscapedAcronyms.begin(), EscapedAcronyms.end(), "|") +
113 ")[A-Z]?)?[a-z]+[a-z0-9]*([A-Z][a-z0-9]+)*" + "(" +
114 llvm::join(EscapedAcronyms.begin(), EscapedAcronyms.end(), "|") + ")?$";
Ben Hamilton52161a52017-11-13 23:54:31 +0000115}
116} // namespace
117
118PropertyDeclarationCheck::PropertyDeclarationCheck(StringRef Name,
119 ClangTidyContext *Context)
120 : ClangTidyCheck(Name, Context),
Ben Hamiltonf94c10d2018-01-22 15:45:25 +0000121 SpecialAcronyms(
122 utils::options::parseStringList(Options.get("Acronyms", ""))),
123 IncludeDefaultAcronyms(Options.get("IncludeDefaultAcronyms", true)) {}
Ben Hamilton52161a52017-11-13 23:54:31 +0000124
125void PropertyDeclarationCheck::registerMatchers(MatchFinder *Finder) {
Ben Hamiltonf94c10d2018-01-22 15:45:25 +0000126 std::vector<std::string> EscapedAcronyms;
127 if (IncludeDefaultAcronyms) {
128 EscapedAcronyms.reserve(llvm::array_lengthof(DefaultSpecialAcronyms) +
129 SpecialAcronyms.size());
130 // No need to regex-escape the default acronyms.
131 EscapedAcronyms.insert(EscapedAcronyms.end(),
132 std::begin(DefaultSpecialAcronyms),
133 std::end(DefaultSpecialAcronyms));
134 } else {
135 EscapedAcronyms.reserve(SpecialAcronyms.size());
136 }
137 // In case someone defines a prefix which includes a regex
138 // special character, regex-escape all the user-defined prefixes.
139 std::transform(SpecialAcronyms.begin(), SpecialAcronyms.end(),
140 std::back_inserter(EscapedAcronyms),
141 [](const std::string &s) { return llvm::Regex::escape(s); });
Ben Hamilton52161a52017-11-13 23:54:31 +0000142 Finder->addMatcher(
143 objcPropertyDecl(
144 // the property name should be in Lower Camel Case like
145 // 'lowerCamelCase'
Ben Hamiltonf94c10d2018-01-22 15:45:25 +0000146 unless(matchesName(validPropertyNameRegex(EscapedAcronyms))))
Ben Hamilton52161a52017-11-13 23:54:31 +0000147 .bind("property"),
148 this);
149}
150
151void PropertyDeclarationCheck::check(const MatchFinder::MatchResult &Result) {
152 const auto *MatchedDecl =
153 Result.Nodes.getNodeAs<ObjCPropertyDecl>("property");
154 assert(MatchedDecl->getName().size() > 0);
155 diag(MatchedDecl->getLocation(),
156 "property name '%0' should use lowerCamelCase style, according to "
157 "the Apple Coding Guidelines")
158 << MatchedDecl->getName() << generateFixItHint(MatchedDecl);
159}
160
161void PropertyDeclarationCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
162 Options.store(Opts, "Acronyms",
163 utils::options::serializeStringList(SpecialAcronyms));
Ben Hamiltonf94c10d2018-01-22 15:45:25 +0000164 Options.store(Opts, "IncludeDefaultAcronyms", IncludeDefaultAcronyms);
Ben Hamilton52161a52017-11-13 23:54:31 +0000165}
166
167} // namespace objc
168} // namespace tidy
169} // namespace clang