llvm-project

Форк
0
/
TransProperties.cpp 
379 строк · 12.2 Кб
1
//===--- TransProperties.cpp - Transformations to ARC mode ----------------===//
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
// rewriteProperties:
10
//
11
// - Adds strong/weak/unsafe_unretained ownership specifier to properties that
12
//   are missing one.
13
// - Migrates properties from (retain) to (strong) and (assign) to
14
//   (unsafe_unretained/weak).
15
// - If a property is synthesized, adds the ownership specifier in the ivar
16
//   backing the property.
17
//
18
//  @interface Foo : NSObject {
19
//      NSObject *x;
20
//  }
21
//  @property (assign) id x;
22
//  @end
23
// ---->
24
//  @interface Foo : NSObject {
25
//      NSObject *__weak x;
26
//  }
27
//  @property (weak) id x;
28
//  @end
29
//
30
//===----------------------------------------------------------------------===//
31

32
#include "Transforms.h"
33
#include "Internals.h"
34
#include "clang/Basic/SourceManager.h"
35
#include "clang/Lex/Lexer.h"
36
#include "clang/Sema/SemaDiagnostic.h"
37
#include <map>
38

39
using namespace clang;
40
using namespace arcmt;
41
using namespace trans;
42

43
namespace {
44

45
class PropertiesRewriter {
46
  MigrationContext &MigrateCtx;
47
  MigrationPass &Pass;
48
  ObjCImplementationDecl *CurImplD = nullptr;
49

50
  enum PropActionKind {
51
    PropAction_None,
52
    PropAction_RetainReplacedWithStrong,
53
    PropAction_AssignRemoved,
54
    PropAction_AssignRewritten,
55
    PropAction_MaybeAddWeakOrUnsafe
56
  };
57

58
  struct PropData {
59
    ObjCPropertyDecl *PropD;
60
    ObjCIvarDecl *IvarD;
61
    ObjCPropertyImplDecl *ImplD;
62

63
    PropData(ObjCPropertyDecl *propD)
64
      : PropD(propD), IvarD(nullptr), ImplD(nullptr) {}
65
  };
66

67
  typedef SmallVector<PropData, 2> PropsTy;
68
  typedef std::map<SourceLocation, PropsTy> AtPropDeclsTy;
69
  AtPropDeclsTy AtProps;
70
  llvm::DenseMap<IdentifierInfo *, PropActionKind> ActionOnProp;
71

72
public:
73
  explicit PropertiesRewriter(MigrationContext &MigrateCtx)
74
    : MigrateCtx(MigrateCtx), Pass(MigrateCtx.Pass) { }
75

76
  static void collectProperties(ObjCContainerDecl *D, AtPropDeclsTy &AtProps,
77
                                AtPropDeclsTy *PrevAtProps = nullptr) {
78
    for (auto *Prop : D->instance_properties()) {
79
      SourceLocation Loc = Prop->getAtLoc();
80
      if (Loc.isInvalid())
81
        continue;
82
      if (PrevAtProps)
83
        if (PrevAtProps->find(Loc) != PrevAtProps->end())
84
          continue;
85
      PropsTy &props = AtProps[Loc];
86
      props.push_back(Prop);
87
    }
88
  }
89

90
  void doTransform(ObjCImplementationDecl *D) {
91
    CurImplD = D;
92
    ObjCInterfaceDecl *iface = D->getClassInterface();
93
    if (!iface)
94
      return;
95

96
    collectProperties(iface, AtProps);
97

98
    // Look through extensions.
99
    for (auto *Ext : iface->visible_extensions())
100
      collectProperties(Ext, AtProps);
101

102
    typedef DeclContext::specific_decl_iterator<ObjCPropertyImplDecl>
103
        prop_impl_iterator;
104
    for (prop_impl_iterator
105
           I = prop_impl_iterator(D->decls_begin()),
106
           E = prop_impl_iterator(D->decls_end()); I != E; ++I) {
107
      ObjCPropertyImplDecl *implD = *I;
108
      if (implD->getPropertyImplementation() != ObjCPropertyImplDecl::Synthesize)
109
        continue;
110
      ObjCPropertyDecl *propD = implD->getPropertyDecl();
111
      if (!propD || propD->isInvalidDecl())
112
        continue;
113
      ObjCIvarDecl *ivarD = implD->getPropertyIvarDecl();
114
      if (!ivarD || ivarD->isInvalidDecl())
115
        continue;
116
      AtPropDeclsTy::iterator findAtLoc = AtProps.find(propD->getAtLoc());
117
      if (findAtLoc == AtProps.end())
118
        continue;
119

120
      PropsTy &props = findAtLoc->second;
121
      for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
122
        if (I->PropD == propD) {
123
          I->IvarD = ivarD;
124
          I->ImplD = implD;
125
          break;
126
        }
127
      }
128
    }
129

130
    for (AtPropDeclsTy::iterator
131
           I = AtProps.begin(), E = AtProps.end(); I != E; ++I) {
132
      SourceLocation atLoc = I->first;
133
      PropsTy &props = I->second;
134
      if (!getPropertyType(props)->isObjCRetainableType())
135
        continue;
136
      if (hasIvarWithExplicitARCOwnership(props))
137
        continue;
138

139
      Transaction Trans(Pass.TA);
140
      rewriteProperty(props, atLoc);
141
    }
142
  }
143

144
private:
145
  void doPropAction(PropActionKind kind,
146
                    PropsTy &props, SourceLocation atLoc,
147
                    bool markAction = true) {
148
    if (markAction)
149
      for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
150
        ActionOnProp[I->PropD->getIdentifier()] = kind;
151

152
    switch (kind) {
153
    case PropAction_None:
154
      return;
155
    case PropAction_RetainReplacedWithStrong: {
156
      StringRef toAttr = "strong";
157
      MigrateCtx.rewritePropertyAttribute("retain", toAttr, atLoc);
158
      return;
159
    }
160
    case PropAction_AssignRemoved:
161
      return removeAssignForDefaultStrong(props, atLoc);
162
    case PropAction_AssignRewritten:
163
      return rewriteAssign(props, atLoc);
164
    case PropAction_MaybeAddWeakOrUnsafe:
165
      return maybeAddWeakOrUnsafeUnretainedAttr(props, atLoc);
166
    }
167
  }
168

169
  void rewriteProperty(PropsTy &props, SourceLocation atLoc) {
170
    ObjCPropertyAttribute::Kind propAttrs = getPropertyAttrs(props);
171

172
    if (propAttrs &
173
        (ObjCPropertyAttribute::kind_copy |
174
         ObjCPropertyAttribute::kind_unsafe_unretained |
175
         ObjCPropertyAttribute::kind_strong | ObjCPropertyAttribute::kind_weak))
176
      return;
177

178
    if (propAttrs & ObjCPropertyAttribute::kind_retain) {
179
      // strong is the default.
180
      return doPropAction(PropAction_RetainReplacedWithStrong, props, atLoc);
181
    }
182

183
    bool HasIvarAssignedAPlusOneObject = hasIvarAssignedAPlusOneObject(props);
184

185
    if (propAttrs & ObjCPropertyAttribute::kind_assign) {
186
      if (HasIvarAssignedAPlusOneObject)
187
        return doPropAction(PropAction_AssignRemoved, props, atLoc);
188
      return doPropAction(PropAction_AssignRewritten, props, atLoc);
189
    }
190

191
    if (HasIvarAssignedAPlusOneObject ||
192
        (Pass.isGCMigration() && !hasGCWeak(props, atLoc)))
193
      return; // 'strong' by default.
194

195
    return doPropAction(PropAction_MaybeAddWeakOrUnsafe, props, atLoc);
196
  }
197

198
  void removeAssignForDefaultStrong(PropsTy &props,
199
                                    SourceLocation atLoc) const {
200
    removeAttribute("retain", atLoc);
201
    if (!removeAttribute("assign", atLoc))
202
      return;
203

204
    for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
205
      if (I->ImplD)
206
        Pass.TA.clearDiagnostic(diag::err_arc_strong_property_ownership,
207
                                diag::err_arc_assign_property_ownership,
208
                                diag::err_arc_inconsistent_property_ownership,
209
                                I->IvarD->getLocation());
210
    }
211
  }
212

213
  void rewriteAssign(PropsTy &props, SourceLocation atLoc) const {
214
    bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props),
215
                                  /*AllowOnUnknownClass=*/Pass.isGCMigration());
216
    const char *toWhich =
217
      (Pass.isGCMigration() && !hasGCWeak(props, atLoc)) ? "strong" :
218
      (canUseWeak ? "weak" : "unsafe_unretained");
219

220
    bool rewroteAttr = rewriteAttribute("assign", toWhich, atLoc);
221
    if (!rewroteAttr)
222
      canUseWeak = false;
223

224
    for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
225
      if (isUserDeclared(I->IvarD)) {
226
        if (I->IvarD &&
227
            I->IvarD->getType().getObjCLifetime() != Qualifiers::OCL_Weak) {
228
          const char *toWhich =
229
            (Pass.isGCMigration() && !hasGCWeak(props, atLoc)) ? "__strong " :
230
              (canUseWeak ? "__weak " : "__unsafe_unretained ");
231
          Pass.TA.insert(I->IvarD->getLocation(), toWhich);
232
        }
233
      }
234
      if (I->ImplD)
235
        Pass.TA.clearDiagnostic(diag::err_arc_strong_property_ownership,
236
                                diag::err_arc_assign_property_ownership,
237
                                diag::err_arc_inconsistent_property_ownership,
238
                                I->IvarD->getLocation());
239
    }
240
  }
241

242
  void maybeAddWeakOrUnsafeUnretainedAttr(PropsTy &props,
243
                                          SourceLocation atLoc) const {
244
    bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props),
245
                                  /*AllowOnUnknownClass=*/Pass.isGCMigration());
246

247
    bool addedAttr = addAttribute(canUseWeak ? "weak" : "unsafe_unretained",
248
                                  atLoc);
249
    if (!addedAttr)
250
      canUseWeak = false;
251

252
    for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
253
      if (isUserDeclared(I->IvarD)) {
254
        if (I->IvarD &&
255
            I->IvarD->getType().getObjCLifetime() != Qualifiers::OCL_Weak)
256
          Pass.TA.insert(I->IvarD->getLocation(),
257
                         canUseWeak ? "__weak " : "__unsafe_unretained ");
258
      }
259
      if (I->ImplD) {
260
        Pass.TA.clearDiagnostic(diag::err_arc_strong_property_ownership,
261
                                diag::err_arc_assign_property_ownership,
262
                                diag::err_arc_inconsistent_property_ownership,
263
                                I->IvarD->getLocation());
264
        Pass.TA.clearDiagnostic(
265
                           diag::err_arc_objc_property_default_assign_on_object,
266
                           I->ImplD->getLocation());
267
      }
268
    }
269
  }
270

271
  bool removeAttribute(StringRef fromAttr, SourceLocation atLoc) const {
272
    return MigrateCtx.removePropertyAttribute(fromAttr, atLoc);
273
  }
274

275
  bool rewriteAttribute(StringRef fromAttr, StringRef toAttr,
276
                        SourceLocation atLoc) const {
277
    return MigrateCtx.rewritePropertyAttribute(fromAttr, toAttr, atLoc);
278
  }
279

280
  bool addAttribute(StringRef attr, SourceLocation atLoc) const {
281
    return MigrateCtx.addPropertyAttribute(attr, atLoc);
282
  }
283

284
  class PlusOneAssign : public RecursiveASTVisitor<PlusOneAssign> {
285
    ObjCIvarDecl *Ivar;
286
  public:
287
    PlusOneAssign(ObjCIvarDecl *D) : Ivar(D) {}
288

289
    bool VisitBinaryOperator(BinaryOperator *E) {
290
      if (E->getOpcode() != BO_Assign)
291
        return true;
292

293
      Expr *lhs = E->getLHS()->IgnoreParenImpCasts();
294
      if (ObjCIvarRefExpr *RE = dyn_cast<ObjCIvarRefExpr>(lhs)) {
295
        if (RE->getDecl() != Ivar)
296
          return true;
297

298
        if (isPlusOneAssign(E))
299
          return false;
300
      }
301

302
      return true;
303
    }
304
  };
305

306
  bool hasIvarAssignedAPlusOneObject(PropsTy &props) const {
307
    for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
308
      PlusOneAssign oneAssign(I->IvarD);
309
      bool notFound = oneAssign.TraverseDecl(CurImplD);
310
      if (!notFound)
311
        return true;
312
    }
313

314
    return false;
315
  }
316

317
  bool hasIvarWithExplicitARCOwnership(PropsTy &props) const {
318
    if (Pass.isGCMigration())
319
      return false;
320

321
    for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
322
      if (isUserDeclared(I->IvarD)) {
323
        if (isa<AttributedType>(I->IvarD->getType()))
324
          return true;
325
        if (I->IvarD->getType().getLocalQualifiers().getObjCLifetime()
326
              != Qualifiers::OCL_Strong)
327
          return true;
328
      }
329
    }
330

331
    return false;
332
  }
333

334
  // Returns true if all declarations in the @property have GC __weak.
335
  bool hasGCWeak(PropsTy &props, SourceLocation atLoc) const {
336
    if (!Pass.isGCMigration())
337
      return false;
338
    if (props.empty())
339
      return false;
340
    return MigrateCtx.AtPropsWeak.count(atLoc);
341
  }
342

343
  bool isUserDeclared(ObjCIvarDecl *ivarD) const {
344
    return ivarD && !ivarD->getSynthesize();
345
  }
346

347
  QualType getPropertyType(PropsTy &props) const {
348
    assert(!props.empty());
349
    QualType ty = props[0].PropD->getType().getUnqualifiedType();
350

351
#ifndef NDEBUG
352
    for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
353
      assert(ty == I->PropD->getType().getUnqualifiedType());
354
#endif
355

356
    return ty;
357
  }
358

359
  ObjCPropertyAttribute::Kind getPropertyAttrs(PropsTy &props) const {
360
    assert(!props.empty());
361
    ObjCPropertyAttribute::Kind attrs =
362
        props[0].PropD->getPropertyAttributesAsWritten();
363

364
#ifndef NDEBUG
365
    for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
366
      assert(attrs == I->PropD->getPropertyAttributesAsWritten());
367
#endif
368

369
    return attrs;
370
  }
371
};
372

373
} // anonymous namespace
374

375
void PropertyRewriteTraverser::traverseObjCImplementation(
376
                                           ObjCImplementationContext &ImplCtx) {
377
  PropertiesRewriter(ImplCtx.getMigrationContext())
378
                                  .doTransform(ImplCtx.getImplementationDecl());
379
}
380

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

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

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

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