blob: 09b03fe79f13ceb427283517264472d38ba5939e [file] [log] [blame]
Benjamin Kramer14d42d92014-07-15 16:47:09 +00001//===--- NamedParameterCheck.cpp - clang-tidy -------------------*- C++ -*-===//
2//
Chandler Carruth2946cd72019-01-19 08:50:56 +00003// 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
Benjamin Kramer14d42d92014-07-15 16:47:09 +00006//
7//===----------------------------------------------------------------------===//
8
9#include "NamedParameterCheck.h"
Chandler Carruth3cbd71c2015-01-14 11:24:38 +000010#include "clang/AST/ASTContext.h"
Benjamin Kramer14d42d92014-07-15 16:47:09 +000011#include "clang/ASTMatchers/ASTMatchFinder.h"
12#include "clang/ASTMatchers/ASTMatchers.h"
Benjamin Kramer14d42d92014-07-15 16:47:09 +000013
14using namespace clang::ast_matchers;
15
16namespace clang {
17namespace tidy {
18namespace readability {
19
20void NamedParameterCheck::registerMatchers(ast_matchers::MatchFinder *Finder) {
Benjamin Kramer6bcbe6e2014-09-03 13:30:28 +000021 Finder->addMatcher(functionDecl(unless(isInstantiated())).bind("decl"), this);
Benjamin Kramer14d42d92014-07-15 16:47:09 +000022}
23
24void NamedParameterCheck::check(const MatchFinder::MatchResult &Result) {
25 const SourceManager &SM = *Result.SourceManager;
26 const auto *Function = Result.Nodes.getNodeAs<FunctionDecl>("decl");
27 SmallVector<std::pair<const FunctionDecl *, unsigned>, 4> UnnamedParams;
28
29 // Ignore implicitly generated members.
30 if (Function->isImplicit())
31 return;
32
Benjamin Kramer610ba532014-08-04 09:33:58 +000033 // Ignore declarations without a definition if we're not dealing with an
34 // overriden method.
35 const FunctionDecl *Definition = nullptr;
Benjamin Kramere59cd6e2014-08-29 08:58:35 +000036 if ((!Function->isDefined(Definition) || Function->isDefaulted() ||
37 Function->isDeleted()) &&
Benjamin Kramer610ba532014-08-04 09:33:58 +000038 (!isa<CXXMethodDecl>(Function) ||
39 cast<CXXMethodDecl>(Function)->size_overridden_methods() == 0))
40 return;
41
Benjamin Kramer14d42d92014-07-15 16:47:09 +000042 // TODO: Handle overloads.
43 // TODO: We could check that all redeclarations use the same name for
44 // arguments in the same position.
45 for (unsigned I = 0, E = Function->getNumParams(); I != E; ++I) {
46 const ParmVarDecl *Parm = Function->getParamDecl(I);
Alexander Kornienko272397b2015-11-06 00:19:21 +000047 if (Parm->isImplicit())
48 continue;
Benjamin Kramer14d42d92014-07-15 16:47:09 +000049 // Look for unnamed parameters.
50 if (!Parm->getName().empty())
51 continue;
52
Benjamin Kramerfec67052014-09-01 09:11:48 +000053 // Don't warn on the dummy argument on post-inc and post-dec operators.
54 if ((Function->getOverloadedOperator() == OO_PlusPlus ||
55 Function->getOverloadedOperator() == OO_MinusMinus) &&
56 Parm->getType()->isSpecificBuiltinType(BuiltinType::Int))
57 continue;
58
Benjamin Kramer14d42d92014-07-15 16:47:09 +000059 // Sanity check the source locations.
60 if (!Parm->getLocation().isValid() || Parm->getLocation().isMacroID() ||
Stephen Kelly43465bf2018-08-09 22:42:26 +000061 !SM.isWrittenInSameFile(Parm->getBeginLoc(), Parm->getLocation()))
Benjamin Kramer14d42d92014-07-15 16:47:09 +000062 continue;
63
Alexander Kornienkof5a57c92014-09-25 08:16:24 +000064 // Skip gmock testing::Unused parameters.
65 if (auto Typedef = Parm->getType()->getAs<clang::TypedefType>())
66 if (Typedef->getDecl()->getQualifiedNameAsString() == "testing::Unused")
67 continue;
68
Alexander Kornienkoe20ce072014-11-05 11:08:39 +000069 // Skip std::nullptr_t.
70 if (Parm->getType().getCanonicalType()->isNullPtrType())
71 continue;
72
Benjamin Kramer14d42d92014-07-15 16:47:09 +000073 // Look for comments. We explicitly want to allow idioms like
74 // void foo(int /*unused*/)
Stephen Kelly43465bf2018-08-09 22:42:26 +000075 const char *Begin = SM.getCharacterData(Parm->getBeginLoc());
Benjamin Kramer14d42d92014-07-15 16:47:09 +000076 const char *End = SM.getCharacterData(Parm->getLocation());
77 StringRef Data(Begin, End - Begin);
78 if (Data.find("/*") != StringRef::npos)
79 continue;
80
81 UnnamedParams.push_back(std::make_pair(Function, I));
82 }
83
84 // Emit only one warning per function but fixits for all unnamed parameters.
85 if (!UnnamedParams.empty()) {
86 const ParmVarDecl *FirstParm =
87 UnnamedParams.front().first->getParamDecl(UnnamedParams.front().second);
88 auto D = diag(FirstParm->getLocation(),
89 "all parameters should be named in a function");
90
91 for (auto P : UnnamedParams) {
Benjamin Kramer78cd5462014-08-04 09:42:18 +000092 // Fallback to an unused marker.
93 StringRef NewName = "unused";
94
Benjamin Kramer14d42d92014-07-15 16:47:09 +000095 // If the method is overridden, try to copy the name from the base method
96 // into the overrider.
Benjamin Kramer14d42d92014-07-15 16:47:09 +000097 const auto *M = dyn_cast<CXXMethodDecl>(P.first);
98 if (M && M->size_overridden_methods() > 0) {
99 const ParmVarDecl *OtherParm =
100 (*M->begin_overridden_methods())->getParamDecl(P.second);
Benjamin Kramer610ba532014-08-04 09:33:58 +0000101 StringRef Name = OtherParm->getName();
102 if (!Name.empty())
103 NewName = Name;
Benjamin Kramer14d42d92014-07-15 16:47:09 +0000104 }
105
Benjamin Kramer610ba532014-08-04 09:33:58 +0000106 // If the definition has a named parameter use that name.
107 if (Definition) {
108 const ParmVarDecl *DefParm = Definition->getParamDecl(P.second);
109 StringRef Name = DefParm->getName();
110 if (!Name.empty())
111 NewName = Name;
112 }
113
114 // Now insert the comment. Note that getLocation() points to the place
115 // where the name would be, this allows us to also get complex cases like
116 // function pointers right.
117 const ParmVarDecl *Parm = P.first->getParamDecl(P.second);
118 D << FixItHint::CreateInsertion(Parm->getLocation(),
119 " /*" + NewName.str() + "*/");
Benjamin Kramer14d42d92014-07-15 16:47:09 +0000120 }
121 }
122}
123
124} // namespace readability
Benjamin Kramer14d42d92014-07-15 16:47:09 +0000125} // namespace tidy
126} // namespace clang