blob: 62f3096cab0384a66d3282e13b8becfe197b34fb [file] [log] [blame]
Benjamin Kramer14d42d92014-07-15 16:47:09 +00001//===--- NamedParameterCheck.cpp - clang-tidy -------------------*- C++ -*-===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
10#include "NamedParameterCheck.h"
11#include "clang/ASTMatchers/ASTMatchFinder.h"
12#include "clang/ASTMatchers/ASTMatchers.h"
13#include "clang/AST/ASTContext.h"
14
15using namespace clang::ast_matchers;
16
17namespace clang {
18namespace tidy {
19namespace readability {
20
21void NamedParameterCheck::registerMatchers(ast_matchers::MatchFinder *Finder) {
Benjamin Kramer6bcbe6e2014-09-03 13:30:28 +000022 Finder->addMatcher(functionDecl(unless(isInstantiated())).bind("decl"), this);
Benjamin Kramer14d42d92014-07-15 16:47:09 +000023}
24
25void NamedParameterCheck::check(const MatchFinder::MatchResult &Result) {
26 const SourceManager &SM = *Result.SourceManager;
27 const auto *Function = Result.Nodes.getNodeAs<FunctionDecl>("decl");
28 SmallVector<std::pair<const FunctionDecl *, unsigned>, 4> UnnamedParams;
29
30 // Ignore implicitly generated members.
31 if (Function->isImplicit())
32 return;
33
Benjamin Kramer610ba532014-08-04 09:33:58 +000034 // Ignore declarations without a definition if we're not dealing with an
35 // overriden method.
36 const FunctionDecl *Definition = nullptr;
Benjamin Kramere59cd6e2014-08-29 08:58:35 +000037 if ((!Function->isDefined(Definition) || Function->isDefaulted() ||
38 Function->isDeleted()) &&
Benjamin Kramer610ba532014-08-04 09:33:58 +000039 (!isa<CXXMethodDecl>(Function) ||
40 cast<CXXMethodDecl>(Function)->size_overridden_methods() == 0))
41 return;
42
Benjamin Kramer14d42d92014-07-15 16:47:09 +000043 // TODO: Handle overloads.
44 // TODO: We could check that all redeclarations use the same name for
45 // arguments in the same position.
46 for (unsigned I = 0, E = Function->getNumParams(); I != E; ++I) {
47 const ParmVarDecl *Parm = Function->getParamDecl(I);
48 // Look for unnamed parameters.
49 if (!Parm->getName().empty())
50 continue;
51
Benjamin Kramerfec67052014-09-01 09:11:48 +000052 // Don't warn on the dummy argument on post-inc and post-dec operators.
53 if ((Function->getOverloadedOperator() == OO_PlusPlus ||
54 Function->getOverloadedOperator() == OO_MinusMinus) &&
55 Parm->getType()->isSpecificBuiltinType(BuiltinType::Int))
56 continue;
57
Benjamin Kramer14d42d92014-07-15 16:47:09 +000058 // Sanity check the source locations.
59 if (!Parm->getLocation().isValid() || Parm->getLocation().isMacroID() ||
60 !SM.isWrittenInSameFile(Parm->getLocStart(), Parm->getLocation()))
61 continue;
62
63 // Look for comments. We explicitly want to allow idioms like
64 // void foo(int /*unused*/)
65 const char *Begin = SM.getCharacterData(Parm->getLocStart());
66 const char *End = SM.getCharacterData(Parm->getLocation());
67 StringRef Data(Begin, End - Begin);
68 if (Data.find("/*") != StringRef::npos)
69 continue;
70
71 UnnamedParams.push_back(std::make_pair(Function, I));
72 }
73
74 // Emit only one warning per function but fixits for all unnamed parameters.
75 if (!UnnamedParams.empty()) {
76 const ParmVarDecl *FirstParm =
77 UnnamedParams.front().first->getParamDecl(UnnamedParams.front().second);
78 auto D = diag(FirstParm->getLocation(),
79 "all parameters should be named in a function");
80
81 for (auto P : UnnamedParams) {
Benjamin Kramer78cd5462014-08-04 09:42:18 +000082 // Fallback to an unused marker.
83 StringRef NewName = "unused";
84
Benjamin Kramer14d42d92014-07-15 16:47:09 +000085 // If the method is overridden, try to copy the name from the base method
86 // into the overrider.
Benjamin Kramer14d42d92014-07-15 16:47:09 +000087 const auto *M = dyn_cast<CXXMethodDecl>(P.first);
88 if (M && M->size_overridden_methods() > 0) {
89 const ParmVarDecl *OtherParm =
90 (*M->begin_overridden_methods())->getParamDecl(P.second);
Benjamin Kramer610ba532014-08-04 09:33:58 +000091 StringRef Name = OtherParm->getName();
92 if (!Name.empty())
93 NewName = Name;
Benjamin Kramer14d42d92014-07-15 16:47:09 +000094 }
95
Benjamin Kramer610ba532014-08-04 09:33:58 +000096 // If the definition has a named parameter use that name.
97 if (Definition) {
98 const ParmVarDecl *DefParm = Definition->getParamDecl(P.second);
99 StringRef Name = DefParm->getName();
100 if (!Name.empty())
101 NewName = Name;
102 }
103
104 // Now insert the comment. Note that getLocation() points to the place
105 // where the name would be, this allows us to also get complex cases like
106 // function pointers right.
107 const ParmVarDecl *Parm = P.first->getParamDecl(P.second);
108 D << FixItHint::CreateInsertion(Parm->getLocation(),
109 " /*" + NewName.str() + "*/");
Benjamin Kramer14d42d92014-07-15 16:47:09 +0000110 }
111 }
112}
113
114} // namespace readability
115} // namespace tidy
116} // namespace clang