clang-tools  3.9.0
InconsistentDeclarationParameterNameCheck.cpp
Go to the documentation of this file.
1 //===--- InconsistentDeclarationParameterNameCheck.cpp - clang-tidy-------===//
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 
11 #include "clang/AST/ASTContext.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 
14 #include <algorithm>
15 #include <functional>
16 #include <sstream>
17 
18 using namespace clang::ast_matchers;
19 
20 namespace clang {
21 namespace tidy {
22 namespace readability {
23 
24 namespace {
25 
26 AST_MATCHER(FunctionDecl, hasOtherDeclarations) {
27  auto It = Node.redecls_begin();
28  auto EndIt = Node.redecls_end();
29 
30  if (It == EndIt)
31  return false;
32 
33  ++It;
34  return It != EndIt;
35 }
36 
37 struct DifferingParamInfo {
38  DifferingParamInfo(StringRef SourceName, StringRef OtherName,
39  SourceRange OtherNameRange, bool GenerateFixItHint)
40  : SourceName(SourceName), OtherName(OtherName),
41  OtherNameRange(OtherNameRange), GenerateFixItHint(GenerateFixItHint) {}
42 
43  StringRef SourceName;
44  StringRef OtherName;
45  SourceRange OtherNameRange;
47 };
48 
49 using DifferingParamsContainer = llvm::SmallVector<DifferingParamInfo, 10>;
50 
51 struct InconsistentDeclarationInfo {
52  InconsistentDeclarationInfo(SourceLocation DeclarationLocation,
53  DifferingParamsContainer &&DifferingParams)
54  : DeclarationLocation(DeclarationLocation),
55  DifferingParams(std::move(DifferingParams)) {}
56 
57  SourceLocation DeclarationLocation;
58  DifferingParamsContainer DifferingParams;
59 };
60 
61 using InconsistentDeclarationsContainer =
62  llvm::SmallVector<InconsistentDeclarationInfo, 2>;
63 
64 bool checkIfFixItHintIsApplicable(
65  const FunctionDecl *ParameterSourceDeclaration,
66  const ParmVarDecl *SourceParam, const FunctionDecl *OriginalDeclaration) {
67  // Assumptions with regard to function declarations/definition:
68  // * If both function declaration and definition are seen, assume that
69  // definition is most up-to-date, and use it to generate replacements.
70  // * If only function declarations are seen, there is no easy way to tell
71  // which is up-to-date and which is not, so don't do anything.
72  // TODO: This may be changed later, but for now it seems the reasonable
73  // solution.
74  if (!ParameterSourceDeclaration->isThisDeclarationADefinition())
75  return false;
76 
77  // Assumption: if parameter is not referenced in function defintion body, it
78  // may indicate that it's outdated, so don't touch it.
79  if (!SourceParam->isReferenced())
80  return false;
81 
82  // In case there is the primary template definition and (possibly several)
83  // template specializations (and each with possibly several redeclarations),
84  // it is not at all clear what to change.
85  if (OriginalDeclaration->getTemplatedKind() ==
86  FunctionDecl::TK_FunctionTemplateSpecialization)
87  return false;
88 
89  // Other cases seem OK to allow replacements.
90  return true;
91 }
92 
93 DifferingParamsContainer
94 findDifferingParamsInDeclaration(const FunctionDecl *ParameterSourceDeclaration,
95  const FunctionDecl *OtherDeclaration,
96  const FunctionDecl *OriginalDeclaration) {
97  DifferingParamsContainer DifferingParams;
98 
99  auto SourceParamIt = ParameterSourceDeclaration->param_begin();
100  auto OtherParamIt = OtherDeclaration->param_begin();
101 
102  while (SourceParamIt != ParameterSourceDeclaration->param_end() &&
103  OtherParamIt != OtherDeclaration->param_end()) {
104  auto SourceParamName = (*SourceParamIt)->getName();
105  auto OtherParamName = (*OtherParamIt)->getName();
106 
107  // FIXME: Provide a way to extract commented out parameter name from comment
108  // next to it.
109  if (!SourceParamName.empty() && !OtherParamName.empty() &&
110  SourceParamName != OtherParamName) {
111  SourceRange OtherParamNameRange =
112  DeclarationNameInfo((*OtherParamIt)->getDeclName(),
113  (*OtherParamIt)->getLocation()).getSourceRange();
114 
115  bool GenerateFixItHint = checkIfFixItHintIsApplicable(
116  ParameterSourceDeclaration, *SourceParamIt, OriginalDeclaration);
117 
118  DifferingParams.emplace_back(SourceParamName, OtherParamName,
119  OtherParamNameRange, GenerateFixItHint);
120  }
121 
122  ++SourceParamIt;
123  ++OtherParamIt;
124  }
125 
126  return DifferingParams;
127 }
128 
129 InconsistentDeclarationsContainer
130 findInconsitentDeclarations(const FunctionDecl *OriginalDeclaration,
131  const FunctionDecl *ParameterSourceDeclaration,
132  SourceManager &SM) {
133  InconsistentDeclarationsContainer InconsistentDeclarations;
134  SourceLocation ParameterSourceLocation =
135  ParameterSourceDeclaration->getLocation();
136 
137  for (const FunctionDecl *OtherDeclaration : OriginalDeclaration->redecls()) {
138  SourceLocation OtherLocation = OtherDeclaration->getLocation();
139  if (OtherLocation != ParameterSourceLocation) { // Skip self.
140  DifferingParamsContainer DifferingParams =
141  findDifferingParamsInDeclaration(ParameterSourceDeclaration,
142  OtherDeclaration,
143  OriginalDeclaration);
144  if (!DifferingParams.empty()) {
145  InconsistentDeclarations.emplace_back(OtherDeclaration->getLocation(),
146  std::move(DifferingParams));
147  }
148  }
149  }
150 
151  // Sort in order of appearance in translation unit to generate clear
152  // diagnostics.
153  std::sort(InconsistentDeclarations.begin(), InconsistentDeclarations.end(),
154  [&SM](const InconsistentDeclarationInfo &Info1,
155  const InconsistentDeclarationInfo &Info2) {
156  return SM.isBeforeInTranslationUnit(Info1.DeclarationLocation,
157  Info2.DeclarationLocation);
158  });
159  return InconsistentDeclarations;
160 }
161 
162 const FunctionDecl *
163 getParameterSourceDeclaration(const FunctionDecl *OriginalDeclaration) {
164  const FunctionTemplateDecl *PrimaryTemplate =
165  OriginalDeclaration->getPrimaryTemplate();
166  if (PrimaryTemplate != nullptr) {
167  // In case of template specializations, use primary template declaration as
168  // the source of parameter names.
169  return PrimaryTemplate->getTemplatedDecl();
170  }
171 
172  // In other cases, try to change to function definition, if available.
173 
174  if (OriginalDeclaration->isThisDeclarationADefinition())
175  return OriginalDeclaration;
176 
177  for (const FunctionDecl *OtherDeclaration : OriginalDeclaration->redecls()) {
178  if (OtherDeclaration->isThisDeclarationADefinition()) {
179  return OtherDeclaration;
180  }
181  }
182 
183  // No definition found, so return original declaration.
184  return OriginalDeclaration;
185 }
186 
187 std::string joinParameterNames(
188  const DifferingParamsContainer &DifferingParams,
189  llvm::function_ref<StringRef(const DifferingParamInfo &)> ChooseParamName) {
190  llvm::SmallVector<char, 40> Buffer;
191  llvm::raw_svector_ostream Str(Buffer);
192  bool First = true;
193  for (const DifferingParamInfo &ParamInfo : DifferingParams) {
194  if (First)
195  First = false;
196  else
197  Str << ", ";
198 
199  Str << "'" << ChooseParamName(ParamInfo).str() << "'";
200  }
201  return Str.str().str();
202 }
203 
204 void formatDifferingParamsDiagnostic(
205  InconsistentDeclarationParameterNameCheck *Check,
206  SourceLocation Location, StringRef OtherDeclarationDescription,
207  const DifferingParamsContainer &DifferingParams) {
208  auto ChooseOtherName =
209  [](const DifferingParamInfo &ParamInfo) { return ParamInfo.OtherName; };
210  auto ChooseSourceName =
211  [](const DifferingParamInfo &ParamInfo) { return ParamInfo.SourceName; };
212 
213  auto ParamDiag =
214  Check->diag(Location,
215  "differing parameters are named here: (%0), in %1: (%2)",
216  DiagnosticIDs::Level::Note)
217  << joinParameterNames(DifferingParams, ChooseOtherName)
218  << OtherDeclarationDescription
219  << joinParameterNames(DifferingParams, ChooseSourceName);
220 
221  for (const DifferingParamInfo &ParamInfo : DifferingParams) {
222  if (ParamInfo.GenerateFixItHint) {
223  ParamDiag << FixItHint::CreateReplacement(
224  CharSourceRange::getTokenRange(ParamInfo.OtherNameRange),
225  ParamInfo.SourceName);
226  }
227  }
228 }
229 
230 void formatDiagnosticsForDeclarations(
231  InconsistentDeclarationParameterNameCheck *Check,
232  const FunctionDecl *ParameterSourceDeclaration,
233  const FunctionDecl *OriginalDeclaration,
234  const InconsistentDeclarationsContainer &InconsistentDeclarations) {
235  Check->diag(
236  OriginalDeclaration->getLocation(),
237  "function %q0 has %1 other declaration%s1 with different parameter names")
238  << OriginalDeclaration
239  << static_cast<int>(InconsistentDeclarations.size());
240  int Count = 1;
241  for (const InconsistentDeclarationInfo &InconsistentDeclaration :
242  InconsistentDeclarations) {
243  Check->diag(InconsistentDeclaration.DeclarationLocation,
244  "the %ordinal0 inconsistent declaration seen here",
245  DiagnosticIDs::Level::Note)
246  << Count;
247 
248  formatDifferingParamsDiagnostic(
249  Check, InconsistentDeclaration.DeclarationLocation,
250  "the other declaration", InconsistentDeclaration.DifferingParams);
251 
252  ++Count;
253  }
254 }
255 
256 void formatDiagnostics(
257  InconsistentDeclarationParameterNameCheck *Check,
258  const FunctionDecl *ParameterSourceDeclaration,
259  const FunctionDecl *OriginalDeclaration,
260  const InconsistentDeclarationsContainer &InconsistentDeclarations,
261  StringRef FunctionDescription, StringRef ParameterSourceDescription) {
262  for (const InconsistentDeclarationInfo &InconsistentDeclaration :
263  InconsistentDeclarations) {
264  Check->diag(InconsistentDeclaration.DeclarationLocation,
265  "%0 %q1 has a %2 with different parameter names")
266  << FunctionDescription << OriginalDeclaration
267  << ParameterSourceDescription;
268 
269  Check->diag(ParameterSourceDeclaration->getLocation(), "the %0 seen here",
270  DiagnosticIDs::Level::Note)
271  << ParameterSourceDescription;
272 
273  formatDifferingParamsDiagnostic(
274  Check, InconsistentDeclaration.DeclarationLocation,
275  ParameterSourceDescription, InconsistentDeclaration.DifferingParams);
276  }
277 }
278 
279 } // anonymous namespace
280 
281 void InconsistentDeclarationParameterNameCheck::registerMatchers(
282  MatchFinder *Finder) {
283  Finder->addMatcher(functionDecl(unless(isImplicit()), hasOtherDeclarations())
284  .bind("functionDecl"),
285  this);
286 }
287 
288 void InconsistentDeclarationParameterNameCheck::check(
289  const MatchFinder::MatchResult &Result) {
290  const auto *OriginalDeclaration =
291  Result.Nodes.getNodeAs<FunctionDecl>("functionDecl");
292 
293  if (VisitedDeclarations.count(OriginalDeclaration) > 0)
294  return; // Avoid multiple warnings.
295 
296  const FunctionDecl *ParameterSourceDeclaration =
297  getParameterSourceDeclaration(OriginalDeclaration);
298 
299  InconsistentDeclarationsContainer InconsistentDeclarations =
300  findInconsitentDeclarations(OriginalDeclaration,
301  ParameterSourceDeclaration,
302  *Result.SourceManager);
303  if (InconsistentDeclarations.empty()) {
304  // Avoid unnecessary further visits.
305  markRedeclarationsAsVisited(OriginalDeclaration);
306  return;
307  }
308 
309  if (OriginalDeclaration->getTemplatedKind() ==
310  FunctionDecl::TK_FunctionTemplateSpecialization) {
311  formatDiagnostics(this, ParameterSourceDeclaration, OriginalDeclaration,
312  InconsistentDeclarations,
313  "function template specialization",
314  "primary template declaration");
315  } else if (ParameterSourceDeclaration->isThisDeclarationADefinition()) {
316  formatDiagnostics(this, ParameterSourceDeclaration, OriginalDeclaration,
317  InconsistentDeclarations, "function", "definition");
318  } else {
319  formatDiagnosticsForDeclarations(this, ParameterSourceDeclaration,
320  OriginalDeclaration,
321  InconsistentDeclarations);
322  }
323 
324  markRedeclarationsAsVisited(OriginalDeclaration);
325 }
326 
327 void InconsistentDeclarationParameterNameCheck::markRedeclarationsAsVisited(
328  const FunctionDecl *OriginalDeclaration) {
329  for (const FunctionDecl *Redecl : OriginalDeclaration->redecls()) {
330  VisitedDeclarations.insert(Redecl);
331  }
332 }
333 
334 } // namespace readability
335 } // namespace tidy
336 } // namespace clang
AST_MATCHER(Type, isStrictlyInteger)
SetLongJmpCheck & Check
std::unique_ptr< ast_matchers::MatchFinder > Finder
Definition: ClangTidy.cpp:210
SourceLocation DeclarationLocation
DifferingParamsContainer DifferingParams
SourceManager & SM
const NamedDecl * Result
Definition: USRFinder.cpp:137