NSInvocationArgumentLifetimeCheck.cpp 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. //===--- NSInvocationArgumentLifetimeCheck.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 "NSInvocationArgumentLifetimeCheck.h"
  9. #include "clang/AST/ASTContext.h"
  10. #include "clang/AST/ComputeDependence.h"
  11. #include "clang/AST/Decl.h"
  12. #include "clang/AST/Expr.h"
  13. #include "clang/AST/ExprObjC.h"
  14. #include "clang/AST/Type.h"
  15. #include "clang/AST/TypeLoc.h"
  16. #include "clang/ASTMatchers/ASTMatchFinder.h"
  17. #include "clang/ASTMatchers/ASTMatchers.h"
  18. #include "clang/ASTMatchers/ASTMatchersMacros.h"
  19. #include "clang/Basic/Diagnostic.h"
  20. #include "clang/Basic/LLVM.h"
  21. #include "clang/Basic/LangOptions.h"
  22. #include "clang/Basic/SourceLocation.h"
  23. #include "clang/Basic/SourceManager.h"
  24. #include "clang/Lex/Lexer.h"
  25. #include "llvm/ADT/StringRef.h"
  26. #include <optional>
  27. using namespace clang::ast_matchers;
  28. namespace clang::tidy::objc {
  29. namespace {
  30. static constexpr StringRef WeakText = "__weak";
  31. static constexpr StringRef StrongText = "__strong";
  32. static constexpr StringRef UnsafeUnretainedText = "__unsafe_unretained";
  33. /// Matches ObjCIvarRefExpr, DeclRefExpr, or MemberExpr that reference
  34. /// Objective-C object (or block) variables or fields whose object lifetimes
  35. /// are not __unsafe_unretained.
  36. AST_POLYMORPHIC_MATCHER(isObjCManagedLifetime,
  37. AST_POLYMORPHIC_SUPPORTED_TYPES(ObjCIvarRefExpr,
  38. DeclRefExpr,
  39. MemberExpr)) {
  40. QualType QT = Node.getType();
  41. return QT->isScalarType() &&
  42. (QT->getScalarTypeKind() == Type::STK_ObjCObjectPointer ||
  43. QT->getScalarTypeKind() == Type::STK_BlockPointer) &&
  44. QT.getQualifiers().getObjCLifetime() > Qualifiers::OCL_ExplicitNone;
  45. }
  46. static std::optional<FixItHint>
  47. fixItHintReplacementForOwnershipString(StringRef Text, CharSourceRange Range,
  48. StringRef Ownership) {
  49. size_t Index = Text.find(Ownership);
  50. if (Index == StringRef::npos)
  51. return std::nullopt;
  52. SourceLocation Begin = Range.getBegin().getLocWithOffset(Index);
  53. SourceLocation End = Begin.getLocWithOffset(Ownership.size());
  54. return FixItHint::CreateReplacement(SourceRange(Begin, End),
  55. UnsafeUnretainedText);
  56. }
  57. static std::optional<FixItHint>
  58. fixItHintForVarDecl(const VarDecl *VD, const SourceManager &SM,
  59. const LangOptions &LangOpts) {
  60. assert(VD && "VarDecl parameter must not be null");
  61. // Don't provide fix-its for any parameter variables at this time.
  62. if (isa<ParmVarDecl>(VD))
  63. return std::nullopt;
  64. // Currently there is no way to directly get the source range for the
  65. // __weak/__strong ObjC lifetime qualifiers, so it's necessary to string
  66. // search in the source code.
  67. CharSourceRange Range = Lexer::makeFileCharRange(
  68. CharSourceRange::getTokenRange(VD->getSourceRange()), SM, LangOpts);
  69. if (Range.isInvalid()) {
  70. // An invalid range likely means inside a macro, in which case don't supply
  71. // a fix-it.
  72. return std::nullopt;
  73. }
  74. StringRef VarDeclText = Lexer::getSourceText(Range, SM, LangOpts);
  75. if (std::optional<FixItHint> Hint =
  76. fixItHintReplacementForOwnershipString(VarDeclText, Range, WeakText))
  77. return Hint;
  78. if (std::optional<FixItHint> Hint = fixItHintReplacementForOwnershipString(
  79. VarDeclText, Range, StrongText))
  80. return Hint;
  81. return FixItHint::CreateInsertion(Range.getBegin(), "__unsafe_unretained ");
  82. }
  83. } // namespace
  84. void NSInvocationArgumentLifetimeCheck::registerMatchers(MatchFinder *Finder) {
  85. Finder->addMatcher(
  86. traverse(
  87. TK_AsIs,
  88. objcMessageExpr(
  89. hasReceiverType(asString("NSInvocation *")),
  90. anyOf(hasSelector("getArgument:atIndex:"),
  91. hasSelector("getReturnValue:")),
  92. hasArgument(
  93. 0,
  94. anyOf(hasDescendant(memberExpr(isObjCManagedLifetime())),
  95. hasDescendant(objcIvarRefExpr(isObjCManagedLifetime())),
  96. hasDescendant(
  97. // Reference to variables, but when dereferencing
  98. // to ivars/fields a more-descendent variable
  99. // reference (e.g. self) may match with strong
  100. // object lifetime, leading to an incorrect match.
  101. // Exclude these conditions.
  102. declRefExpr(to(varDecl().bind("var")),
  103. unless(hasParent(implicitCastExpr())),
  104. isObjCManagedLifetime())))))
  105. .bind("call")),
  106. this);
  107. }
  108. void NSInvocationArgumentLifetimeCheck::check(
  109. const MatchFinder::MatchResult &Result) {
  110. const auto *MatchedExpr = Result.Nodes.getNodeAs<ObjCMessageExpr>("call");
  111. auto Diag = diag(MatchedExpr->getArg(0)->getBeginLoc(),
  112. "NSInvocation %objcinstance0 should only pass pointers to "
  113. "objects with ownership __unsafe_unretained")
  114. << MatchedExpr->getSelector();
  115. // Only provide fix-it hints for references to local variables; fixes for
  116. // instance variable references don't have as clear an automated fix.
  117. const auto *VD = Result.Nodes.getNodeAs<VarDecl>("var");
  118. if (!VD)
  119. return;
  120. if (auto Hint = fixItHintForVarDecl(VD, *Result.SourceManager,
  121. Result.Context->getLangOpts()))
  122. Diag << *Hint;
  123. }
  124. } // namespace clang::tidy::objc