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