brintos

brintos / llvm-project-archived public Read only

0
0
Text · 4.8 KiB · 690572f Raw
136 lines · cpp
1//===----------------------------------------------------------------------===//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-exception6//7//===----------------------------------------------------------------------===//8 9#include "PropertyDeclarationCheck.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::objc {17 18namespace {19 20// For StandardProperty the naming style is 'lowerCamelCase'.21// For CategoryProperty especially in categories of system class,22// to avoid naming conflict, the suggested naming style is23// 'abc_lowerCamelCase' (adding lowercase prefix followed by '_').24// Regardless of the style, all acronyms and initialisms should be capitalized.25enum NamingStyle {26  StandardProperty = 1,27  CategoryProperty = 2,28};29 30} // namespace31 32/// For now we will only fix 'CamelCase' or 'abc_CamelCase' property to33/// 'camelCase' or 'abc_camelCase'. For other cases the users need to34/// come up with a proper name by their own.35/// FIXME: provide fix for snake_case to snakeCase36static FixItHint generateFixItHint(const ObjCPropertyDecl *Decl,37                                   NamingStyle Style) {38  auto Name = Decl->getName();39  auto NewName = Decl->getName().str();40  size_t Index = 0;41  if (Style == CategoryProperty) {42    const size_t UnderScorePos = Name.find_first_of('_');43    if (UnderScorePos != llvm::StringRef::npos) {44      Index = UnderScorePos + 1;45      NewName.replace(0, Index - 1, Name.substr(0, Index - 1).lower());46    }47  }48  if (Index < Name.size()) {49    NewName[Index] = tolower(NewName[Index]);50    if (NewName != Name) {51      return FixItHint::CreateReplacement(52          CharSourceRange::getTokenRange(SourceRange(Decl->getLocation())),53          llvm::StringRef(NewName));54    }55  }56  return {};57}58 59static std::string validPropertyNameRegex(bool UsedInMatcher) {60  // Allow any of these names:61  // foo62  // fooBar63  // url64  // urlString65  // ID66  // IDs67  // URL68  // URLString69  // bundleID70  // CIColor71  //72  // Disallow names of this form:73  // LongString74  //75  // aRbITRaRyCapS is allowed to avoid generating false positives for names76  // like isVitaminBSupplement, CProgrammingLanguage, and isBeforeM.77  const std::string StartMatcher = UsedInMatcher ? "::" : "^";78  return StartMatcher + "([a-z]|[A-Z][A-Z0-9])[a-z0-9A-Z]*$";79}80 81static bool hasCategoryPropertyPrefix(llvm::StringRef PropertyName) {82  auto RegexExp =83      llvm::Regex("^[a-zA-Z][a-zA-Z0-9]*_[a-zA-Z0-9][a-zA-Z0-9_]+$");84  return RegexExp.match(PropertyName);85}86 87static bool prefixedPropertyNameValid(llvm::StringRef PropertyName) {88  const size_t Start = PropertyName.find_first_of('_');89  assert(Start != llvm::StringRef::npos && Start + 1 < PropertyName.size());90  auto Prefix = PropertyName.substr(0, Start);91  if (Prefix.lower() != Prefix) {92    return false;93  }94  auto RegexExp = llvm::Regex(llvm::StringRef(validPropertyNameRegex(false)));95  return RegexExp.match(PropertyName.substr(Start + 1));96}97 98void PropertyDeclarationCheck::registerMatchers(MatchFinder *Finder) {99  Finder->addMatcher(objcPropertyDecl(100                         // the property name should be in Lower Camel Case like101                         // 'lowerCamelCase'102                         unless(matchesName(validPropertyNameRegex(true))))103                         .bind("property"),104                     this);105}106 107void PropertyDeclarationCheck::check(const MatchFinder::MatchResult &Result) {108  const auto *MatchedDecl =109      Result.Nodes.getNodeAs<ObjCPropertyDecl>("property");110  assert(!MatchedDecl->getName().empty());111  auto *DeclContext = MatchedDecl->getDeclContext();112  auto *CategoryDecl = llvm::dyn_cast<ObjCCategoryDecl>(DeclContext);113 114  if (CategoryDecl != nullptr &&115      hasCategoryPropertyPrefix(MatchedDecl->getName())) {116    if (!prefixedPropertyNameValid(MatchedDecl->getName()) ||117        CategoryDecl->IsClassExtension()) {118      const NamingStyle Style = CategoryDecl->IsClassExtension()119                                    ? StandardProperty120                                    : CategoryProperty;121      diag(MatchedDecl->getLocation(),122           "property name '%0' not using lowerCamelCase style or not prefixed "123           "in a category, according to the Apple Coding Guidelines")124          << MatchedDecl->getName() << generateFixItHint(MatchedDecl, Style);125    }126    return;127  }128  diag(MatchedDecl->getLocation(),129       "property name '%0' not using lowerCamelCase style or not prefixed in "130       "a category, according to the Apple Coding Guidelines")131      << MatchedDecl->getName()132      << generateFixItHint(MatchedDecl, StandardProperty);133}134 135} // namespace clang::tidy::objc136