clang-tools 3.8.0

- Main Page
- Namespaces
- Classes
- Files

- File List
- File Members

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 namespace clang {
19 namespace tidy {
20 namespace readability {
21
22 using namespace ast_matchers;
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()).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 std::function<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
Check
SetLongJmpCheck & Check
Definition: SetLongJmpCheck.cpp:28
Finder
std::unique_ptr< ast_matchers::MatchFinder > Finder
Definition: ClangTidy.cpp:188
DeclarationLocation
SourceLocation DeclarationLocation
Definition: InconsistentDeclarationParameterNameCheck.cpp:57
OtherNameRange
SourceRange OtherNameRange
Definition: InconsistentDeclarationParameterNameCheck.cpp:45
InconsistentDeclarationParameterNameCheck.h
clang::tidy::readability::InconsistentDeclarationParameterNameCheck::check
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
Definition: InconsistentDeclarationParameterNameCheck.cpp:288
DifferingParams
DifferingParamsContainer DifferingParams
Definition: InconsistentDeclarationParameterNameCheck.cpp:58
clang::tidy::AST_MATCHER
AST_MATCHER(Stmt, isInsideOfRangeBeginEndStmt)
Definition: ProBoundsArrayToPointerDecayCheck.cpp:25
SM
SourceManager & SM
Definition: IncludeOrderCheck.cpp:44
OtherName
StringRef OtherName
Definition: InconsistentDeclarationParameterNameCheck.cpp:44
clang::tidy::readability::InconsistentDeclarationParameterNameCheck::registerMatchers
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register ASTMatchers with Finder.
Definition: InconsistentDeclarationParameterNameCheck.cpp:281
GenerateFixItHint
bool GenerateFixItHint
Definition: InconsistentDeclarationParameterNameCheck.cpp:46
SourceName
StringRef SourceName
Definition: InconsistentDeclarationParameterNameCheck.cpp:43
Result
const NamedDecl * Result
Definition: USRFinder.cpp:121
Location
Definition: Modularize.cpp:386