clang-tools  5.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 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())
114  .getSourceRange();
115 
116  bool GenerateFixItHint = checkIfFixItHintIsApplicable(
117  ParameterSourceDeclaration, *SourceParamIt, OriginalDeclaration);
118 
119  DifferingParams.emplace_back(SourceParamName, OtherParamName,
120  OtherParamNameRange, GenerateFixItHint);
121  }
122 
123  ++SourceParamIt;
124  ++OtherParamIt;
125  }
126 
127  return DifferingParams;
128 }
129 
130 InconsistentDeclarationsContainer
131 findInconsitentDeclarations(const FunctionDecl *OriginalDeclaration,
132  const FunctionDecl *ParameterSourceDeclaration,
133  SourceManager &SM) {
134  InconsistentDeclarationsContainer InconsistentDeclarations;
135  SourceLocation ParameterSourceLocation =
136  ParameterSourceDeclaration->getLocation();
137 
138  for (const FunctionDecl *OtherDeclaration : OriginalDeclaration->redecls()) {
139  SourceLocation OtherLocation = OtherDeclaration->getLocation();
140  if (OtherLocation != ParameterSourceLocation) { // Skip self.
141  DifferingParamsContainer DifferingParams =
142  findDifferingParamsInDeclaration(ParameterSourceDeclaration,
143  OtherDeclaration,
144  OriginalDeclaration);
145  if (!DifferingParams.empty()) {
146  InconsistentDeclarations.emplace_back(OtherDeclaration->getLocation(),
147  std::move(DifferingParams));
148  }
149  }
150  }
151 
152  // Sort in order of appearance in translation unit to generate clear
153  // diagnostics.
154  std::sort(InconsistentDeclarations.begin(), InconsistentDeclarations.end(),
155  [&SM](const InconsistentDeclarationInfo &Info1,
156  const InconsistentDeclarationInfo &Info2) {
157  return SM.isBeforeInTranslationUnit(Info1.DeclarationLocation,
158  Info2.DeclarationLocation);
159  });
160  return InconsistentDeclarations;
161 }
162 
163 const FunctionDecl *
164 getParameterSourceDeclaration(const FunctionDecl *OriginalDeclaration) {
165  const FunctionTemplateDecl *PrimaryTemplate =
166  OriginalDeclaration->getPrimaryTemplate();
167  if (PrimaryTemplate != nullptr) {
168  // In case of template specializations, use primary template declaration as
169  // the source of parameter names.
170  return PrimaryTemplate->getTemplatedDecl();
171  }
172 
173  // In other cases, try to change to function definition, if available.
174 
175  if (OriginalDeclaration->isThisDeclarationADefinition())
176  return OriginalDeclaration;
177 
178  for (const FunctionDecl *OtherDeclaration : OriginalDeclaration->redecls()) {
179  if (OtherDeclaration->isThisDeclarationADefinition()) {
180  return OtherDeclaration;
181  }
182  }
183 
184  // No definition found, so return original declaration.
185  return OriginalDeclaration;
186 }
187 
188 std::string joinParameterNames(
189  const DifferingParamsContainer &DifferingParams,
190  llvm::function_ref<StringRef(const DifferingParamInfo &)> ChooseParamName) {
191  llvm::SmallVector<char, 40> Buffer;
192  llvm::raw_svector_ostream Str(Buffer);
193  bool First = true;
194  for (const DifferingParamInfo &ParamInfo : DifferingParams) {
195  if (First)
196  First = false;
197  else
198  Str << ", ";
199 
200  Str << "'" << ChooseParamName(ParamInfo).str() << "'";
201  }
202  return Str.str().str();
203 }
204 
205 void formatDifferingParamsDiagnostic(
206  InconsistentDeclarationParameterNameCheck *Check, SourceLocation Location,
207  StringRef OtherDeclarationDescription,
208  const DifferingParamsContainer &DifferingParams) {
209  auto ChooseOtherName = [](const DifferingParamInfo &ParamInfo) {
210  return ParamInfo.OtherName;
211  };
212  auto ChooseSourceName = [](const DifferingParamInfo &ParamInfo) {
213  return ParamInfo.SourceName;
214  };
215 
216  auto ParamDiag =
217  Check->diag(Location,
218  "differing parameters are named here: (%0), in %1: (%2)",
219  DiagnosticIDs::Level::Note)
220  << joinParameterNames(DifferingParams, ChooseOtherName)
221  << OtherDeclarationDescription
222  << joinParameterNames(DifferingParams, ChooseSourceName);
223 
224  for (const DifferingParamInfo &ParamInfo : DifferingParams) {
225  if (ParamInfo.GenerateFixItHint) {
226  ParamDiag << FixItHint::CreateReplacement(
227  CharSourceRange::getTokenRange(ParamInfo.OtherNameRange),
228  ParamInfo.SourceName);
229  }
230  }
231 }
232 
233 void formatDiagnosticsForDeclarations(
234  InconsistentDeclarationParameterNameCheck *Check,
235  const FunctionDecl *ParameterSourceDeclaration,
236  const FunctionDecl *OriginalDeclaration,
237  const InconsistentDeclarationsContainer &InconsistentDeclarations) {
238  Check->diag(
239  OriginalDeclaration->getLocation(),
240  "function %q0 has %1 other declaration%s1 with different parameter names")
241  << OriginalDeclaration
242  << static_cast<int>(InconsistentDeclarations.size());
243  int Count = 1;
244  for (const InconsistentDeclarationInfo &InconsistentDeclaration :
245  InconsistentDeclarations) {
246  Check->diag(InconsistentDeclaration.DeclarationLocation,
247  "the %ordinal0 inconsistent declaration seen here",
248  DiagnosticIDs::Level::Note)
249  << Count;
250 
251  formatDifferingParamsDiagnostic(
252  Check, InconsistentDeclaration.DeclarationLocation,
253  "the other declaration", InconsistentDeclaration.DifferingParams);
254 
255  ++Count;
256  }
257 }
258 
259 void formatDiagnostics(
260  InconsistentDeclarationParameterNameCheck *Check,
261  const FunctionDecl *ParameterSourceDeclaration,
262  const FunctionDecl *OriginalDeclaration,
263  const InconsistentDeclarationsContainer &InconsistentDeclarations,
264  StringRef FunctionDescription, StringRef ParameterSourceDescription) {
265  for (const InconsistentDeclarationInfo &InconsistentDeclaration :
266  InconsistentDeclarations) {
267  Check->diag(InconsistentDeclaration.DeclarationLocation,
268  "%0 %q1 has a %2 with different parameter names")
269  << FunctionDescription << OriginalDeclaration
270  << ParameterSourceDescription;
271 
272  Check->diag(ParameterSourceDeclaration->getLocation(), "the %0 seen here",
273  DiagnosticIDs::Level::Note)
274  << ParameterSourceDescription;
275 
276  formatDifferingParamsDiagnostic(
277  Check, InconsistentDeclaration.DeclarationLocation,
278  ParameterSourceDescription, InconsistentDeclaration.DifferingParams);
279  }
280 }
281 
282 } // anonymous namespace
283 
284 void InconsistentDeclarationParameterNameCheck::registerMatchers(
285  MatchFinder *Finder) {
286  Finder->addMatcher(functionDecl(unless(isImplicit()), hasOtherDeclarations())
287  .bind("functionDecl"),
288  this);
289 }
290 
291 void InconsistentDeclarationParameterNameCheck::check(
292  const MatchFinder::MatchResult &Result) {
293  const auto *OriginalDeclaration =
294  Result.Nodes.getNodeAs<FunctionDecl>("functionDecl");
295 
296  if (VisitedDeclarations.count(OriginalDeclaration) > 0)
297  return; // Avoid multiple warnings.
298 
299  const FunctionDecl *ParameterSourceDeclaration =
300  getParameterSourceDeclaration(OriginalDeclaration);
301 
302  InconsistentDeclarationsContainer InconsistentDeclarations =
303  findInconsitentDeclarations(OriginalDeclaration,
304  ParameterSourceDeclaration,
305  *Result.SourceManager);
306  if (InconsistentDeclarations.empty()) {
307  // Avoid unnecessary further visits.
308  markRedeclarationsAsVisited(OriginalDeclaration);
309  return;
310  }
311 
312  if (OriginalDeclaration->getTemplatedKind() ==
313  FunctionDecl::TK_FunctionTemplateSpecialization) {
314  formatDiagnostics(this, ParameterSourceDeclaration, OriginalDeclaration,
315  InconsistentDeclarations,
316  "function template specialization",
317  "primary template declaration");
318  } else if (ParameterSourceDeclaration->isThisDeclarationADefinition()) {
319  formatDiagnostics(this, ParameterSourceDeclaration, OriginalDeclaration,
320  InconsistentDeclarations, "function", "definition");
321  } else {
322  formatDiagnosticsForDeclarations(this, ParameterSourceDeclaration,
323  OriginalDeclaration,
324  InconsistentDeclarations);
325  }
326 
327  markRedeclarationsAsVisited(OriginalDeclaration);
328 }
329 
330 void InconsistentDeclarationParameterNameCheck::markRedeclarationsAsVisited(
331  const FunctionDecl *OriginalDeclaration) {
332  for (const FunctionDecl *Redecl : OriginalDeclaration->redecls()) {
333  VisitedDeclarations.insert(Redecl);
334  }
335 }
336 
337 } // namespace readability
338 } // namespace tidy
339 } // namespace clang
SetLongJmpCheck & Check
std::unique_ptr< ast_matchers::MatchFinder > Finder
Definition: ClangTidy.cpp:275
SourceLocation DeclarationLocation
DifferingParamsContainer DifferingParams
SourceManager & SM
AST_MATCHER(VarDecl, isAsm)