llvm-project/clang-tools-extra/clang-tidy/objc/PropertyDeclarationCheck.cpp

116 lines
3.6 KiB
C++

//===--- PropertyDeclarationCheck.cpp - clang-tidy-------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "PropertyDeclarationCheck.h"
#include "../utils/OptionsUtils.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/Regex.h"
#include <algorithm>
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
namespace objc {
namespace {
/// The acronyms are from
/// https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/CodingGuidelines/Articles/APIAbbreviations.html#//apple_ref/doc/uid/20001285-BCIHCGAE
constexpr char DefaultSpecialAcronyms[] =
"ASCII;"
"PDF;"
"XML;"
"HTML;"
"URL;"
"RTF;"
"HTTP;"
"TIFF;"
"JPG;"
"PNG;"
"GIF;"
"LZW;"
"ROM;"
"RGB;"
"CMYK;"
"MIDI;"
"FTP";
/// For now we will only fix 'CamelCase' property to
/// 'camelCase'. For other cases the users need to
/// come up with a proper name by their own.
/// FIXME: provide fix for snake_case to snakeCase
FixItHint generateFixItHint(const ObjCPropertyDecl *Decl) {
if (isupper(Decl->getName()[0])) {
auto NewName = Decl->getName().str();
NewName[0] = tolower(NewName[0]);
return FixItHint::CreateReplacement(
CharSourceRange::getTokenRange(SourceRange(Decl->getLocation())),
llvm::StringRef(NewName));
}
return FixItHint();
}
std::string validPropertyNameRegex(const std::vector<std::string> &Prefixes) {
std::vector<std::string> EscapedPrefixes;
EscapedPrefixes.reserve(Prefixes.size());
// In case someone defines a custom prefix which includes a regex
// special character, escape all the prefixes.
std::transform(Prefixes.begin(), Prefixes.end(),
std::back_inserter(EscapedPrefixes), [](const std::string& s) {
return llvm::Regex::escape(s); });
// Allow any of these names:
// foo
// fooBar
// url
// urlString
// URL
// URLString
return std::string("::((") +
llvm::join(EscapedPrefixes.begin(), EscapedPrefixes.end(), "|") +
")[A-Z]?)?[a-z]+[a-z0-9]*([A-Z][a-z0-9]+)*$";
}
} // namespace
PropertyDeclarationCheck::PropertyDeclarationCheck(StringRef Name,
ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
SpecialAcronyms(utils::options::parseStringList(
Options.get("Acronyms", DefaultSpecialAcronyms))) {}
void PropertyDeclarationCheck::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher(
objcPropertyDecl(
// the property name should be in Lower Camel Case like
// 'lowerCamelCase'
unless(matchesName(validPropertyNameRegex(SpecialAcronyms))))
.bind("property"),
this);
}
void PropertyDeclarationCheck::check(const MatchFinder::MatchResult &Result) {
const auto *MatchedDecl =
Result.Nodes.getNodeAs<ObjCPropertyDecl>("property");
assert(MatchedDecl->getName().size() > 0);
diag(MatchedDecl->getLocation(),
"property name '%0' should use lowerCamelCase style, according to "
"the Apple Coding Guidelines")
<< MatchedDecl->getName() << generateFixItHint(MatchedDecl);
}
void PropertyDeclarationCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "Acronyms",
utils::options::serializeStringList(SpecialAcronyms));
}
} // namespace objc
} // namespace tidy
} // namespace clang