brintos

brintos / llvm-project-archived public Read only

0
0
Text · 5.6 KiB · 1283632 Raw
144 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 "NamedParameterCheck.h"10#include "clang/AST/ASTContext.h"11#include "clang/ASTMatchers/ASTMatchFinder.h"12#include "clang/ASTMatchers/ASTMatchers.h"13 14using namespace clang::ast_matchers;15 16namespace clang::tidy::readability {17 18NamedParameterCheck::NamedParameterCheck(StringRef Name,19                                         ClangTidyContext *Context)20    : ClangTidyCheck(Name, Context),21      InsertPlainNamesInForwardDecls(22          Options.get("InsertPlainNamesInForwardDecls", false)) {}23 24void NamedParameterCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {25  Options.store(Opts, "InsertPlainNamesInForwardDecls",26                InsertPlainNamesInForwardDecls);27}28 29void NamedParameterCheck::registerMatchers(ast_matchers::MatchFinder *Finder) {30  Finder->addMatcher(functionDecl().bind("decl"), this);31}32 33void NamedParameterCheck::check(const MatchFinder::MatchResult &Result) {34  const SourceManager &SM = *Result.SourceManager;35  const auto *Function = Result.Nodes.getNodeAs<FunctionDecl>("decl");36  SmallVector<std::pair<const FunctionDecl *, unsigned>, 4> UnnamedParams;37 38  // Ignore declarations without a definition if we're not dealing with an39  // overriden method.40  const FunctionDecl *Definition = nullptr;41  if ((!Function->isDefined(Definition) || Function->isDefaulted() ||42       Definition->isDefaulted() || Function->isDeleted()) &&43      (!isa<CXXMethodDecl>(Function) ||44       cast<CXXMethodDecl>(Function)->size_overridden_methods() == 0))45    return;46 47  // TODO: Handle overloads.48  // TODO: We could check that all redeclarations use the same name for49  //       arguments in the same position.50  for (unsigned I = 0, E = Function->getNumParams(); I != E; ++I) {51    const ParmVarDecl *Parm = Function->getParamDecl(I);52    if (Parm->isImplicit())53      continue;54    // Look for unnamed parameters.55    if (!Parm->getName().empty())56      continue;57 58    // Don't warn on the dummy argument on post-inc and post-dec operators.59    if ((Function->getOverloadedOperator() == OO_PlusPlus ||60         Function->getOverloadedOperator() == OO_MinusMinus) &&61        Parm->getType()->isSpecificBuiltinType(BuiltinType::Int))62      continue;63 64    // Sanity check the source locations.65    if (!Parm->getLocation().isValid() || Parm->getLocation().isMacroID() ||66        !SM.isWrittenInSameFile(Parm->getBeginLoc(), Parm->getLocation()))67      continue;68 69    // Skip gmock testing::Unused parameters.70    if (const auto *Typedef = Parm->getType()->getAs<clang::TypedefType>())71      if (Typedef->getDecl()->getQualifiedNameAsString() == "testing::Unused")72        continue;73 74    // Skip std::nullptr_t.75    if (Parm->getType().getCanonicalType()->isNullPtrType())76      continue;77 78    // Look for comments. We explicitly want to allow idioms like79    // void foo(int /*unused*/)80    const char *Begin = SM.getCharacterData(Parm->getBeginLoc());81    const char *End = SM.getCharacterData(Parm->getLocation());82    const StringRef Data(Begin, End - Begin);83    if (Data.contains("/*"))84      continue;85 86    UnnamedParams.push_back(std::make_pair(Function, I));87  }88 89  // Emit only one warning per function but fixits for all unnamed parameters.90  if (!UnnamedParams.empty()) {91    const ParmVarDecl *FirstParm =92        UnnamedParams.front().first->getParamDecl(UnnamedParams.front().second);93    auto D = diag(FirstParm->getLocation(),94                  "all parameters should be named in a function");95 96    for (auto P : UnnamedParams) {97      // Fallback to an unused marker.98      static constexpr StringRef FallbackName = "unused";99      StringRef NewName = FallbackName;100 101      // If the method is overridden, try to copy the name from the base method102      // into the overrider.103      const auto *M = dyn_cast<CXXMethodDecl>(P.first);104      if (M && M->size_overridden_methods() > 0) {105        const ParmVarDecl *OtherParm =106            (*M->begin_overridden_methods())->getParamDecl(P.second);107        const StringRef Name = OtherParm->getName();108        if (!Name.empty())109          NewName = Name;110      }111 112      // If the definition has a named parameter use that name.113      if (Definition) {114        const ParmVarDecl *DefParm = Definition->getParamDecl(P.second);115        const StringRef Name = DefParm->getName();116        if (!Name.empty())117          NewName = Name;118      }119 120      // Now insert the fix. Note that getLocation() points to the place121      // where the name would be, this allows us to also get complex cases like122      // function pointers right.123      const ParmVarDecl *Parm = P.first->getParamDecl(P.second);124 125      // The fix depends on the InsertPlainNamesInForwardDecls option,126      // whether this is a forward declaration and whether the parameter has127      // a real name.128      const bool IsForwardDeclaration = (!Definition || Function != Definition);129      if (InsertPlainNamesInForwardDecls && IsForwardDeclaration &&130          NewName != FallbackName) {131        // For forward declarations with InsertPlainNamesInForwardDecls enabled,132        // insert the parameter name without comments.133        D << FixItHint::CreateInsertion(Parm->getLocation(),134                                        " " + NewName.str());135      } else {136        D << FixItHint::CreateInsertion(Parm->getLocation(),137                                        " /*" + NewName.str() + "*/");138      }139    }140  }141}142 143} // namespace clang::tidy::readability144