Coverage Report

Created: 2019-07-24 05:18

/Users/buildslave/jenkins/workspace/clang-stage2-coverage-R/llvm/tools/clang/lib/ARCMigrate/TransProperties.cpp
Line
Count
Source (jump to first uncovered line)
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;
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
195
      : PropD(propD), IvarD(nullptr), ImplD(nullptr) {}
65
  };
66
67
  typedef SmallVector<PropData, 2> PropsTy;
68
  typedef std::map<unsigned, PropsTy> AtPropDeclsTy;
69
  AtPropDeclsTy AtProps;
70
  llvm::DenseMap<IdentifierInfo *, PropActionKind> ActionOnProp;
71
72
public:
73
  explicit PropertiesRewriter(MigrationContext &MigrateCtx)
74
88
    : MigrateCtx(MigrateCtx), Pass(MigrateCtx.Pass) { }
75
76
  static void collectProperties(ObjCContainerDecl *D, AtPropDeclsTy &AtProps,
77
90
                                AtPropDeclsTy *PrevAtProps = nullptr) {
78
195
    for (auto *Prop : D->instance_properties()) {
79
195
      if (Prop->getAtLoc().isInvalid())
80
0
        continue;
81
195
      unsigned RawLoc = Prop->getAtLoc().getRawEncoding();
82
195
      if (PrevAtProps)
83
0
        if (PrevAtProps->find(RawLoc) != PrevAtProps->end())
84
0
          continue;
85
195
      PropsTy &props = AtProps[RawLoc];
86
195
      props.push_back(Prop);
87
195
    }
88
90
  }
89
90
88
  void doTransform(ObjCImplementationDecl *D) {
91
88
    CurImplD = D;
92
88
    ObjCInterfaceDecl *iface = D->getClassInterface();
93
88
    if (!iface)
94
0
      return;
95
88
96
88
    collectProperties(iface, AtProps);
97
88
98
88
    // Look through extensions.
99
88
    for (auto *Ext : iface->visible_extensions())
100
2
      collectProperties(Ext, AtProps);
101
88
102
88
    typedef DeclContext::specific_decl_iterator<ObjCPropertyImplDecl>
103
88
        prop_impl_iterator;
104
88
    for (prop_impl_iterator
105
88
           I = prop_impl_iterator(D->decls_begin()),
106
275
           E = prop_impl_iterator(D->decls_end()); I != E; 
++I187
) {
107
187
      ObjCPropertyImplDecl *implD = *I;
108
187
      if (implD->getPropertyImplementation() != ObjCPropertyImplDecl::Synthesize)
109
2
        continue;
110
185
      ObjCPropertyDecl *propD = implD->getPropertyDecl();
111
185
      if (!propD || propD->isInvalidDecl())
112
51
        continue;
113
134
      ObjCIvarDecl *ivarD = implD->getPropertyIvarDecl();
114
134
      if (!ivarD || ivarD->isInvalidDecl())
115
0
        continue;
116
134
      unsigned rawAtLoc = propD->getAtLoc().getRawEncoding();
117
134
      AtPropDeclsTy::iterator findAtLoc = AtProps.find(rawAtLoc);
118
134
      if (findAtLoc == AtProps.end())
119
0
        continue;
120
134
121
134
      PropsTy &props = findAtLoc->second;
122
136
      for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; 
++I2
) {
123
136
        if (I->PropD == propD) {
124
134
          I->IvarD = ivarD;
125
134
          I->ImplD = implD;
126
134
          break;
127
134
        }
128
136
      }
129
134
    }
130
88
131
88
    for (AtPropDeclsTy::iterator
132
272
           I = AtProps.begin(), E = AtProps.end(); I != E; 
++I184
) {
133
184
      SourceLocation atLoc = SourceLocation::getFromRawEncoding(I->first);
134
184
      PropsTy &props = I->second;
135
184
      if (!getPropertyType(props)->isObjCRetainableType())
136
0
        continue;
137
184
      if (hasIvarWithExplicitARCOwnership(props))
138
0
        continue;
139
184
140
184
      Transaction Trans(Pass.TA);
141
184
      rewriteProperty(props, atLoc);
142
184
    }
143
88
  }
144
145
private:
146
  void doPropAction(PropActionKind kind,
147
                    PropsTy &props, SourceLocation atLoc,
148
155
                    bool markAction = true) {
149
155
    if (markAction)
150
321
      
for (PropsTy::iterator I = props.begin(), E = props.end(); 155
I != E;
++I166
)
151
166
        ActionOnProp[I->PropD->getIdentifier()] = kind;
152
155
153
155
    switch (kind) {
154
155
    case PropAction_None:
155
0
      return;
156
155
    case PropAction_RetainReplacedWithStrong: {
157
50
      StringRef toAttr = "strong";
158
50
      MigrateCtx.rewritePropertyAttribute("retain", toAttr, atLoc);
159
50
      return;
160
155
    }
161
155
    case PropAction_AssignRemoved:
162
1
      return removeAssignForDefaultStrong(props, atLoc);
163
155
    case PropAction_AssignRewritten:
164
82
      return rewriteAssign(props, atLoc);
165
155
    case PropAction_MaybeAddWeakOrUnsafe:
166
22
      return maybeAddWeakOrUnsafeUnretainedAttr(props, atLoc);
167
155
    }
168
155
  }
169
170
184
  void rewriteProperty(PropsTy &props, SourceLocation atLoc) {
171
184
    ObjCPropertyDecl::PropertyAttributeKind propAttrs = getPropertyAttrs(props);
172
184
173
184
    if (propAttrs & (ObjCPropertyDecl::OBJC_PR_copy |
174
184
                     ObjCPropertyDecl::OBJC_PR_unsafe_unretained |
175
184
                     ObjCPropertyDecl::OBJC_PR_strong |
176
184
                     ObjCPropertyDecl::OBJC_PR_weak))
177
7
      return;
178
177
179
177
    if (propAttrs & ObjCPropertyDecl::OBJC_PR_retain) {
180
50
      // strong is the default.
181
50
      return doPropAction(PropAction_RetainReplacedWithStrong, props, atLoc);
182
50
    }
183
127
184
127
    bool HasIvarAssignedAPlusOneObject = hasIvarAssignedAPlusOneObject(props);
185
127
186
127
    if (propAttrs & ObjCPropertyDecl::OBJC_PR_assign) {
187
83
      if (HasIvarAssignedAPlusOneObject)
188
1
        return doPropAction(PropAction_AssignRemoved, props, atLoc);
189
82
      return doPropAction(PropAction_AssignRewritten, props, atLoc);
190
82
    }
191
44
192
44
    if (HasIvarAssignedAPlusOneObject ||
193
44
        
(38
Pass.isGCMigration()38
&&
!hasGCWeak(props, atLoc)24
))
194
22
      return; // 'strong' by default.
195
22
196
22
    return doPropAction(PropAction_MaybeAddWeakOrUnsafe, props, atLoc);
197
22
  }
198
199
  void removeAssignForDefaultStrong(PropsTy &props,
200
1
                                    SourceLocation atLoc) const {
201
1
    removeAttribute("retain", atLoc);
202
1
    if (!removeAttribute("assign", atLoc))
203
0
      return;
204
1
205
2
    
for (PropsTy::iterator I = props.begin(), E = props.end(); 1
I != E;
++I1
) {
206
1
      if (I->ImplD)
207
1
        Pass.TA.clearDiagnostic(diag::err_arc_strong_property_ownership,
208
1
                                diag::err_arc_assign_property_ownership,
209
1
                                diag::err_arc_inconsistent_property_ownership,
210
1
                                I->IvarD->getLocation());
211
1
    }
212
1
  }
213
214
82
  void rewriteAssign(PropsTy &props, SourceLocation atLoc) const {
215
82
    bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props),
216
82
                                  /*AllowOnUnknownClass=*/Pass.isGCMigration());
217
82
    const char *toWhich =
218
82
      (Pass.isGCMigration() && 
!hasGCWeak(props, atLoc)68
) ?
"strong"43
:
219
82
      
(canUseWeak 39
?
"weak"28
:
"unsafe_unretained"11
);
220
82
221
82
    bool rewroteAttr = rewriteAttribute("assign", toWhich, atLoc);
222
82
    if (!rewroteAttr)
223
0
      canUseWeak = false;
224
82
225
173
    for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; 
++I91
) {
226
91
      if (isUserDeclared(I->IvarD)) {
227
19
        if (I->IvarD &&
228
19
            I->IvarD->getType().getObjCLifetime() != Qualifiers::OCL_Weak) {
229
19
          const char *toWhich =
230
19
            (Pass.isGCMigration() && 
!hasGCWeak(props, atLoc)9
) ?
"__strong "9
:
231
19
              
(canUseWeak 10
?
"__weak "2
:
"__unsafe_unretained "8
);
232
19
          Pass.TA.insert(I->IvarD->getLocation(), toWhich);
233
19
        }
234
19
      }
235
91
      if (I->ImplD)
236
40
        Pass.TA.clearDiagnostic(diag::err_arc_strong_property_ownership,
237
40
                                diag::err_arc_assign_property_ownership,
238
40
                                diag::err_arc_inconsistent_property_ownership,
239
40
                                I->IvarD->getLocation());
240
91
    }
241
82
  }
242
243
  void maybeAddWeakOrUnsafeUnretainedAttr(PropsTy &props,
244
22
                                          SourceLocation atLoc) const {
245
22
    bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props),
246
22
                                  /*AllowOnUnknownClass=*/Pass.isGCMigration());
247
22
248
22
    bool addedAttr = addAttribute(canUseWeak ? 
"weak"18
:
"unsafe_unretained"4
,
249
22
                                  atLoc);
250
22
    if (!addedAttr)
251
0
      canUseWeak = false;
252
22
253
46
    for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; 
++I24
) {
254
24
      if (isUserDeclared(I->IvarD)) {
255
18
        if (I->IvarD &&
256
18
            I->IvarD->getType().getObjCLifetime() != Qualifiers::OCL_Weak)
257
10
          Pass.TA.insert(I->IvarD->getLocation(),
258
10
                         canUseWeak ? 
"__weak "6
:
"__unsafe_unretained "4
);
259
18
      }
260
24
      if (I->ImplD) {
261
22
        Pass.TA.clearDiagnostic(diag::err_arc_strong_property_ownership,
262
22
                                diag::err_arc_assign_property_ownership,
263
22
                                diag::err_arc_inconsistent_property_ownership,
264
22
                                I->IvarD->getLocation());
265
22
        Pass.TA.clearDiagnostic(
266
22
                           diag::err_arc_objc_property_default_assign_on_object,
267
22
                           I->ImplD->getLocation());
268
22
      }
269
24
    }
270
22
  }
271
272
2
  bool removeAttribute(StringRef fromAttr, SourceLocation atLoc) const {
273
2
    return MigrateCtx.removePropertyAttribute(fromAttr, atLoc);
274
2
  }
275
276
  bool rewriteAttribute(StringRef fromAttr, StringRef toAttr,
277
82
                        SourceLocation atLoc) const {
278
82
    return MigrateCtx.rewritePropertyAttribute(fromAttr, toAttr, atLoc);
279
82
  }
280
281
22
  bool addAttribute(StringRef attr, SourceLocation atLoc) const {
282
22
    return MigrateCtx.addPropertyAttribute(attr, atLoc);
283
22
  }
284
285
  class PlusOneAssign : public RecursiveASTVisitor<PlusOneAssign> {
286
    ObjCIvarDecl *Ivar;
287
  public:
288
138
    PlusOneAssign(ObjCIvarDecl *D) : Ivar(D) {}
289
290
81
    bool VisitBinAssign(BinaryOperator *E) {
291
81
      Expr *lhs = E->getLHS()->IgnoreParenImpCasts();
292
81
      if (ObjCIvarRefExpr *RE = dyn_cast<ObjCIvarRefExpr>(lhs)) {
293
79
        if (RE->getDecl() != Ivar)
294
72
          return true;
295
7
296
7
        if (isPlusOneAssign(E))
297
7
          return false;
298
2
      }
299
2
300
2
      return true;
301
2
    }
302
  };
303
304
127
  bool hasIvarAssignedAPlusOneObject(PropsTy &props) const {
305
258
    for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; 
++I131
) {
306
138
      PlusOneAssign oneAssign(I->IvarD);
307
138
      bool notFound = oneAssign.TraverseDecl(CurImplD);
308
138
      if (!notFound)
309
7
        return true;
310
138
    }
311
127
312
127
    
return false120
;
313
127
  }
314
315
184
  bool hasIvarWithExplicitARCOwnership(PropsTy &props) const {
316
184
    if (Pass.isGCMigration())
317
101
      return false;
318
83
319
168
    
for (PropsTy::iterator I = props.begin(), E = props.end(); 83
I != E;
++I85
) {
320
85
      if (isUserDeclared(I->IvarD)) {
321
28
        if (isa<AttributedType>(I->IvarD->getType()))
322
0
          return true;
323
28
        if (I->IvarD->getType().getLocalQualifiers().getObjCLifetime()
324
28
              != Qualifiers::OCL_Strong)
325
0
          return true;
326
28
      }
327
85
    }
328
83
329
83
    return false;
330
83
  }
331
332
  // Returns true if all declarations in the @property have GC __weak.
333
101
  bool hasGCWeak(PropsTy &props, SourceLocation atLoc) const {
334
101
    if (!Pass.isGCMigration())
335
0
      return false;
336
101
    if (props.empty())
337
0
      return false;
338
101
    return MigrateCtx.AtPropsWeak.count(atLoc.getRawEncoding());
339
101
  }
340
341
200
  bool isUserDeclared(ObjCIvarDecl *ivarD) const {
342
200
    return ivarD && 
!ivarD->getSynthesize()137
;
343
200
  }
344
345
288
  QualType getPropertyType(PropsTy &props) const {
346
288
    assert(!props.empty());
347
288
    QualType ty = props[0].PropD->getType().getUnqualifiedType();
348
288
349
#ifndef NDEBUG
350
    for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
351
      assert(ty == I->PropD->getType().getUnqualifiedType());
352
#endif
353
354
288
    return ty;
355
288
  }
356
357
  ObjCPropertyDecl::PropertyAttributeKind
358
184
  getPropertyAttrs(PropsTy &props) const {
359
184
    assert(!props.empty());
360
184
    ObjCPropertyDecl::PropertyAttributeKind
361
184
      attrs = props[0].PropD->getPropertyAttributesAsWritten();
362
184
363
#ifndef NDEBUG
364
    for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
365
      assert(attrs == I->PropD->getPropertyAttributesAsWritten());
366
#endif
367
368
184
    return attrs;
369
184
  }
370
};
371
372
} // anonymous namespace
373
374
void PropertyRewriteTraverser::traverseObjCImplementation(
375
88
                                           ObjCImplementationContext &ImplCtx) {
376
88
  PropertiesRewriter(ImplCtx.getMigrationContext())
377
88
                                  .doTransform(ImplCtx.getImplementationDecl());
378
88
}