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 if (Typedef->getDecl()->getQualifiedNameAsString() == "testing::Unused")
105 continue;
106
107 // Skip std::nullptr_t.
108 if (Parm->getType().getCanonicalType()->isNullPtrType())
109 continue;
110
111 // Skip the types configured by the IgnoredTypes option (e.g. standard
112 // tag dispatch types).
113 if (const auto *Record =
114 Parm->getType().getCanonicalType()->getAsCXXRecordDecl()) {
115 const std::string QName = Record->getQualifiedNameAsString();
116 if (llvm::is_contained(IgnoredTypes, QName))
117 continue;
118 }
119
120 // Look for comments. We explicitly want to allow idioms like
121 // void foo(int /*unused*/)
122 const char *Begin = SM.getCharacterData(Parm->getBeginLoc());
123 const char *End = SM.getCharacterData(Parm->getLocation());
124 const StringRef Data(Begin, End - Begin);
125 if (Data.contains("/*"))
126 continue;
127
128 UnnamedParams.emplace_back(Function, I);
129 }
130
131 // Emit only one warning per function but fixits for all unnamed parameters.
132 if (!UnnamedParams.empty()) {
133 const ParmVarDecl *FirstParm =
134 UnnamedParams.front().first->getParamDecl(UnnamedParams.front().second);
135 auto D = diag(FirstParm->getLocation(),
136 "all parameters should be named in a function");
137
138 for (auto P : UnnamedParams) {
139 // Fallback to an unused marker.
140 static constexpr StringRef FallbackName = "unused";
141 StringRef NewName = FallbackName;
142
143 // If the method is overridden, try to copy the name from the base method
144 // into the overrider.
145 const auto *M = dyn_cast<CXXMethodDecl>(P.first);
146 if (M && M->size_overridden_methods() > 0) {
147 const ParmVarDecl *OtherParm =
148 (*M->begin_overridden_methods())->getParamDecl(P.second);
149 const StringRef Name = OtherParm->getName();
150 if (!Name.empty())
151 NewName = Name;
152 }
153
154 // If the definition has a named parameter use that name.
155 if (Definition) {
156 const ParmVarDecl *DefParm = Definition->getParamDecl(P.second);
157 const StringRef Name = DefParm->getName();
158 if (!Name.empty())
159 NewName = Name;
160 }
161
162 // Now insert the fix. Note that getLocation() points to the place
163 // where the name would be, this allows us to also get complex cases like
164 // function pointers right.
165 const ParmVarDecl *Parm = P.first->getParamDecl(P.second);
166
167 // The fix depends on the InsertPlainNamesInForwardDecls option,
168 // whether this is a forward declaration and whether the parameter has
169 // a real name.
170 const bool IsForwardDeclaration = (!Definition || Function != Definition);
171 if (InsertPlainNamesInForwardDecls && IsForwardDeclaration &&
172 NewName != FallbackName) {
173 // For forward declarations with InsertPlainNamesInForwardDecls enabled,
174 // insert the parameter name without comments.
175 D << FixItHint::CreateInsertion(Parm->getLocation(),
176 " " + NewName.str());
177 } else {
178 D << FixItHint::CreateInsertion(Parm->getLocation(),
179 " /*" + NewName.str() + "*/");
180 }
181 }
182 }
183}
184
185} // 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