clang-tools 24.0.0git
NamedParameterCheck.cpp
Go to the documentation of this file.
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-exception
6//
7//===----------------------------------------------------------------------===//
8
11#include "clang/AST/ASTContext.h"
12#include "clang/ASTMatchers/ASTMatchFinder.h"
13#include "clang/ASTMatchers/ASTMatchers.h"
14
15using namespace clang::ast_matchers;
16
18
19// Types whose parameters do not need a name (tag dispatch types, category
20// tags, etc.).
21static constexpr StringRef DefaultIgnoredTypes =
22 "std::adopt_lock_t;"
23 "std::allocator_arg_t;"
24 "std::bidirectional_iterator_tag;"
25 "std::contiguous_iterator_tag;"
26 "std::default_sentinel_t;"
27 "std::defer_lock_t;"
28 "std::destroying_delete_t;"
29 "std::forward_iterator_tag;"
30 "std::from_range_t;"
31 "std::in_place_index_t;"
32 "std::in_place_t;"
33 "std::in_place_type_t;"
34 "std::input_iterator_tag;"
35 "std::nothrow_t;"
36 "std::nostopstate_t;"
37 "std::nullopt_t;"
38 "std::output_iterator_tag;"
39 "std::piecewise_construct_t;"
40 "std::random_access_iterator_tag;"
41 "std::sorted_equivalent_t;"
42 "std::sorted_unique_t;"
43 "std::try_to_lock_t;"
44 "std::unexpect_t;"
45 "std::unreachable_sentinel_t";
46
48 ClangTidyContext *Context)
49 : ClangTidyCheck(Name, Context),
50 InsertPlainNamesInForwardDecls(
51 Options.get("InsertPlainNamesInForwardDecls", false)),
52 IgnoredTypes(utils::options::parseStringList(
53 Options.get("IgnoredTypes", DefaultIgnoredTypes))) {}
54
56 Options.store(Opts, "InsertPlainNamesInForwardDecls",
57 InsertPlainNamesInForwardDecls);
58 Options.store(Opts, "IgnoredTypes",
60}
61
62void NamedParameterCheck::registerMatchers(ast_matchers::MatchFinder *Finder) {
63 Finder->addMatcher(functionDecl().bind("decl"), this);
64}
65
66void NamedParameterCheck::check(const MatchFinder::MatchResult &Result) {
67 const SourceManager &SM = *Result.SourceManager;
68 const auto *Function = Result.Nodes.getNodeAs<FunctionDecl>("decl");
70
71 // Ignore declarations without a definition if we're not dealing with an
72 // overriden method.
73 const FunctionDecl *Definition = nullptr;
74 if ((!Function->isDefined(Definition) || Function->isDefaulted() ||
75 Definition->isDefaulted() || Function->isDeleted()) &&
76 (!isa<CXXMethodDecl>(Function) ||
77 cast<CXXMethodDecl>(Function)->size_overridden_methods() == 0))
78 return;
79
80 // TODO: Handle overloads.
81 // TODO: We could check that all redeclarations use the same name for
82 // arguments in the same position.
83 for (unsigned I = 0, E = Function->getNumParams(); I != E; ++I) {
84 const ParmVarDecl *Parm = Function->getParamDecl(I);
85 if (Parm->isImplicit())
86 continue;
87 // Look for unnamed parameters.
88 if (!Parm->getName().empty())
89 continue;
90
91 // Don't warn on the dummy argument on post-inc and post-dec operators.
92 if ((Function->getOverloadedOperator() == OO_PlusPlus ||
93 Function->getOverloadedOperator() == OO_MinusMinus) &&
94 Parm->getType()->isSpecificBuiltinType(BuiltinType::Int))
95 continue;
96
97 // Sanity check the source locations.
98 if (!Parm->getLocation().isValid() || Parm->getLocation().isMacroID() ||
99 !SM.isWrittenInSameFile(Parm->getBeginLoc(), Parm->getLocation()))
100 continue;
101
102 // Skip gmock testing::Unused parameters.
103 if (const auto *Typedef = Parm->getType()->getAs<TypedefType>();
104 Typedef &&
105 Typedef->getDecl()->getQualifiedNameAsString() == "testing::Unused")
106 continue;
107
108 // Skip std::nullptr_t.
109 if (Parm->getType().getCanonicalType()->isNullPtrType())
110 continue;
111
112 // Skip the types configured by the IgnoredTypes option (e.g. standard
113 // tag dispatch types).
114 if (const auto *Record =
115 Parm->getType().getCanonicalType()->getAsCXXRecordDecl()) {
116 const std::string QName = Record->getQualifiedNameAsString();
117 if (llvm::is_contained(IgnoredTypes, QName))
118 continue;
119 }
120
121 // Look for comments. We explicitly want to allow idioms like
122 // void foo(int /*unused*/)
123 const char *Begin = SM.getCharacterData(Parm->getBeginLoc());
124 const char *End = SM.getCharacterData(Parm->getLocation());
125 const StringRef Data(Begin, End - Begin);
126 if (Data.contains("/*"))
127 continue;
128
129 UnnamedParams.emplace_back(Function, I);
130 }
131
132 // Emit only one warning per function but fixits for all unnamed parameters.
133 if (!UnnamedParams.empty()) {
134 const ParmVarDecl *FirstParm =
135 UnnamedParams.front().first->getParamDecl(UnnamedParams.front().second);
136 const auto D = diag(FirstParm->getLocation(),
137 "all parameters should be named in a function");
138
139 for (const auto P : UnnamedParams) {
140 // Fallback to an unused marker.
141 static constexpr StringRef FallbackName = "unused";
142 StringRef NewName = FallbackName;
143
144 // If the method is overridden, try to copy the name from the base method
145 // into the overrider.
146 const auto *M = dyn_cast<CXXMethodDecl>(P.first);
147 if (M && M->size_overridden_methods() > 0) {
148 const ParmVarDecl *OtherParm =
149 (*M->begin_overridden_methods())->getParamDecl(P.second);
150 const StringRef Name = OtherParm->getName();
151 if (!Name.empty())
152 NewName = Name;
153 }
154
155 // If the definition has a named parameter use that name.
156 if (Definition) {
157 const ParmVarDecl *DefParm = Definition->getParamDecl(P.second);
158 const StringRef Name = DefParm->getName();
159 if (!Name.empty())
160 NewName = Name;
161 }
162
163 // Now insert the fix. Note that getLocation() points to the place
164 // where the name would be, this allows us to also get complex cases like
165 // function pointers right.
166 const ParmVarDecl *Parm = P.first->getParamDecl(P.second);
167
168 // The fix depends on the InsertPlainNamesInForwardDecls option,
169 // whether this is a forward declaration and whether the parameter has
170 // a real name.
171 const bool IsForwardDeclaration = (!Definition || Function != Definition);
172 if (InsertPlainNamesInForwardDecls && IsForwardDeclaration &&
173 NewName != FallbackName) {
174 // For forward declarations with InsertPlainNamesInForwardDecls enabled,
175 // insert the parameter name without comments.
176 D << FixItHint::CreateInsertion(Parm->getLocation(),
177 " " + NewName.str());
178 } else {
179 D << FixItHint::CreateInsertion(Parm->getLocation(),
180 " /*" + NewName.str() + "*/");
181 }
182 }
183 }
184}
185
186} // namespace clang::tidy::readability
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
NamedParameterCheck(StringRef Name, ClangTidyContext *Context)
void registerMatchers(ast_matchers::MatchFinder *Finder) override
static constexpr StringRef DefaultIgnoredTypes
std::string serializeStringList(ArrayRef< StringRef > Strings)
Serialize a sequence of names that can be parsed by parseStringList.
cppcoreguidelines::ProBoundsAvoidUncheckedContainerAccessCheck P
llvm::StringMap< ClangTidyValue > OptionMap