clang-tools  8.0.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 bool nameMatch(StringRef L, StringRef R, bool Strict) {
94  if (Strict)
95  return L.empty() || R.empty() || L == R;
96  // We allow two names if one is a prefix/suffix of the other, ignoring case.
97  // Important special case: this is true if either parameter has no name!
98  return L.startswith_lower(R) || R.startswith_lower(L) ||
99  L.endswith_lower(R) || R.endswith_lower(L);
100 }
101 
102 DifferingParamsContainer
103 findDifferingParamsInDeclaration(const FunctionDecl *ParameterSourceDeclaration,
104  const FunctionDecl *OtherDeclaration,
105  const FunctionDecl *OriginalDeclaration,
106  bool Strict) {
107  DifferingParamsContainer DifferingParams;
108 
109  auto SourceParamIt = ParameterSourceDeclaration->param_begin();
110  auto OtherParamIt = OtherDeclaration->param_begin();
111 
112  while (SourceParamIt != ParameterSourceDeclaration->param_end() &&
113  OtherParamIt != OtherDeclaration->param_end()) {
114  auto SourceParamName = (*SourceParamIt)->getName();
115  auto OtherParamName = (*OtherParamIt)->getName();
116 
117  // FIXME: Provide a way to extract commented out parameter name from comment
118  // next to it.
119  if (!nameMatch(SourceParamName, OtherParamName, Strict)) {
120  SourceRange OtherParamNameRange =
121  DeclarationNameInfo((*OtherParamIt)->getDeclName(),
122  (*OtherParamIt)->getLocation())
123  .getSourceRange();
124 
125  bool GenerateFixItHint = checkIfFixItHintIsApplicable(
126  ParameterSourceDeclaration, *SourceParamIt, OriginalDeclaration);
127 
128  DifferingParams.emplace_back(SourceParamName, OtherParamName,
129  OtherParamNameRange, GenerateFixItHint);
130  }
131 
132  ++SourceParamIt;
133  ++OtherParamIt;
134  }
135 
136  return DifferingParams;
137 }
138 
139 InconsistentDeclarationsContainer
140 findInconsistentDeclarations(const FunctionDecl *OriginalDeclaration,
141  const FunctionDecl *ParameterSourceDeclaration,
142  SourceManager &SM, bool Strict) {
143  InconsistentDeclarationsContainer InconsistentDeclarations;
144  SourceLocation ParameterSourceLocation =
145  ParameterSourceDeclaration->getLocation();
146 
147  for (const FunctionDecl *OtherDeclaration : OriginalDeclaration->redecls()) {
148  SourceLocation OtherLocation = OtherDeclaration->getLocation();
149  if (OtherLocation != ParameterSourceLocation) { // Skip self.
150  DifferingParamsContainer DifferingParams =
151  findDifferingParamsInDeclaration(ParameterSourceDeclaration,
152  OtherDeclaration,
153  OriginalDeclaration, Strict);
154  if (!DifferingParams.empty()) {
155  InconsistentDeclarations.emplace_back(OtherDeclaration->getLocation(),
156  std::move(DifferingParams));
157  }
158  }
159  }
160 
161  // Sort in order of appearance in translation unit to generate clear
162  // diagnostics.
163  std::sort(InconsistentDeclarations.begin(), InconsistentDeclarations.end(),
164  [&SM](const InconsistentDeclarationInfo &Info1,
165  const InconsistentDeclarationInfo &Info2) {
166  return SM.isBeforeInTranslationUnit(Info1.DeclarationLocation,
167  Info2.DeclarationLocation);
168  });
169  return InconsistentDeclarations;
170 }
171 
172 const FunctionDecl *
173 getParameterSourceDeclaration(const FunctionDecl *OriginalDeclaration) {
174  const FunctionTemplateDecl *PrimaryTemplate =
175  OriginalDeclaration->getPrimaryTemplate();
176  if (PrimaryTemplate != nullptr) {
177  // In case of template specializations, use primary template declaration as
178  // the source of parameter names.
179  return PrimaryTemplate->getTemplatedDecl();
180  }
181 
182  // In other cases, try to change to function definition, if available.
183 
184  if (OriginalDeclaration->isThisDeclarationADefinition())
185  return OriginalDeclaration;
186 
187  for (const FunctionDecl *OtherDeclaration : OriginalDeclaration->redecls()) {
188  if (OtherDeclaration->isThisDeclarationADefinition()) {
189  return OtherDeclaration;
190  }
191  }
192 
193  // No definition found, so return original declaration.
194  return OriginalDeclaration;
195 }
196 
197 std::string joinParameterNames(
198  const DifferingParamsContainer &DifferingParams,
199  llvm::function_ref<StringRef(const DifferingParamInfo &)> ChooseParamName) {
200  llvm::SmallVector<char, 40> Buffer;
201  llvm::raw_svector_ostream Str(Buffer);
202  bool First = true;
203  for (const DifferingParamInfo &ParamInfo : DifferingParams) {
204  if (First)
205  First = false;
206  else
207  Str << ", ";
208 
209  Str << "'" << ChooseParamName(ParamInfo).str() << "'";
210  }
211  return Str.str().str();
212 }
213 
214 void formatDifferingParamsDiagnostic(
215  InconsistentDeclarationParameterNameCheck *Check, SourceLocation Location,
216  StringRef OtherDeclarationDescription,
217  const DifferingParamsContainer &DifferingParams) {
218  auto ChooseOtherName = [](const DifferingParamInfo &ParamInfo) {
219  return ParamInfo.OtherName;
220  };
221  auto ChooseSourceName = [](const DifferingParamInfo &ParamInfo) {
222  return ParamInfo.SourceName;
223  };
224 
225  auto ParamDiag =
226  Check->diag(Location,
227  "differing parameters are named here: (%0), in %1: (%2)",
228  DiagnosticIDs::Level::Note)
229  << joinParameterNames(DifferingParams, ChooseOtherName)
230  << OtherDeclarationDescription
231  << joinParameterNames(DifferingParams, ChooseSourceName);
232 
233  for (const DifferingParamInfo &ParamInfo : DifferingParams) {
234  if (ParamInfo.GenerateFixItHint) {
235  ParamDiag << FixItHint::CreateReplacement(
236  CharSourceRange::getTokenRange(ParamInfo.OtherNameRange),
237  ParamInfo.SourceName);
238  }
239  }
240 }
241 
242 void formatDiagnosticsForDeclarations(
243  InconsistentDeclarationParameterNameCheck *Check,
244  const FunctionDecl *ParameterSourceDeclaration,
245  const FunctionDecl *OriginalDeclaration,
246  const InconsistentDeclarationsContainer &InconsistentDeclarations) {
247  Check->diag(
248  OriginalDeclaration->getLocation(),
249  "function %q0 has %1 other declaration%s1 with different parameter names")
250  << OriginalDeclaration
251  << static_cast<int>(InconsistentDeclarations.size());
252  int Count = 1;
253  for (const InconsistentDeclarationInfo &InconsistentDeclaration :
254  InconsistentDeclarations) {
255  Check->diag(InconsistentDeclaration.DeclarationLocation,
256  "the %ordinal0 inconsistent declaration seen here",
257  DiagnosticIDs::Level::Note)
258  << Count;
259 
260  formatDifferingParamsDiagnostic(
261  Check, InconsistentDeclaration.DeclarationLocation,
262  "the other declaration", InconsistentDeclaration.DifferingParams);
263 
264  ++Count;
265  }
266 }
267 
268 void formatDiagnostics(
269  InconsistentDeclarationParameterNameCheck *Check,
270  const FunctionDecl *ParameterSourceDeclaration,
271  const FunctionDecl *OriginalDeclaration,
272  const InconsistentDeclarationsContainer &InconsistentDeclarations,
273  StringRef FunctionDescription, StringRef ParameterSourceDescription) {
274  for (const InconsistentDeclarationInfo &InconsistentDeclaration :
275  InconsistentDeclarations) {
276  Check->diag(InconsistentDeclaration.DeclarationLocation,
277  "%0 %q1 has a %2 with different parameter names")
278  << FunctionDescription << OriginalDeclaration
279  << ParameterSourceDescription;
280 
281  Check->diag(ParameterSourceDeclaration->getLocation(), "the %0 seen here",
282  DiagnosticIDs::Level::Note)
283  << ParameterSourceDescription;
284 
285  formatDifferingParamsDiagnostic(
286  Check, InconsistentDeclaration.DeclarationLocation,
287  ParameterSourceDescription, InconsistentDeclaration.DifferingParams);
288  }
289 }
290 
291 } // anonymous namespace
292 
293 void InconsistentDeclarationParameterNameCheck::storeOptions(
295  Options.store(Opts, "IgnoreMacros", IgnoreMacros);
296  Options.store(Opts, "Strict", Strict);
297 }
298 
299 void InconsistentDeclarationParameterNameCheck::registerMatchers(
300  MatchFinder *Finder) {
301  Finder->addMatcher(functionDecl(unless(isImplicit()), hasOtherDeclarations())
302  .bind("functionDecl"),
303  this);
304 }
305 
306 void InconsistentDeclarationParameterNameCheck::check(
307  const MatchFinder::MatchResult &Result) {
308  const auto *OriginalDeclaration =
309  Result.Nodes.getNodeAs<FunctionDecl>("functionDecl");
310 
311  if (VisitedDeclarations.count(OriginalDeclaration) > 0)
312  return; // Avoid multiple warnings.
313 
314  const FunctionDecl *ParameterSourceDeclaration =
315  getParameterSourceDeclaration(OriginalDeclaration);
316 
317  InconsistentDeclarationsContainer InconsistentDeclarations =
318  findInconsistentDeclarations(OriginalDeclaration,
319  ParameterSourceDeclaration,
320  *Result.SourceManager, Strict);
321  if (InconsistentDeclarations.empty()) {
322  // Avoid unnecessary further visits.
323  markRedeclarationsAsVisited(OriginalDeclaration);
324  return;
325  }
326 
327  SourceLocation StartLoc = OriginalDeclaration->getBeginLoc();
328  if (StartLoc.isMacroID() && IgnoreMacros) {
329  markRedeclarationsAsVisited(OriginalDeclaration);
330  return;
331  }
332 
333  if (OriginalDeclaration->getTemplatedKind() ==
334  FunctionDecl::TK_FunctionTemplateSpecialization) {
335  formatDiagnostics(this, ParameterSourceDeclaration, OriginalDeclaration,
336  InconsistentDeclarations,
337  "function template specialization",
338  "primary template declaration");
339  } else if (ParameterSourceDeclaration->isThisDeclarationADefinition()) {
340  formatDiagnostics(this, ParameterSourceDeclaration, OriginalDeclaration,
341  InconsistentDeclarations, "function", "definition");
342  } else {
343  formatDiagnosticsForDeclarations(this, ParameterSourceDeclaration,
344  OriginalDeclaration,
345  InconsistentDeclarations);
346  }
347 
348  markRedeclarationsAsVisited(OriginalDeclaration);
349 }
350 
351 void InconsistentDeclarationParameterNameCheck::markRedeclarationsAsVisited(
352  const FunctionDecl *OriginalDeclaration) {
353  for (const FunctionDecl *Redecl : OriginalDeclaration->redecls()) {
354  VisitedDeclarations.insert(Redecl);
355  }
356 }
357 
358 } // namespace readability
359 } // namespace tidy
360 } // namespace clang
AST_MATCHER(BinaryOperator, isAssignmentOperator)
Definition: Matchers.h:20
SourceLocation DeclarationLocation
DifferingParamsContainer DifferingParams
std::map< std::string, std::string > OptionMap
llvm::Optional< llvm::Expected< tooling::AtomicChanges > > Result
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//