10#include "clang/AST/ASTContext.h"
11#include "clang/ASTMatchers/ASTMatchFinder.h"
12#include "llvm/ADT/STLExtras.h"
23 auto It =
Node.redecls_begin();
24 auto EndIt =
Node.redecls_end();
33struct DifferingParamInfo {
34 DifferingParamInfo(StringRef SourceName, StringRef OtherName,
35 SourceRange OtherNameRange,
bool GenerateFixItHint)
45using DifferingParamsContainer = llvm::SmallVector<DifferingParamInfo, 10>;
47struct InconsistentDeclarationInfo {
57using InconsistentDeclarationsContainer =
58 llvm::SmallVector<InconsistentDeclarationInfo, 2>;
60bool checkIfFixItHintIsApplicable(
61 const FunctionDecl *ParameterSourceDeclaration,
62 const ParmVarDecl *SourceParam,
const FunctionDecl *OriginalDeclaration) {
70 if (!ParameterSourceDeclaration->isThisDeclarationADefinition())
75 if (!SourceParam->isReferenced())
81 if (OriginalDeclaration->getTemplatedKind() ==
82 FunctionDecl::TK_FunctionTemplateSpecialization)
89bool nameMatch(StringRef L, StringRef R,
bool Strict) {
91 return L.empty() || R.empty() || L == R;
94 return L.starts_with_insensitive(R) || R.starts_with_insensitive(L) ||
95 L.ends_with_insensitive(R) || R.ends_with_insensitive(L);
98DifferingParamsContainer
99findDifferingParamsInDeclaration(
const FunctionDecl *ParameterSourceDeclaration,
100 const FunctionDecl *OtherDeclaration,
101 const FunctionDecl *OriginalDeclaration,
105 const auto *SourceParamIt = ParameterSourceDeclaration->param_begin();
106 const auto *OtherParamIt = OtherDeclaration->param_begin();
108 while (SourceParamIt != ParameterSourceDeclaration->param_end() &&
109 OtherParamIt != OtherDeclaration->param_end()) {
110 auto SourceParamName = (*SourceParamIt)->getName();
111 auto OtherParamName = (*OtherParamIt)->getName();
115 if (!nameMatch(SourceParamName, OtherParamName, Strict)) {
116 SourceRange OtherParamNameRange =
117 DeclarationNameInfo((*OtherParamIt)->getDeclName(),
118 (*OtherParamIt)->getLocation())
122 ParameterSourceDeclaration, *SourceParamIt, OriginalDeclaration);
135InconsistentDeclarationsContainer
136findInconsistentDeclarations(
const FunctionDecl *OriginalDeclaration,
137 const FunctionDecl *ParameterSourceDeclaration,
138 SourceManager &SM,
bool Strict) {
139 InconsistentDeclarationsContainer InconsistentDeclarations;
140 SourceLocation ParameterSourceLocation =
141 ParameterSourceDeclaration->getLocation();
143 for (
const FunctionDecl *OtherDeclaration : OriginalDeclaration->redecls()) {
144 SourceLocation OtherLocation = OtherDeclaration->getLocation();
145 if (OtherLocation != ParameterSourceLocation) {
147 findDifferingParamsInDeclaration(ParameterSourceDeclaration,
149 OriginalDeclaration, Strict);
151 InconsistentDeclarations.emplace_back(OtherDeclaration->getLocation(),
159 llvm::sort(InconsistentDeclarations,
160 [&SM](
const InconsistentDeclarationInfo &Info1,
161 const InconsistentDeclarationInfo &Info2) {
162 return SM.isBeforeInTranslationUnit(Info1.DeclarationLocation,
163 Info2.DeclarationLocation);
165 return InconsistentDeclarations;
169getParameterSourceDeclaration(
const FunctionDecl *OriginalDeclaration) {
170 const FunctionTemplateDecl *PrimaryTemplate =
171 OriginalDeclaration->getPrimaryTemplate();
172 if (PrimaryTemplate !=
nullptr) {
175 return PrimaryTemplate->getTemplatedDecl();
180 if (OriginalDeclaration->isThisDeclarationADefinition())
181 return OriginalDeclaration;
183 for (
const FunctionDecl *OtherDeclaration : OriginalDeclaration->redecls()) {
184 if (OtherDeclaration->isThisDeclarationADefinition()) {
185 return OtherDeclaration;
190 return OriginalDeclaration;
193std::string joinParameterNames(
195 llvm::function_ref<StringRef(
const DifferingParamInfo &)> ChooseParamName) {
196 llvm::SmallString<40> Str;
203 Str.append({
"'", ChooseParamName(ParamInfo),
"'"});
205 return std::string(Str);
208void formatDifferingParamsDiagnostic(
209 InconsistentDeclarationParameterNameCheck *Check, SourceLocation
Location,
210 StringRef OtherDeclarationDescription,
212 auto ChooseOtherName = [](
const DifferingParamInfo &ParamInfo) {
213 return ParamInfo.OtherName;
215 auto ChooseSourceName = [](
const DifferingParamInfo &ParamInfo) {
216 return ParamInfo.SourceName;
221 "differing parameters are named here: (%0), in %1: (%2)",
222 DiagnosticIDs::Level::Note)
224 << OtherDeclarationDescription
228 if (ParamInfo.GenerateFixItHint) {
229 ParamDiag << FixItHint::CreateReplacement(
230 CharSourceRange::getTokenRange(ParamInfo.OtherNameRange),
231 ParamInfo.SourceName);
236void formatDiagnosticsForDeclarations(
237 InconsistentDeclarationParameterNameCheck *Check,
238 const FunctionDecl *ParameterSourceDeclaration,
239 const FunctionDecl *OriginalDeclaration,
240 const InconsistentDeclarationsContainer &InconsistentDeclarations) {
242 OriginalDeclaration->getLocation(),
243 "function %q0 has %1 other declaration%s1 with different parameter names")
244 << OriginalDeclaration
245 <<
static_cast<int>(InconsistentDeclarations.size());
247 for (
const InconsistentDeclarationInfo &InconsistentDeclaration :
248 InconsistentDeclarations) {
249 Check->diag(InconsistentDeclaration.DeclarationLocation,
250 "the %ordinal0 inconsistent declaration seen here",
251 DiagnosticIDs::Level::Note)
254 formatDifferingParamsDiagnostic(
255 Check, InconsistentDeclaration.DeclarationLocation,
256 "the other declaration", InconsistentDeclaration.DifferingParams);
262void formatDiagnostics(
263 InconsistentDeclarationParameterNameCheck *Check,
264 const FunctionDecl *ParameterSourceDeclaration,
265 const FunctionDecl *OriginalDeclaration,
266 const InconsistentDeclarationsContainer &InconsistentDeclarations,
267 StringRef FunctionDescription, StringRef ParameterSourceDescription) {
268 for (
const InconsistentDeclarationInfo &InconsistentDeclaration :
269 InconsistentDeclarations) {
270 Check->diag(InconsistentDeclaration.DeclarationLocation,
271 "%0 %q1 has a %2 with different parameter names")
272 << FunctionDescription << OriginalDeclaration
273 << ParameterSourceDescription;
275 Check->diag(ParameterSourceDeclaration->getLocation(),
"the %0 seen here",
276 DiagnosticIDs::Level::Note)
277 << ParameterSourceDescription;
279 formatDifferingParamsDiagnostic(
280 Check, InconsistentDeclaration.DeclarationLocation,
281 ParameterSourceDescription, InconsistentDeclaration.DifferingParams);
294 MatchFinder *Finder) {
295 Finder->addMatcher(functionDecl(hasOtherDeclarations()).bind(
"functionDecl"),
300 const MatchFinder::MatchResult &Result) {
301 const auto *OriginalDeclaration =
302 Result.Nodes.getNodeAs<FunctionDecl>(
"functionDecl");
304 if (VisitedDeclarations.contains(OriginalDeclaration))
307 const FunctionDecl *ParameterSourceDeclaration =
308 getParameterSourceDeclaration(OriginalDeclaration);
310 InconsistentDeclarationsContainer InconsistentDeclarations =
311 findInconsistentDeclarations(OriginalDeclaration,
312 ParameterSourceDeclaration,
313 *Result.SourceManager, Strict);
314 if (InconsistentDeclarations.empty()) {
316 markRedeclarationsAsVisited(OriginalDeclaration);
320 SourceLocation StartLoc = OriginalDeclaration->getBeginLoc();
321 if (StartLoc.isMacroID() && IgnoreMacros) {
322 markRedeclarationsAsVisited(OriginalDeclaration);
326 if (OriginalDeclaration->getTemplatedKind() ==
327 FunctionDecl::TK_FunctionTemplateSpecialization) {
328 formatDiagnostics(
this, ParameterSourceDeclaration, OriginalDeclaration,
329 InconsistentDeclarations,
330 "function template specialization",
331 "primary template declaration");
332 }
else if (ParameterSourceDeclaration->isThisDeclarationADefinition()) {
333 formatDiagnostics(
this, ParameterSourceDeclaration, OriginalDeclaration,
334 InconsistentDeclarations,
"function",
"definition");
336 formatDiagnosticsForDeclarations(
this, ParameterSourceDeclaration,
338 InconsistentDeclarations);
341 markRedeclarationsAsVisited(OriginalDeclaration);
344void InconsistentDeclarationParameterNameCheck::markRedeclarationsAsVisited(
345 const FunctionDecl *OriginalDeclaration) {
346 for (
const FunctionDecl *Redecl : OriginalDeclaration->redecls()) {
347 VisitedDeclarations.insert(Redecl);
SourceRange OtherNameRange
DifferingParamsContainer DifferingParams
SourceLocation DeclarationLocation
::clang::DynTypedNode Node
void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName, StringRef Value) const
Stores an option with the check-local name LocalName with string value Value to Options.
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
AST_MATCHER(CXXMethodDecl, isStatic)
llvm::StringMap< ClangTidyValue > OptionMap