NamedParameterCheck.cpp 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. //===--- NamedParameterCheck.cpp - clang-tidy -------------------*- C++ -*-===//
  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 "NamedParameterCheck.h"
  9. #include "clang/AST/ASTContext.h"
  10. #include "clang/ASTMatchers/ASTMatchFinder.h"
  11. #include "clang/ASTMatchers/ASTMatchers.h"
  12. using namespace clang::ast_matchers;
  13. namespace clang::tidy::readability {
  14. void NamedParameterCheck::registerMatchers(ast_matchers::MatchFinder *Finder) {
  15. Finder->addMatcher(functionDecl().bind("decl"), this);
  16. }
  17. void NamedParameterCheck::check(const MatchFinder::MatchResult &Result) {
  18. const SourceManager &SM = *Result.SourceManager;
  19. const auto *Function = Result.Nodes.getNodeAs<FunctionDecl>("decl");
  20. SmallVector<std::pair<const FunctionDecl *, unsigned>, 4> UnnamedParams;
  21. // Ignore declarations without a definition if we're not dealing with an
  22. // overriden method.
  23. const FunctionDecl *Definition = nullptr;
  24. if ((!Function->isDefined(Definition) || Function->isDefaulted() ||
  25. Function->isDeleted()) &&
  26. (!isa<CXXMethodDecl>(Function) ||
  27. cast<CXXMethodDecl>(Function)->size_overridden_methods() == 0))
  28. return;
  29. // TODO: Handle overloads.
  30. // TODO: We could check that all redeclarations use the same name for
  31. // arguments in the same position.
  32. for (unsigned I = 0, E = Function->getNumParams(); I != E; ++I) {
  33. const ParmVarDecl *Parm = Function->getParamDecl(I);
  34. if (Parm->isImplicit())
  35. continue;
  36. // Look for unnamed parameters.
  37. if (!Parm->getName().empty())
  38. continue;
  39. // Don't warn on the dummy argument on post-inc and post-dec operators.
  40. if ((Function->getOverloadedOperator() == OO_PlusPlus ||
  41. Function->getOverloadedOperator() == OO_MinusMinus) &&
  42. Parm->getType()->isSpecificBuiltinType(BuiltinType::Int))
  43. continue;
  44. // Sanity check the source locations.
  45. if (!Parm->getLocation().isValid() || Parm->getLocation().isMacroID() ||
  46. !SM.isWrittenInSameFile(Parm->getBeginLoc(), Parm->getLocation()))
  47. continue;
  48. // Skip gmock testing::Unused parameters.
  49. if (const auto *Typedef = Parm->getType()->getAs<clang::TypedefType>())
  50. if (Typedef->getDecl()->getQualifiedNameAsString() == "testing::Unused")
  51. continue;
  52. // Skip std::nullptr_t.
  53. if (Parm->getType().getCanonicalType()->isNullPtrType())
  54. continue;
  55. // Look for comments. We explicitly want to allow idioms like
  56. // void foo(int /*unused*/)
  57. const char *Begin = SM.getCharacterData(Parm->getBeginLoc());
  58. const char *End = SM.getCharacterData(Parm->getLocation());
  59. StringRef Data(Begin, End - Begin);
  60. if (Data.contains("/*"))
  61. continue;
  62. UnnamedParams.push_back(std::make_pair(Function, I));
  63. }
  64. // Emit only one warning per function but fixits for all unnamed parameters.
  65. if (!UnnamedParams.empty()) {
  66. const ParmVarDecl *FirstParm =
  67. UnnamedParams.front().first->getParamDecl(UnnamedParams.front().second);
  68. auto D = diag(FirstParm->getLocation(),
  69. "all parameters should be named in a function");
  70. for (auto P : UnnamedParams) {
  71. // Fallback to an unused marker.
  72. StringRef NewName = "unused";
  73. // If the method is overridden, try to copy the name from the base method
  74. // into the overrider.
  75. const auto *M = dyn_cast<CXXMethodDecl>(P.first);
  76. if (M && M->size_overridden_methods() > 0) {
  77. const ParmVarDecl *OtherParm =
  78. (*M->begin_overridden_methods())->getParamDecl(P.second);
  79. StringRef Name = OtherParm->getName();
  80. if (!Name.empty())
  81. NewName = Name;
  82. }
  83. // If the definition has a named parameter use that name.
  84. if (Definition) {
  85. const ParmVarDecl *DefParm = Definition->getParamDecl(P.second);
  86. StringRef Name = DefParm->getName();
  87. if (!Name.empty())
  88. NewName = Name;
  89. }
  90. // Now insert the comment. Note that getLocation() points to the place
  91. // where the name would be, this allows us to also get complex cases like
  92. // function pointers right.
  93. const ParmVarDecl *Parm = P.first->getParamDecl(P.second);
  94. D << FixItHint::CreateInsertion(Parm->getLocation(),
  95. " /*" + NewName.str() + "*/");
  96. }
  97. }
  98. }
  99. } // namespace clang::tidy::readability