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