10#include "clang/ASTMatchers/ASTMatchFinder.h"
11#include "llvm/ADT/STLExtras.h"
20 auto It = Node.redecls_begin();
21 auto EndIt = Node.redecls_end();
30struct DifferingParamInfo {
31 DifferingParamInfo(StringRef SourceName, StringRef OtherName,
32 SourceRange OtherNameRange,
bool GenerateFixItHint)
33 : SourceName(SourceName), OtherName(OtherName),
34 OtherNameRange(OtherNameRange), GenerateFixItHint(GenerateFixItHint) {}
38 SourceRange OtherNameRange;
39 bool GenerateFixItHint;
42using DifferingParamsContainer = llvm::SmallVector<DifferingParamInfo, 10>;
44struct InconsistentDeclarationInfo {
45 InconsistentDeclarationInfo(SourceLocation DeclarationLocation,
46 DifferingParamsContainer &&DifferingParams)
47 : DeclarationLocation(DeclarationLocation),
48 DifferingParams(std::move(DifferingParams)) {}
50 SourceLocation DeclarationLocation;
51 DifferingParamsContainer DifferingParams;
54using InconsistentDeclarationsContainer =
55 llvm::SmallVector<InconsistentDeclarationInfo, 2>;
57bool checkIfFixItHintIsApplicable(
58 const FunctionDecl *ParameterSourceDeclaration,
59 const ParmVarDecl *SourceParam,
const FunctionDecl *OriginalDeclaration) {
67 if (!ParameterSourceDeclaration->isThisDeclarationADefinition())
72 if (!SourceParam->isReferenced())
78 if (OriginalDeclaration->getTemplatedKind() ==
79 FunctionDecl::TK_FunctionTemplateSpecialization)
86bool nameMatch(StringRef L, StringRef R,
bool Strict) {
88 return L.empty() || R.empty() || L == R;
91 return L.starts_with_insensitive(R) || R.starts_with_insensitive(L) ||
92 L.ends_with_insensitive(R) || R.ends_with_insensitive(L);
95DifferingParamsContainer
96findDifferingParamsInDeclaration(
const FunctionDecl *ParameterSourceDeclaration,
97 const FunctionDecl *OtherDeclaration,
98 const FunctionDecl *OriginalDeclaration,
100 DifferingParamsContainer DifferingParams;
102 const auto *SourceParamIt = ParameterSourceDeclaration->param_begin();
103 const auto *OtherParamIt = OtherDeclaration->param_begin();
105 while (SourceParamIt != ParameterSourceDeclaration->param_end() &&
106 OtherParamIt != OtherDeclaration->param_end()) {
107 auto SourceParamName = (*SourceParamIt)->getName();
108 auto OtherParamName = (*OtherParamIt)->getName();
112 if (!nameMatch(SourceParamName, OtherParamName, Strict)) {
113 SourceRange OtherParamNameRange =
114 DeclarationNameInfo((*OtherParamIt)->getDeclName(),
115 (*OtherParamIt)->getLocation())
118 bool GenerateFixItHint = checkIfFixItHintIsApplicable(
119 ParameterSourceDeclaration, *SourceParamIt, OriginalDeclaration);
121 DifferingParams.emplace_back(SourceParamName, OtherParamName,
122 OtherParamNameRange, GenerateFixItHint);
129 return DifferingParams;
132InconsistentDeclarationsContainer
133findInconsistentDeclarations(
const FunctionDecl *OriginalDeclaration,
134 const FunctionDecl *ParameterSourceDeclaration,
135 SourceManager &SM,
bool Strict) {
136 InconsistentDeclarationsContainer InconsistentDeclarations;
137 SourceLocation ParameterSourceLocation =
138 ParameterSourceDeclaration->getLocation();
140 for (
const FunctionDecl *OtherDeclaration : OriginalDeclaration->redecls()) {
141 SourceLocation OtherLocation = OtherDeclaration->getLocation();
142 if (OtherLocation != ParameterSourceLocation) {
143 DifferingParamsContainer DifferingParams =
144 findDifferingParamsInDeclaration(ParameterSourceDeclaration,
146 OriginalDeclaration, Strict);
147 if (!DifferingParams.empty()) {
148 InconsistentDeclarations.emplace_back(OtherDeclaration->getLocation(),
149 std::move(DifferingParams));
156 llvm::sort(InconsistentDeclarations,
157 [&SM](
const InconsistentDeclarationInfo &Info1,
158 const InconsistentDeclarationInfo &Info2) {
159 return SM.isBeforeInTranslationUnit(Info1.DeclarationLocation,
160 Info2.DeclarationLocation);
162 return InconsistentDeclarations;
166getParameterSourceDeclaration(
const FunctionDecl *OriginalDeclaration) {
167 const FunctionTemplateDecl *PrimaryTemplate =
168 OriginalDeclaration->getPrimaryTemplate();
169 if (PrimaryTemplate !=
nullptr) {
172 return PrimaryTemplate->getTemplatedDecl();
177 if (OriginalDeclaration->isThisDeclarationADefinition())
178 return OriginalDeclaration;
180 for (
const FunctionDecl *OtherDeclaration : OriginalDeclaration->redecls()) {
181 if (OtherDeclaration->isThisDeclarationADefinition()) {
182 return OtherDeclaration;
187 return OriginalDeclaration;
190std::string joinParameterNames(
191 const DifferingParamsContainer &DifferingParams,
192 llvm::function_ref<StringRef(
const DifferingParamInfo &)> ChooseParamName) {
193 llvm::SmallString<40> Str;
195 for (
const DifferingParamInfo &ParamInfo : DifferingParams) {
200 Str.append({
"'", ChooseParamName(ParamInfo),
"'"});
202 return std::string(Str);
205void formatDifferingParamsDiagnostic(
207 StringRef OtherDeclarationDescription,
208 const DifferingParamsContainer &DifferingParams) {
209 auto ChooseOtherName = [](
const DifferingParamInfo &ParamInfo) {
210 return ParamInfo.OtherName;
212 auto ChooseSourceName = [](
const DifferingParamInfo &ParamInfo) {
213 return ParamInfo.SourceName;
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);
224 for (
const DifferingParamInfo &ParamInfo : DifferingParams) {
225 if (ParamInfo.GenerateFixItHint) {
226 ParamDiag << FixItHint::CreateReplacement(
227 CharSourceRange::getTokenRange(ParamInfo.OtherNameRange),
228 ParamInfo.SourceName);
233void formatDiagnosticsForDeclarations(
235 const FunctionDecl *ParameterSourceDeclaration,
236 const FunctionDecl *OriginalDeclaration,
237 const InconsistentDeclarationsContainer &InconsistentDeclarations) {
239 OriginalDeclaration->getLocation(),
240 "function %q0 has %1 other declaration%s1 with different parameter names")
241 << OriginalDeclaration
242 <<
static_cast<int>(InconsistentDeclarations.size());
244 for (
const InconsistentDeclarationInfo &InconsistentDeclaration :
245 InconsistentDeclarations) {
246 Check->diag(InconsistentDeclaration.DeclarationLocation,
247 "the %ordinal0 inconsistent declaration seen here",
248 DiagnosticIDs::Level::Note)
251 formatDifferingParamsDiagnostic(
252 Check, InconsistentDeclaration.DeclarationLocation,
253 "the other declaration", InconsistentDeclaration.DifferingParams);
259void formatDiagnostics(
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;
272 Check->diag(ParameterSourceDeclaration->getLocation(),
"the %0 seen here",
273 DiagnosticIDs::Level::Note)
274 << ParameterSourceDescription;
276 formatDifferingParamsDiagnostic(
277 Check, InconsistentDeclaration.DeclarationLocation,
278 ParameterSourceDescription, InconsistentDeclaration.DifferingParams);
286 Options.store(Opts,
"IgnoreMacros", IgnoreMacros);
287 Options.store(Opts,
"Strict", Strict);
291 MatchFinder *Finder) {
292 Finder->addMatcher(functionDecl(hasOtherDeclarations()).bind(
"functionDecl"),
297 const MatchFinder::MatchResult &Result) {
298 const auto *OriginalDeclaration =
299 Result.Nodes.getNodeAs<FunctionDecl>(
"functionDecl");
301 if (VisitedDeclarations.contains(OriginalDeclaration))
304 const FunctionDecl *ParameterSourceDeclaration =
305 getParameterSourceDeclaration(OriginalDeclaration);
307 InconsistentDeclarationsContainer InconsistentDeclarations =
308 findInconsistentDeclarations(OriginalDeclaration,
309 ParameterSourceDeclaration,
310 *Result.SourceManager, Strict);
311 if (InconsistentDeclarations.empty()) {
313 markRedeclarationsAsVisited(OriginalDeclaration);
317 SourceLocation StartLoc = OriginalDeclaration->getBeginLoc();
318 if (StartLoc.isMacroID() && IgnoreMacros) {
319 markRedeclarationsAsVisited(OriginalDeclaration);
323 if (OriginalDeclaration->getTemplatedKind() ==
324 FunctionDecl::TK_FunctionTemplateSpecialization) {
325 formatDiagnostics(
this, ParameterSourceDeclaration, OriginalDeclaration,
326 InconsistentDeclarations,
327 "function template specialization",
328 "primary template declaration");
329 }
else if (ParameterSourceDeclaration->isThisDeclarationADefinition()) {
330 formatDiagnostics(
this, ParameterSourceDeclaration, OriginalDeclaration,
331 InconsistentDeclarations,
"function",
"definition");
333 formatDiagnosticsForDeclarations(
this, ParameterSourceDeclaration,
335 InconsistentDeclarations);
338 markRedeclarationsAsVisited(OriginalDeclaration);
341void InconsistentDeclarationParameterNameCheck::markRedeclarationsAsVisited(
342 const FunctionDecl *OriginalDeclaration) {
343 VisitedDeclarations.insert_range(OriginalDeclaration->redecls());
Checks for declarations of functions which differ in parameter names.
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
void registerMatchers(ast_matchers::MatchFinder *Finder) override
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
AST_MATCHER(BinaryOperator, isRelationalOperator)
llvm::StringMap< ClangTidyValue > OptionMap