blob: c10b2a3d35aeeb74540a36ac16e1afb66f1cfd8c [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) {
22 Finder->addMatcher(
23 functionDecl(
24 unless(hasAncestor(decl(
25 anyOf(recordDecl(ast_matchers::isTemplateInstantiation()),
26 functionDecl(ast_matchers::isTemplateInstantiation()))))))
27 .bind("decl"),
28 this);
29}
30
31void NamedParameterCheck::check(const MatchFinder::MatchResult &Result) {
32 const SourceManager &SM = *Result.SourceManager;
33 const auto *Function = Result.Nodes.getNodeAs<FunctionDecl>("decl");
34 SmallVector<std::pair<const FunctionDecl *, unsigned>, 4> UnnamedParams;
35
36 // Ignore implicitly generated members.
37 if (Function->isImplicit())
38 return;
39
Benjamin Kramer610ba532014-08-04 09:33:58 +000040 // Ignore declarations without a definition if we're not dealing with an
41 // overriden method.
42 const FunctionDecl *Definition = nullptr;
43 if (!Function->isDefined(Definition) &&
44 (!isa<CXXMethodDecl>(Function) ||
45 cast<CXXMethodDecl>(Function)->size_overridden_methods() == 0))
46 return;
47
Benjamin Kramer14d42d92014-07-15 16:47:09 +000048 // TODO: Handle overloads.
49 // TODO: We could check that all redeclarations use the same name for
50 // arguments in the same position.
51 for (unsigned I = 0, E = Function->getNumParams(); I != E; ++I) {
52 const ParmVarDecl *Parm = Function->getParamDecl(I);
53 // Look for unnamed parameters.
54 if (!Parm->getName().empty())
55 continue;
56
57 // Sanity check the source locations.
58 if (!Parm->getLocation().isValid() || Parm->getLocation().isMacroID() ||
59 !SM.isWrittenInSameFile(Parm->getLocStart(), Parm->getLocation()))
60 continue;
61
62 // Look for comments. We explicitly want to allow idioms like
63 // void foo(int /*unused*/)
64 const char *Begin = SM.getCharacterData(Parm->getLocStart());
65 const char *End = SM.getCharacterData(Parm->getLocation());
66 StringRef Data(Begin, End - Begin);
67 if (Data.find("/*") != StringRef::npos)
68 continue;
69
70 UnnamedParams.push_back(std::make_pair(Function, I));
71 }
72
73 // Emit only one warning per function but fixits for all unnamed parameters.
74 if (!UnnamedParams.empty()) {
75 const ParmVarDecl *FirstParm =
76 UnnamedParams.front().first->getParamDecl(UnnamedParams.front().second);
77 auto D = diag(FirstParm->getLocation(),
78 "all parameters should be named in a function");
79
Benjamin Kramer610ba532014-08-04 09:33:58 +000080 // Fallback to an unused marker.
81 StringRef NewName = "unused";
82
Benjamin Kramer14d42d92014-07-15 16:47:09 +000083 for (auto P : UnnamedParams) {
84 // If the method is overridden, try to copy the name from the base method
85 // into the overrider.
Benjamin Kramer14d42d92014-07-15 16:47:09 +000086 const auto *M = dyn_cast<CXXMethodDecl>(P.first);
87 if (M && M->size_overridden_methods() > 0) {
88 const ParmVarDecl *OtherParm =
89 (*M->begin_overridden_methods())->getParamDecl(P.second);
Benjamin Kramer610ba532014-08-04 09:33:58 +000090 StringRef Name = OtherParm->getName();
91 if (!Name.empty())
92 NewName = Name;
Benjamin Kramer14d42d92014-07-15 16:47:09 +000093 }
94
Benjamin Kramer610ba532014-08-04 09:33:58 +000095 // If the definition has a named parameter use that name.
96 if (Definition) {
97 const ParmVarDecl *DefParm = Definition->getParamDecl(P.second);
98 StringRef Name = DefParm->getName();
99 if (!Name.empty())
100 NewName = Name;
101 }
102
103 // Now insert the comment. Note that getLocation() points to the place
104 // where the name would be, this allows us to also get complex cases like
105 // function pointers right.
106 const ParmVarDecl *Parm = P.first->getParamDecl(P.second);
107 D << FixItHint::CreateInsertion(Parm->getLocation(),
108 " /*" + NewName.str() + "*/");
Benjamin Kramer14d42d92014-07-15 16:47:09 +0000109 }
110 }
111}
112
113} // namespace readability
114} // namespace tidy
115} // namespace clang