llvm-project

Форк
0
/
NSInvocationArgumentLifetimeCheck.cpp 
143 строки · 5.7 Кб
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

9
#include "NSInvocationArgumentLifetimeCheck.h"
10
#include "clang/AST/ASTContext.h"
11
#include "clang/AST/ComputeDependence.h"
12
#include "clang/AST/Decl.h"
13
#include "clang/AST/Expr.h"
14
#include "clang/AST/ExprObjC.h"
15
#include "clang/AST/Type.h"
16
#include "clang/AST/TypeLoc.h"
17
#include "clang/ASTMatchers/ASTMatchFinder.h"
18
#include "clang/ASTMatchers/ASTMatchers.h"
19
#include "clang/ASTMatchers/ASTMatchersMacros.h"
20
#include "clang/Basic/Diagnostic.h"
21
#include "clang/Basic/LLVM.h"
22
#include "clang/Basic/LangOptions.h"
23
#include "clang/Basic/SourceLocation.h"
24
#include "clang/Basic/SourceManager.h"
25
#include "clang/Lex/Lexer.h"
26
#include "llvm/ADT/StringRef.h"
27
#include <optional>
28

29
using namespace clang::ast_matchers;
30

31
namespace clang::tidy::objc {
32
namespace {
33

34
static constexpr StringRef WeakText = "__weak";
35
static constexpr StringRef StrongText = "__strong";
36
static constexpr StringRef UnsafeUnretainedText = "__unsafe_unretained";
37

38
/// Matches ObjCIvarRefExpr, DeclRefExpr, or MemberExpr that reference
39
/// Objective-C object (or block) variables or fields whose object lifetimes
40
/// are not __unsafe_unretained.
41
AST_POLYMORPHIC_MATCHER(isObjCManagedLifetime,
42
                        AST_POLYMORPHIC_SUPPORTED_TYPES(ObjCIvarRefExpr,
43
                                                        DeclRefExpr,
44
                                                        MemberExpr)) {
45
  QualType QT = Node.getType();
46
  return QT->isScalarType() &&
47
         (QT->getScalarTypeKind() == Type::STK_ObjCObjectPointer ||
48
          QT->getScalarTypeKind() == Type::STK_BlockPointer) &&
49
         QT.getQualifiers().getObjCLifetime() > Qualifiers::OCL_ExplicitNone;
50
}
51

52
static std::optional<FixItHint>
53
fixItHintReplacementForOwnershipString(StringRef Text, CharSourceRange Range,
54
                                       StringRef Ownership) {
55
  size_t Index = Text.find(Ownership);
56
  if (Index == StringRef::npos)
57
    return std::nullopt;
58

59
  SourceLocation Begin = Range.getBegin().getLocWithOffset(Index);
60
  SourceLocation End = Begin.getLocWithOffset(Ownership.size());
61
  return FixItHint::CreateReplacement(SourceRange(Begin, End),
62
                                      UnsafeUnretainedText);
63
}
64

65
static std::optional<FixItHint>
66
fixItHintForVarDecl(const VarDecl *VD, const SourceManager &SM,
67
                    const LangOptions &LangOpts) {
68
  assert(VD && "VarDecl parameter must not be null");
69
  // Don't provide fix-its for any parameter variables at this time.
70
  if (isa<ParmVarDecl>(VD))
71
    return std::nullopt;
72

73
  // Currently there is no way to directly get the source range for the
74
  // __weak/__strong ObjC lifetime qualifiers, so it's necessary to string
75
  // search in the source code.
76
  CharSourceRange Range = Lexer::makeFileCharRange(
77
      CharSourceRange::getTokenRange(VD->getSourceRange()), SM, LangOpts);
78
  if (Range.isInvalid()) {
79
    // An invalid range likely means inside a macro, in which case don't supply
80
    // a fix-it.
81
    return std::nullopt;
82
  }
83

84
  StringRef VarDeclText = Lexer::getSourceText(Range, SM, LangOpts);
85
  if (std::optional<FixItHint> Hint =
86
          fixItHintReplacementForOwnershipString(VarDeclText, Range, WeakText))
87
    return Hint;
88

89
  if (std::optional<FixItHint> Hint = fixItHintReplacementForOwnershipString(
90
          VarDeclText, Range, StrongText))
91
    return Hint;
92

93
  return FixItHint::CreateInsertion(Range.getBegin(), "__unsafe_unretained ");
94
}
95

96
} // namespace
97

98
void NSInvocationArgumentLifetimeCheck::registerMatchers(MatchFinder *Finder) {
99
  Finder->addMatcher(
100
      traverse(
101
          TK_AsIs,
102
          objcMessageExpr(
103
              hasReceiverType(asString("NSInvocation *")),
104
              anyOf(hasSelector("getArgument:atIndex:"),
105
                    hasSelector("getReturnValue:")),
106
              hasArgument(
107
                  0,
108
                  anyOf(hasDescendant(memberExpr(isObjCManagedLifetime())),
109
                        hasDescendant(objcIvarRefExpr(isObjCManagedLifetime())),
110
                        hasDescendant(
111
                            // Reference to variables, but when dereferencing
112
                            // to ivars/fields a more-descendent variable
113
                            // reference (e.g. self) may match with strong
114
                            // object lifetime, leading to an incorrect match.
115
                            // Exclude these conditions.
116
                            declRefExpr(to(varDecl().bind("var")),
117
                                        unless(hasParent(implicitCastExpr())),
118
                                        isObjCManagedLifetime())))))
119
              .bind("call")),
120
      this);
121
}
122

123
void NSInvocationArgumentLifetimeCheck::check(
124
    const MatchFinder::MatchResult &Result) {
125
  const auto *MatchedExpr = Result.Nodes.getNodeAs<ObjCMessageExpr>("call");
126

127
  auto Diag = diag(MatchedExpr->getArg(0)->getBeginLoc(),
128
                   "NSInvocation %objcinstance0 should only pass pointers to "
129
                   "objects with ownership __unsafe_unretained")
130
              << MatchedExpr->getSelector();
131

132
  // Only provide fix-it hints for references to local variables; fixes for
133
  // instance variable references don't have as clear an automated fix.
134
  const auto *VD = Result.Nodes.getNodeAs<VarDecl>("var");
135
  if (!VD)
136
    return;
137

138
  if (auto Hint = fixItHintForVarDecl(VD, *Result.SourceManager,
139
                                      Result.Context->getLangOpts()))
140
    Diag << *Hint;
141
}
142

143
} // namespace clang::tidy::objc
144

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.