brintos

brintos / llvm-project-archived public Read only

0
0
Text · 13.0 KiB · c496841 Raw
348 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 "InconsistentDeclarationParameterNameCheck.h"10#include "clang/ASTMatchers/ASTMatchFinder.h"11#include "llvm/ADT/STLExtras.h"12 13using namespace clang::ast_matchers;14 15namespace clang::tidy::readability {16 17namespace {18 19AST_MATCHER(FunctionDecl, hasOtherDeclarations) {20  auto It = Node.redecls_begin();21  auto EndIt = Node.redecls_end();22 23  if (It == EndIt)24    return false;25 26  ++It;27  return It != EndIt;28}29 30struct DifferingParamInfo {31  DifferingParamInfo(StringRef SourceName, StringRef OtherName,32                     SourceRange OtherNameRange, bool GenerateFixItHint)33      : SourceName(SourceName), OtherName(OtherName),34        OtherNameRange(OtherNameRange), GenerateFixItHint(GenerateFixItHint) {}35 36  StringRef SourceName;37  StringRef OtherName;38  SourceRange OtherNameRange;39  bool GenerateFixItHint;40};41 42using DifferingParamsContainer = llvm::SmallVector<DifferingParamInfo, 10>;43 44struct InconsistentDeclarationInfo {45  InconsistentDeclarationInfo(SourceLocation DeclarationLocation,46                              DifferingParamsContainer &&DifferingParams)47      : DeclarationLocation(DeclarationLocation),48        DifferingParams(std::move(DifferingParams)) {}49 50  SourceLocation DeclarationLocation;51  DifferingParamsContainer DifferingParams;52};53 54using InconsistentDeclarationsContainer =55    llvm::SmallVector<InconsistentDeclarationInfo, 2>;56 57} // namespace58 59static bool60checkIfFixItHintIsApplicable(const FunctionDecl *ParameterSourceDeclaration,61                             const ParmVarDecl *SourceParam,62                             const FunctionDecl *OriginalDeclaration) {63  // Assumptions with regard to function declarations/definition:64  //  * If both function declaration and definition are seen, assume that65  //    definition is most up-to-date, and use it to generate replacements.66  //  * If only function declarations are seen, there is no easy way to tell67  //    which is up-to-date and which is not, so don't do anything.68  // TODO: This may be changed later, but for now it seems the reasonable69  // solution.70  if (!ParameterSourceDeclaration->isThisDeclarationADefinition())71    return false;72 73  // Assumption: if parameter is not referenced in function definition body, it74  // may indicate that it's outdated, so don't touch it.75  if (!SourceParam->isReferenced())76    return false;77 78  // In case there is the primary template definition and (possibly several)79  // template specializations (and each with possibly several redeclarations),80  // it is not at all clear what to change.81  if (OriginalDeclaration->getTemplatedKind() ==82      FunctionDecl::TK_FunctionTemplateSpecialization)83    return false;84 85  // Other cases seem OK to allow replacements.86  return true;87}88 89static bool nameMatch(StringRef L, StringRef R, bool Strict) {90  if (Strict)91    return L.empty() || R.empty() || L == R;92  // We allow two names if one is a prefix/suffix of the other, ignoring case.93  // Important special case: this is true if either parameter has no name!94  return L.starts_with_insensitive(R) || R.starts_with_insensitive(L) ||95         L.ends_with_insensitive(R) || R.ends_with_insensitive(L);96}97 98static DifferingParamsContainer99findDifferingParamsInDeclaration(const FunctionDecl *ParameterSourceDeclaration,100                                 const FunctionDecl *OtherDeclaration,101                                 const FunctionDecl *OriginalDeclaration,102                                 bool Strict) {103  DifferingParamsContainer DifferingParams;104 105  const auto *SourceParamIt = ParameterSourceDeclaration->param_begin();106  const auto *OtherParamIt = OtherDeclaration->param_begin();107 108  while (SourceParamIt != ParameterSourceDeclaration->param_end() &&109         OtherParamIt != OtherDeclaration->param_end()) {110    auto SourceParamName = (*SourceParamIt)->getName();111    auto OtherParamName = (*OtherParamIt)->getName();112 113    // FIXME: Provide a way to extract commented out parameter name from comment114    // next to it.115    if (!nameMatch(SourceParamName, OtherParamName, Strict)) {116      const SourceRange OtherParamNameRange =117          DeclarationNameInfo((*OtherParamIt)->getDeclName(),118                              (*OtherParamIt)->getLocation())119              .getSourceRange();120 121      const bool GenerateFixItHint = checkIfFixItHintIsApplicable(122          ParameterSourceDeclaration, *SourceParamIt, OriginalDeclaration);123 124      DifferingParams.emplace_back(SourceParamName, OtherParamName,125                                   OtherParamNameRange, GenerateFixItHint);126    }127 128    ++SourceParamIt;129    ++OtherParamIt;130  }131 132  return DifferingParams;133}134 135static InconsistentDeclarationsContainer136findInconsistentDeclarations(const FunctionDecl *OriginalDeclaration,137                             const FunctionDecl *ParameterSourceDeclaration,138                             SourceManager &SM, bool Strict) {139  InconsistentDeclarationsContainer InconsistentDeclarations;140  const SourceLocation ParameterSourceLocation =141      ParameterSourceDeclaration->getLocation();142 143  for (const FunctionDecl *OtherDeclaration : OriginalDeclaration->redecls()) {144    const SourceLocation OtherLocation = OtherDeclaration->getLocation();145    if (OtherLocation != ParameterSourceLocation) { // Skip self.146      DifferingParamsContainer DifferingParams =147          findDifferingParamsInDeclaration(ParameterSourceDeclaration,148                                           OtherDeclaration,149                                           OriginalDeclaration, Strict);150      if (!DifferingParams.empty()) {151        InconsistentDeclarations.emplace_back(OtherDeclaration->getLocation(),152                                              std::move(DifferingParams));153      }154    }155  }156 157  // Sort in order of appearance in translation unit to generate clear158  // diagnostics.159  llvm::sort(InconsistentDeclarations,160             [&SM](const InconsistentDeclarationInfo &Info1,161                   const InconsistentDeclarationInfo &Info2) {162               return SM.isBeforeInTranslationUnit(Info1.DeclarationLocation,163                                                   Info2.DeclarationLocation);164             });165  return InconsistentDeclarations;166}167 168static const FunctionDecl *169getParameterSourceDeclaration(const FunctionDecl *OriginalDeclaration) {170  const FunctionTemplateDecl *PrimaryTemplate =171      OriginalDeclaration->getPrimaryTemplate();172  if (PrimaryTemplate != nullptr) {173    // In case of template specializations, use primary template declaration as174    // the source of parameter names.175    return PrimaryTemplate->getTemplatedDecl();176  }177 178  // In other cases, try to change to function definition, if available.179 180  if (OriginalDeclaration->isThisDeclarationADefinition())181    return OriginalDeclaration;182 183  for (const FunctionDecl *OtherDeclaration : OriginalDeclaration->redecls()) {184    if (OtherDeclaration->isThisDeclarationADefinition()) {185      return OtherDeclaration;186    }187  }188 189  // No definition found, so return original declaration.190  return OriginalDeclaration;191}192 193static std::string joinParameterNames(194    const DifferingParamsContainer &DifferingParams,195    llvm::function_ref<StringRef(const DifferingParamInfo &)> ChooseParamName) {196  llvm::SmallString<40> Str;197  bool First = true;198  for (const DifferingParamInfo &ParamInfo : DifferingParams) {199    if (First)200      First = false;201    else202      Str += ", ";203    Str.append({"'", ChooseParamName(ParamInfo), "'"});204  }205  return std::string(Str);206}207 208static void formatDifferingParamsDiagnostic(209    InconsistentDeclarationParameterNameCheck *Check, SourceLocation Location,210    StringRef OtherDeclarationDescription,211    const DifferingParamsContainer &DifferingParams) {212  auto ChooseOtherName = [](const DifferingParamInfo &ParamInfo) {213    return ParamInfo.OtherName;214  };215  auto ChooseSourceName = [](const DifferingParamInfo &ParamInfo) {216    return ParamInfo.SourceName;217  };218 219  auto ParamDiag =220      Check->diag(Location,221                  "differing parameters are named here: (%0), in %1: (%2)",222                  DiagnosticIDs::Level::Note)223      << joinParameterNames(DifferingParams, ChooseOtherName)224      << OtherDeclarationDescription225      << joinParameterNames(DifferingParams, ChooseSourceName);226 227  for (const DifferingParamInfo &ParamInfo : DifferingParams) {228    if (ParamInfo.GenerateFixItHint) {229      ParamDiag << FixItHint::CreateReplacement(230          CharSourceRange::getTokenRange(ParamInfo.OtherNameRange),231          ParamInfo.SourceName);232    }233  }234}235 236static void formatDiagnosticsForDeclarations(237    InconsistentDeclarationParameterNameCheck *Check,238    const FunctionDecl *ParameterSourceDeclaration,239    const FunctionDecl *OriginalDeclaration,240    const InconsistentDeclarationsContainer &InconsistentDeclarations) {241  Check->diag(242      OriginalDeclaration->getLocation(),243      "function %q0 has %1 other declaration%s1 with different parameter names")244      << OriginalDeclaration245      << static_cast<int>(InconsistentDeclarations.size());246  int Count = 1;247  for (const InconsistentDeclarationInfo &InconsistentDeclaration :248       InconsistentDeclarations) {249    Check->diag(InconsistentDeclaration.DeclarationLocation,250                "the %ordinal0 inconsistent declaration seen here",251                DiagnosticIDs::Level::Note)252        << Count;253 254    formatDifferingParamsDiagnostic(255        Check, InconsistentDeclaration.DeclarationLocation,256        "the other declaration", InconsistentDeclaration.DifferingParams);257 258    ++Count;259  }260}261 262static void formatDiagnostics(263    InconsistentDeclarationParameterNameCheck *Check,264    const FunctionDecl *ParameterSourceDeclaration,265    const FunctionDecl *OriginalDeclaration,266    const InconsistentDeclarationsContainer &InconsistentDeclarations,267    StringRef FunctionDescription, StringRef ParameterSourceDescription) {268  for (const InconsistentDeclarationInfo &InconsistentDeclaration :269       InconsistentDeclarations) {270    Check->diag(InconsistentDeclaration.DeclarationLocation,271                "%0 %q1 has a %2 with different parameter names")272        << FunctionDescription << OriginalDeclaration273        << ParameterSourceDescription;274 275    Check->diag(ParameterSourceDeclaration->getLocation(), "the %0 seen here",276                DiagnosticIDs::Level::Note)277        << ParameterSourceDescription;278 279    formatDifferingParamsDiagnostic(280        Check, InconsistentDeclaration.DeclarationLocation,281        ParameterSourceDescription, InconsistentDeclaration.DifferingParams);282  }283}284 285void InconsistentDeclarationParameterNameCheck::storeOptions(286    ClangTidyOptions::OptionMap &Opts) {287  Options.store(Opts, "IgnoreMacros", IgnoreMacros);288  Options.store(Opts, "Strict", Strict);289}290 291void InconsistentDeclarationParameterNameCheck::registerMatchers(292    MatchFinder *Finder) {293  Finder->addMatcher(functionDecl(hasOtherDeclarations()).bind("functionDecl"),294                     this);295}296 297void InconsistentDeclarationParameterNameCheck::check(298    const MatchFinder::MatchResult &Result) {299  const auto *OriginalDeclaration =300      Result.Nodes.getNodeAs<FunctionDecl>("functionDecl");301 302  if (VisitedDeclarations.contains(OriginalDeclaration))303    return; // Avoid multiple warnings.304 305  const FunctionDecl *ParameterSourceDeclaration =306      getParameterSourceDeclaration(OriginalDeclaration);307 308  const InconsistentDeclarationsContainer InconsistentDeclarations =309      findInconsistentDeclarations(OriginalDeclaration,310                                   ParameterSourceDeclaration,311                                   *Result.SourceManager, Strict);312  if (InconsistentDeclarations.empty()) {313    // Avoid unnecessary further visits.314    markRedeclarationsAsVisited(OriginalDeclaration);315    return;316  }317 318  const SourceLocation StartLoc = OriginalDeclaration->getBeginLoc();319  if (StartLoc.isMacroID() && IgnoreMacros) {320    markRedeclarationsAsVisited(OriginalDeclaration);321    return;322  }323 324  if (OriginalDeclaration->getTemplatedKind() ==325      FunctionDecl::TK_FunctionTemplateSpecialization) {326    formatDiagnostics(this, ParameterSourceDeclaration, OriginalDeclaration,327                      InconsistentDeclarations,328                      "function template specialization",329                      "primary template declaration");330  } else if (ParameterSourceDeclaration->isThisDeclarationADefinition()) {331    formatDiagnostics(this, ParameterSourceDeclaration, OriginalDeclaration,332                      InconsistentDeclarations, "function", "definition");333  } else {334    formatDiagnosticsForDeclarations(this, ParameterSourceDeclaration,335                                     OriginalDeclaration,336                                     InconsistentDeclarations);337  }338 339  markRedeclarationsAsVisited(OriginalDeclaration);340}341 342void InconsistentDeclarationParameterNameCheck::markRedeclarationsAsVisited(343    const FunctionDecl *OriginalDeclaration) {344  VisitedDeclarations.insert_range(OriginalDeclaration->redecls());345}346 347} // namespace clang::tidy::readability348