InconsistentDeclarationParameterNameCheck.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. //===--- InconsistentDeclarationParameterNameCheck.cpp - clang-tidy-------===//
  2. //
  3. // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
  4. // See https://llvm.org/LICENSE.txt for license information.
  5. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  6. //
  7. //===----------------------------------------------------------------------===//
  8. #include "InconsistentDeclarationParameterNameCheck.h"
  9. #include "clang/AST/ASTContext.h"
  10. #include "clang/ASTMatchers/ASTMatchFinder.h"
  11. #include "llvm/ADT/STLExtras.h"
  12. #include <functional>
  13. using namespace clang::ast_matchers;
  14. namespace clang::tidy::readability {
  15. namespace {
  16. AST_MATCHER(FunctionDecl, hasOtherDeclarations) {
  17. auto It = Node.redecls_begin();
  18. auto EndIt = Node.redecls_end();
  19. if (It == EndIt)
  20. return false;
  21. ++It;
  22. return It != EndIt;
  23. }
  24. struct DifferingParamInfo {
  25. DifferingParamInfo(StringRef SourceName, StringRef OtherName,
  26. SourceRange OtherNameRange, bool GenerateFixItHint)
  27. : SourceName(SourceName), OtherName(OtherName),
  28. OtherNameRange(OtherNameRange), GenerateFixItHint(GenerateFixItHint) {}
  29. StringRef SourceName;
  30. StringRef OtherName;
  31. SourceRange OtherNameRange;
  32. bool GenerateFixItHint;
  33. };
  34. using DifferingParamsContainer = llvm::SmallVector<DifferingParamInfo, 10>;
  35. struct InconsistentDeclarationInfo {
  36. InconsistentDeclarationInfo(SourceLocation DeclarationLocation,
  37. DifferingParamsContainer &&DifferingParams)
  38. : DeclarationLocation(DeclarationLocation),
  39. DifferingParams(std::move(DifferingParams)) {}
  40. SourceLocation DeclarationLocation;
  41. DifferingParamsContainer DifferingParams;
  42. };
  43. using InconsistentDeclarationsContainer =
  44. llvm::SmallVector<InconsistentDeclarationInfo, 2>;
  45. bool checkIfFixItHintIsApplicable(
  46. const FunctionDecl *ParameterSourceDeclaration,
  47. const ParmVarDecl *SourceParam, const FunctionDecl *OriginalDeclaration) {
  48. // Assumptions with regard to function declarations/definition:
  49. // * If both function declaration and definition are seen, assume that
  50. // definition is most up-to-date, and use it to generate replacements.
  51. // * If only function declarations are seen, there is no easy way to tell
  52. // which is up-to-date and which is not, so don't do anything.
  53. // TODO: This may be changed later, but for now it seems the reasonable
  54. // solution.
  55. if (!ParameterSourceDeclaration->isThisDeclarationADefinition())
  56. return false;
  57. // Assumption: if parameter is not referenced in function definition body, it
  58. // may indicate that it's outdated, so don't touch it.
  59. if (!SourceParam->isReferenced())
  60. return false;
  61. // In case there is the primary template definition and (possibly several)
  62. // template specializations (and each with possibly several redeclarations),
  63. // it is not at all clear what to change.
  64. if (OriginalDeclaration->getTemplatedKind() ==
  65. FunctionDecl::TK_FunctionTemplateSpecialization)
  66. return false;
  67. // Other cases seem OK to allow replacements.
  68. return true;
  69. }
  70. bool nameMatch(StringRef L, StringRef R, bool Strict) {
  71. if (Strict)
  72. return L.empty() || R.empty() || L == R;
  73. // We allow two names if one is a prefix/suffix of the other, ignoring case.
  74. // Important special case: this is true if either parameter has no name!
  75. return L.startswith_insensitive(R) || R.startswith_insensitive(L) ||
  76. L.endswith_insensitive(R) || R.endswith_insensitive(L);
  77. }
  78. DifferingParamsContainer
  79. findDifferingParamsInDeclaration(const FunctionDecl *ParameterSourceDeclaration,
  80. const FunctionDecl *OtherDeclaration,
  81. const FunctionDecl *OriginalDeclaration,
  82. bool Strict) {
  83. DifferingParamsContainer DifferingParams;
  84. const auto *SourceParamIt = ParameterSourceDeclaration->param_begin();
  85. const auto *OtherParamIt = OtherDeclaration->param_begin();
  86. while (SourceParamIt != ParameterSourceDeclaration->param_end() &&
  87. OtherParamIt != OtherDeclaration->param_end()) {
  88. auto SourceParamName = (*SourceParamIt)->getName();
  89. auto OtherParamName = (*OtherParamIt)->getName();
  90. // FIXME: Provide a way to extract commented out parameter name from comment
  91. // next to it.
  92. if (!nameMatch(SourceParamName, OtherParamName, Strict)) {
  93. SourceRange OtherParamNameRange =
  94. DeclarationNameInfo((*OtherParamIt)->getDeclName(),
  95. (*OtherParamIt)->getLocation())
  96. .getSourceRange();
  97. bool GenerateFixItHint = checkIfFixItHintIsApplicable(
  98. ParameterSourceDeclaration, *SourceParamIt, OriginalDeclaration);
  99. DifferingParams.emplace_back(SourceParamName, OtherParamName,
  100. OtherParamNameRange, GenerateFixItHint);
  101. }
  102. ++SourceParamIt;
  103. ++OtherParamIt;
  104. }
  105. return DifferingParams;
  106. }
  107. InconsistentDeclarationsContainer
  108. findInconsistentDeclarations(const FunctionDecl *OriginalDeclaration,
  109. const FunctionDecl *ParameterSourceDeclaration,
  110. SourceManager &SM, bool Strict) {
  111. InconsistentDeclarationsContainer InconsistentDeclarations;
  112. SourceLocation ParameterSourceLocation =
  113. ParameterSourceDeclaration->getLocation();
  114. for (const FunctionDecl *OtherDeclaration : OriginalDeclaration->redecls()) {
  115. SourceLocation OtherLocation = OtherDeclaration->getLocation();
  116. if (OtherLocation != ParameterSourceLocation) { // Skip self.
  117. DifferingParamsContainer DifferingParams =
  118. findDifferingParamsInDeclaration(ParameterSourceDeclaration,
  119. OtherDeclaration,
  120. OriginalDeclaration, Strict);
  121. if (!DifferingParams.empty()) {
  122. InconsistentDeclarations.emplace_back(OtherDeclaration->getLocation(),
  123. std::move(DifferingParams));
  124. }
  125. }
  126. }
  127. // Sort in order of appearance in translation unit to generate clear
  128. // diagnostics.
  129. llvm::sort(InconsistentDeclarations,
  130. [&SM](const InconsistentDeclarationInfo &Info1,
  131. const InconsistentDeclarationInfo &Info2) {
  132. return SM.isBeforeInTranslationUnit(Info1.DeclarationLocation,
  133. Info2.DeclarationLocation);
  134. });
  135. return InconsistentDeclarations;
  136. }
  137. const FunctionDecl *
  138. getParameterSourceDeclaration(const FunctionDecl *OriginalDeclaration) {
  139. const FunctionTemplateDecl *PrimaryTemplate =
  140. OriginalDeclaration->getPrimaryTemplate();
  141. if (PrimaryTemplate != nullptr) {
  142. // In case of template specializations, use primary template declaration as
  143. // the source of parameter names.
  144. return PrimaryTemplate->getTemplatedDecl();
  145. }
  146. // In other cases, try to change to function definition, if available.
  147. if (OriginalDeclaration->isThisDeclarationADefinition())
  148. return OriginalDeclaration;
  149. for (const FunctionDecl *OtherDeclaration : OriginalDeclaration->redecls()) {
  150. if (OtherDeclaration->isThisDeclarationADefinition()) {
  151. return OtherDeclaration;
  152. }
  153. }
  154. // No definition found, so return original declaration.
  155. return OriginalDeclaration;
  156. }
  157. std::string joinParameterNames(
  158. const DifferingParamsContainer &DifferingParams,
  159. llvm::function_ref<StringRef(const DifferingParamInfo &)> ChooseParamName) {
  160. llvm::SmallString<40> Str;
  161. bool First = true;
  162. for (const DifferingParamInfo &ParamInfo : DifferingParams) {
  163. if (First)
  164. First = false;
  165. else
  166. Str += ", ";
  167. Str.append({"'", ChooseParamName(ParamInfo), "'"});
  168. }
  169. return std::string(Str);
  170. }
  171. void formatDifferingParamsDiagnostic(
  172. InconsistentDeclarationParameterNameCheck *Check, SourceLocation Location,
  173. StringRef OtherDeclarationDescription,
  174. const DifferingParamsContainer &DifferingParams) {
  175. auto ChooseOtherName = [](const DifferingParamInfo &ParamInfo) {
  176. return ParamInfo.OtherName;
  177. };
  178. auto ChooseSourceName = [](const DifferingParamInfo &ParamInfo) {
  179. return ParamInfo.SourceName;
  180. };
  181. auto ParamDiag =
  182. Check->diag(Location,
  183. "differing parameters are named here: (%0), in %1: (%2)",
  184. DiagnosticIDs::Level::Note)
  185. << joinParameterNames(DifferingParams, ChooseOtherName)
  186. << OtherDeclarationDescription
  187. << joinParameterNames(DifferingParams, ChooseSourceName);
  188. for (const DifferingParamInfo &ParamInfo : DifferingParams) {
  189. if (ParamInfo.GenerateFixItHint) {
  190. ParamDiag << FixItHint::CreateReplacement(
  191. CharSourceRange::getTokenRange(ParamInfo.OtherNameRange),
  192. ParamInfo.SourceName);
  193. }
  194. }
  195. }
  196. void formatDiagnosticsForDeclarations(
  197. InconsistentDeclarationParameterNameCheck *Check,
  198. const FunctionDecl *ParameterSourceDeclaration,
  199. const FunctionDecl *OriginalDeclaration,
  200. const InconsistentDeclarationsContainer &InconsistentDeclarations) {
  201. Check->diag(
  202. OriginalDeclaration->getLocation(),
  203. "function %q0 has %1 other declaration%s1 with different parameter names")
  204. << OriginalDeclaration
  205. << static_cast<int>(InconsistentDeclarations.size());
  206. int Count = 1;
  207. for (const InconsistentDeclarationInfo &InconsistentDeclaration :
  208. InconsistentDeclarations) {
  209. Check->diag(InconsistentDeclaration.DeclarationLocation,
  210. "the %ordinal0 inconsistent declaration seen here",
  211. DiagnosticIDs::Level::Note)
  212. << Count;
  213. formatDifferingParamsDiagnostic(
  214. Check, InconsistentDeclaration.DeclarationLocation,
  215. "the other declaration", InconsistentDeclaration.DifferingParams);
  216. ++Count;
  217. }
  218. }
  219. void formatDiagnostics(
  220. InconsistentDeclarationParameterNameCheck *Check,
  221. const FunctionDecl *ParameterSourceDeclaration,
  222. const FunctionDecl *OriginalDeclaration,
  223. const InconsistentDeclarationsContainer &InconsistentDeclarations,
  224. StringRef FunctionDescription, StringRef ParameterSourceDescription) {
  225. for (const InconsistentDeclarationInfo &InconsistentDeclaration :
  226. InconsistentDeclarations) {
  227. Check->diag(InconsistentDeclaration.DeclarationLocation,
  228. "%0 %q1 has a %2 with different parameter names")
  229. << FunctionDescription << OriginalDeclaration
  230. << ParameterSourceDescription;
  231. Check->diag(ParameterSourceDeclaration->getLocation(), "the %0 seen here",
  232. DiagnosticIDs::Level::Note)
  233. << ParameterSourceDescription;
  234. formatDifferingParamsDiagnostic(
  235. Check, InconsistentDeclaration.DeclarationLocation,
  236. ParameterSourceDescription, InconsistentDeclaration.DifferingParams);
  237. }
  238. }
  239. } // anonymous namespace
  240. void InconsistentDeclarationParameterNameCheck::storeOptions(
  241. ClangTidyOptions::OptionMap &Opts) {
  242. Options.store(Opts, "IgnoreMacros", IgnoreMacros);
  243. Options.store(Opts, "Strict", Strict);
  244. }
  245. void InconsistentDeclarationParameterNameCheck::registerMatchers(
  246. MatchFinder *Finder) {
  247. Finder->addMatcher(functionDecl(hasOtherDeclarations()).bind("functionDecl"),
  248. this);
  249. }
  250. void InconsistentDeclarationParameterNameCheck::check(
  251. const MatchFinder::MatchResult &Result) {
  252. const auto *OriginalDeclaration =
  253. Result.Nodes.getNodeAs<FunctionDecl>("functionDecl");
  254. if (VisitedDeclarations.contains(OriginalDeclaration))
  255. return; // Avoid multiple warnings.
  256. const FunctionDecl *ParameterSourceDeclaration =
  257. getParameterSourceDeclaration(OriginalDeclaration);
  258. InconsistentDeclarationsContainer InconsistentDeclarations =
  259. findInconsistentDeclarations(OriginalDeclaration,
  260. ParameterSourceDeclaration,
  261. *Result.SourceManager, Strict);
  262. if (InconsistentDeclarations.empty()) {
  263. // Avoid unnecessary further visits.
  264. markRedeclarationsAsVisited(OriginalDeclaration);
  265. return;
  266. }
  267. SourceLocation StartLoc = OriginalDeclaration->getBeginLoc();
  268. if (StartLoc.isMacroID() && IgnoreMacros) {
  269. markRedeclarationsAsVisited(OriginalDeclaration);
  270. return;
  271. }
  272. if (OriginalDeclaration->getTemplatedKind() ==
  273. FunctionDecl::TK_FunctionTemplateSpecialization) {
  274. formatDiagnostics(this, ParameterSourceDeclaration, OriginalDeclaration,
  275. InconsistentDeclarations,
  276. "function template specialization",
  277. "primary template declaration");
  278. } else if (ParameterSourceDeclaration->isThisDeclarationADefinition()) {
  279. formatDiagnostics(this, ParameterSourceDeclaration, OriginalDeclaration,
  280. InconsistentDeclarations, "function", "definition");
  281. } else {
  282. formatDiagnosticsForDeclarations(this, ParameterSourceDeclaration,
  283. OriginalDeclaration,
  284. InconsistentDeclarations);
  285. }
  286. markRedeclarationsAsVisited(OriginalDeclaration);
  287. }
  288. void InconsistentDeclarationParameterNameCheck::markRedeclarationsAsVisited(
  289. const FunctionDecl *OriginalDeclaration) {
  290. for (const FunctionDecl *Redecl : OriginalDeclaration->redecls()) {
  291. VisitedDeclarations.insert(Redecl);
  292. }
  293. }
  294. } // namespace clang::tidy::readability