blob: 0bb2d4833d5840dfee291eda0b2a769358979443 [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"
14#include "llvm/ADT/StringExtras.h"
15#include "llvm/Support/Regex.h"
16#include <algorithm>
17
18using namespace clang::ast_matchers;
19
20namespace clang {
21namespace tidy {
22namespace objc {
23
24namespace {
25/// The acronyms are from
26/// 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 +000027///
28/// Keep this list sorted.
Ben Hamilton52161a52017-11-13 23:54:31 +000029constexpr char DefaultSpecialAcronyms[] =
Ben Hamilton55c3a322018-01-18 20:51:24 +000030 "ACL;"
31 "API;"
32 "ARGB;"
Ben Hamilton52161a52017-11-13 23:54:31 +000033 "ASCII;"
Ben Hamilton55c3a322018-01-18 20:51:24 +000034 "BGRA;"
Ben Hamilton52161a52017-11-13 23:54:31 +000035 "CMYK;"
Ben Hamilton55c3a322018-01-18 20:51:24 +000036 "DNS;"
37 "FPS;"
Yan Zhang8c298d22018-01-17 00:19:35 +000038 "FTP;"
Ben Hamilton55c3a322018-01-18 20:51:24 +000039 "GIF;"
40 "GPS;"
41 "HD;"
42 "HDR;"
43 "HTML;"
44 "HTTP;"
45 "HTTPS;"
46 "HUD;"
47 "ID;"
48 "JPG;"
49 "JS;"
50 "LAN;"
51 "LZW;"
52 "MDNS;"
53 "MIDI;"
54 "OS;"
55 "PDF;"
56 "PIN;"
57 "PNG;"
58 "POI;"
59 "PSTN;"
60 "PTR;"
61 "QA;"
62 "QOS;"
63 "RGB;"
64 "RGBA;"
65 "RGBX;"
66 "ROM;"
67 "RPC;"
68 "RTF;"
69 "RTL;"
70 "SDK;"
71 "SSO;"
72 "TCP;"
73 "TIFF;"
74 "TTS;"
75 "UI;"
76 "URI;"
77 "URL;"
78 "VC;"
79 "VOIP;"
80 "VPN;"
81 "VR;"
82 "WAN;"
83 "XML";
Ben Hamilton52161a52017-11-13 23:54:31 +000084
85/// For now we will only fix 'CamelCase' property to
86/// 'camelCase'. For other cases the users need to
87/// come up with a proper name by their own.
88/// FIXME: provide fix for snake_case to snakeCase
89FixItHint generateFixItHint(const ObjCPropertyDecl *Decl) {
90 if (isupper(Decl->getName()[0])) {
91 auto NewName = Decl->getName().str();
92 NewName[0] = tolower(NewName[0]);
93 return FixItHint::CreateReplacement(
94 CharSourceRange::getTokenRange(SourceRange(Decl->getLocation())),
95 llvm::StringRef(NewName));
96 }
97 return FixItHint();
98}
99
Yan Zhang8c298d22018-01-17 00:19:35 +0000100std::string validPropertyNameRegex(const std::vector<std::string> &Acronyms) {
101 std::vector<std::string> EscapedAcronyms;
102 EscapedAcronyms.reserve(Acronyms.size());
Ben Hamilton52161a52017-11-13 23:54:31 +0000103 // In case someone defines a custom prefix which includes a regex
104 // special character, escape all the prefixes.
Yan Zhang8c298d22018-01-17 00:19:35 +0000105 std::transform(Acronyms.begin(), Acronyms.end(),
106 std::back_inserter(EscapedAcronyms), [](const std::string& s) {
Ben Hamilton52161a52017-11-13 23:54:31 +0000107 return llvm::Regex::escape(s); });
108 // Allow any of these names:
109 // foo
110 // fooBar
111 // url
112 // urlString
113 // URL
114 // URLString
Yan Zhang8c298d22018-01-17 00:19:35 +0000115 // bundleID
Ben Hamilton52161a52017-11-13 23:54:31 +0000116 return std::string("::((") +
Yan Zhang8c298d22018-01-17 00:19:35 +0000117 llvm::join(EscapedAcronyms.begin(), EscapedAcronyms.end(), "|") +
118 ")[A-Z]?)?[a-z]+[a-z0-9]*([A-Z][a-z0-9]+)*" + "(" +
119 llvm::join(EscapedAcronyms.begin(), EscapedAcronyms.end(), "|") + ")?$";
Ben Hamilton52161a52017-11-13 23:54:31 +0000120}
121} // namespace
122
123PropertyDeclarationCheck::PropertyDeclarationCheck(StringRef Name,
124 ClangTidyContext *Context)
125 : ClangTidyCheck(Name, Context),
126 SpecialAcronyms(utils::options::parseStringList(
127 Options.get("Acronyms", DefaultSpecialAcronyms))) {}
128
129void PropertyDeclarationCheck::registerMatchers(MatchFinder *Finder) {
130 Finder->addMatcher(
131 objcPropertyDecl(
132 // the property name should be in Lower Camel Case like
133 // 'lowerCamelCase'
134 unless(matchesName(validPropertyNameRegex(SpecialAcronyms))))
135 .bind("property"),
136 this);
137}
138
139void PropertyDeclarationCheck::check(const MatchFinder::MatchResult &Result) {
140 const auto *MatchedDecl =
141 Result.Nodes.getNodeAs<ObjCPropertyDecl>("property");
142 assert(MatchedDecl->getName().size() > 0);
143 diag(MatchedDecl->getLocation(),
144 "property name '%0' should use lowerCamelCase style, according to "
145 "the Apple Coding Guidelines")
146 << MatchedDecl->getName() << generateFixItHint(MatchedDecl);
147}
148
149void PropertyDeclarationCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
150 Options.store(Opts, "Acronyms",
151 utils::options::serializeStringList(SpecialAcronyms));
152}
153
154} // namespace objc
155} // namespace tidy
156} // namespace clang