clang-tools 22.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
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
17
19 ClangTidyContext *Context)
20 : ClangTidyCheck(Name, Context),
21 InsertPlainNamesInForwardDecls(
22 Options.get("InsertPlainNamesInForwardDecls", false)) {}
23
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 an
39 // 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 for
49 // 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 like
79 // void foo(int /*unused*/)
80 const char *Begin = SM.getCharacterData(Parm->getBeginLoc());
81 const char *End = SM.getCharacterData(Parm->getLocation());
82 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 method
102 // 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 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 StringRef Name = DefParm->getName();
116 if (!Name.empty())
117 NewName = Name;
118 }
119
120 // Now insert the fix. Note that getLocation() points to the place
121 // where the name would be, this allows us to also get complex cases like
122 // 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 has
127 // 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::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
cppcoreguidelines::ProBoundsAvoidUncheckedContainerAccess P
llvm::StringMap< ClangTidyValue > OptionMap