clang-tools 6.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
10 #include "InconsistentDeclarationParameterNameCheck.h"
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;
46 bool GenerateFixItHint;
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
clang::ast_matchers
Definition: ClangTidyDiagnosticConsumer.h:27
DeclarationLocation
SourceLocation DeclarationLocation
Definition: InconsistentDeclarationParameterNameCheck.cpp:57
OtherNameRange
SourceRange OtherNameRange
Definition: InconsistentDeclarationParameterNameCheck.cpp:45
InconsistentDeclarationParameterNameCheck.h
DifferingParams
DifferingParamsContainer DifferingParams
Definition: InconsistentDeclarationParameterNameCheck.cpp:58
OtherName
StringRef OtherName
Definition: InconsistentDeclarationParameterNameCheck.cpp:44
clang
Definition: AndroidTidyModule.cpp:28
GenerateFixItHint
bool GenerateFixItHint
Definition: InconsistentDeclarationParameterNameCheck.cpp:46
SourceName
StringRef SourceName
Definition: InconsistentDeclarationParameterNameCheck.cpp:43
clang::ast_matchers::AST_MATCHER
AST_MATCHER(VarDecl, isAsm)
Definition: NoAssemblerCheck.cpp:18
Location
Definition: Modularize.cpp:384