xref: /llvm-project/clang-tools-extra/clang-tidy/readability/NamedParameterCheck.cpp (revision 92420f4aefbef49c3eccaf678bc23713a59e5eab)
1 //===--- NamedParameterCheck.cpp - clang-tidy -------------------*- C++ -*-===//
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-exception
6 //
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 
14 using namespace clang::ast_matchers;
15 
16 namespace clang::tidy::readability {
17 
registerMatchers(ast_matchers::MatchFinder * Finder)18 void NamedParameterCheck::registerMatchers(ast_matchers::MatchFinder *Finder) {
19   Finder->addMatcher(functionDecl().bind("decl"), this);
20 }
21 
check(const MatchFinder::MatchResult & Result)22 void NamedParameterCheck::check(const MatchFinder::MatchResult &Result) {
23   const SourceManager &SM = *Result.SourceManager;
24   const auto *Function = Result.Nodes.getNodeAs<FunctionDecl>("decl");
25   SmallVector<std::pair<const FunctionDecl *, unsigned>, 4> UnnamedParams;
26 
27   // Ignore declarations without a definition if we're not dealing with an
28   // overriden method.
29   const FunctionDecl *Definition = nullptr;
30   if ((!Function->isDefined(Definition) || Function->isDefaulted() ||
31        Definition->isDefaulted() || Function->isDeleted()) &&
32       (!isa<CXXMethodDecl>(Function) ||
33        cast<CXXMethodDecl>(Function)->size_overridden_methods() == 0))
34     return;
35 
36   // TODO: Handle overloads.
37   // TODO: We could check that all redeclarations use the same name for
38   //       arguments in the same position.
39   for (unsigned I = 0, E = Function->getNumParams(); I != E; ++I) {
40     const ParmVarDecl *Parm = Function->getParamDecl(I);
41     if (Parm->isImplicit())
42       continue;
43     // Look for unnamed parameters.
44     if (!Parm->getName().empty())
45       continue;
46 
47     // Don't warn on the dummy argument on post-inc and post-dec operators.
48     if ((Function->getOverloadedOperator() == OO_PlusPlus ||
49          Function->getOverloadedOperator() == OO_MinusMinus) &&
50         Parm->getType()->isSpecificBuiltinType(BuiltinType::Int))
51       continue;
52 
53     // Sanity check the source locations.
54     if (!Parm->getLocation().isValid() || Parm->getLocation().isMacroID() ||
55         !SM.isWrittenInSameFile(Parm->getBeginLoc(), Parm->getLocation()))
56       continue;
57 
58     // Skip gmock testing::Unused parameters.
59     if (const auto *Typedef = Parm->getType()->getAs<clang::TypedefType>())
60       if (Typedef->getDecl()->getQualifiedNameAsString() == "testing::Unused")
61         continue;
62 
63     // Skip std::nullptr_t.
64     if (Parm->getType().getCanonicalType()->isNullPtrType())
65       continue;
66 
67     // Look for comments. We explicitly want to allow idioms like
68     // void foo(int /*unused*/)
69     const char *Begin = SM.getCharacterData(Parm->getBeginLoc());
70     const char *End = SM.getCharacterData(Parm->getLocation());
71     StringRef Data(Begin, End - Begin);
72     if (Data.contains("/*"))
73       continue;
74 
75     UnnamedParams.push_back(std::make_pair(Function, I));
76   }
77 
78   // Emit only one warning per function but fixits for all unnamed parameters.
79   if (!UnnamedParams.empty()) {
80     const ParmVarDecl *FirstParm =
81         UnnamedParams.front().first->getParamDecl(UnnamedParams.front().second);
82     auto D = diag(FirstParm->getLocation(),
83                   "all parameters should be named in a function");
84 
85     for (auto P : UnnamedParams) {
86       // Fallback to an unused marker.
87       StringRef NewName = "unused";
88 
89       // If the method is overridden, try to copy the name from the base method
90       // into the overrider.
91       const auto *M = dyn_cast<CXXMethodDecl>(P.first);
92       if (M && M->size_overridden_methods() > 0) {
93         const ParmVarDecl *OtherParm =
94             (*M->begin_overridden_methods())->getParamDecl(P.second);
95         StringRef Name = OtherParm->getName();
96         if (!Name.empty())
97           NewName = Name;
98       }
99 
100       // If the definition has a named parameter use that name.
101       if (Definition) {
102         const ParmVarDecl *DefParm = Definition->getParamDecl(P.second);
103         StringRef Name = DefParm->getName();
104         if (!Name.empty())
105           NewName = Name;
106       }
107 
108       // Now insert the comment. Note that getLocation() points to the place
109       // where the name would be, this allows us to also get complex cases like
110       // function pointers right.
111       const ParmVarDecl *Parm = P.first->getParamDecl(P.second);
112       D << FixItHint::CreateInsertion(Parm->getLocation(),
113                                       " /*" + NewName.str() + "*/");
114     }
115   }
116 }
117 
118 } // namespace clang::tidy::readability
119