1//===--- TransAPIUses.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// checkAPIUses:
10//
11// Emits error/fix with some API uses that are obsolete or not safe in ARC mode:
12//
13// - NSInvocation's [get/set]ReturnValue and [get/set]Argument are only safe
14//   with __unsafe_unretained objects.
15// - Calling -zone gets replaced with 'nil'.
16//
17//===----------------------------------------------------------------------===//
18
19#include "Transforms.h"
20#include "Internals.h"
21#include "clang/AST/ASTContext.h"
22#include "clang/Sema/SemaDiagnostic.h"
23
24using namespace clang;
25using namespace arcmt;
26using namespace trans;
27
28namespace {
29
30class APIChecker : public RecursiveASTVisitor<APIChecker> {
31  MigrationPass &Pass;
32
33  Selector getReturnValueSel, setReturnValueSel;
34  Selector getArgumentSel, setArgumentSel;
35
36  Selector zoneSel;
37public:
38  APIChecker(MigrationPass &pass) : Pass(pass) {
39    SelectorTable &sels = Pass.Ctx.Selectors;
40    IdentifierTable &ids = Pass.Ctx.Idents;
41    getReturnValueSel = sels.getUnarySelector(&ids.get("getReturnValue"));
42    setReturnValueSel = sels.getUnarySelector(&ids.get("setReturnValue"));
43
44    IdentifierInfo *selIds[2];
45    selIds[0] = &ids.get("getArgument");
46    selIds[1] = &ids.get("atIndex");
47    getArgumentSel = sels.getSelector(2, selIds);
48    selIds[0] = &ids.get("setArgument");
49    setArgumentSel = sels.getSelector(2, selIds);
50
51    zoneSel = sels.getNullarySelector(&ids.get("zone"));
52  }
53
54  bool VisitObjCMessageExpr(ObjCMessageExpr *E) {
55    // NSInvocation.
56    if (E->isInstanceMessage() &&
57        E->getReceiverInterface() &&
58        E->getReceiverInterface()->getName() == "NSInvocation") {
59      StringRef selName;
60      if (E->getSelector() == getReturnValueSel)
61        selName = "getReturnValue";
62      else if (E->getSelector() == setReturnValueSel)
63        selName = "setReturnValue";
64      else if (E->getSelector() == getArgumentSel)
65        selName = "getArgument";
66      else if (E->getSelector() == setArgumentSel)
67        selName = "setArgument";
68      else
69        return true;
70
71      Expr *parm = E->getArg(0)->IgnoreParenCasts();
72      QualType pointee = parm->getType()->getPointeeType();
73      if (pointee.isNull())
74        return true;
75
76      if (pointee.getObjCLifetime() > Qualifiers::OCL_ExplicitNone)
77        Pass.TA.report(parm->getBeginLoc(),
78                       diag::err_arcmt_nsinvocation_ownership,
79                       parm->getSourceRange())
80            << selName;
81
82      return true;
83    }
84
85    // -zone.
86    if (E->isInstanceMessage() &&
87        E->getInstanceReceiver() &&
88        E->getSelector() == zoneSel &&
89        Pass.TA.hasDiagnostic(diag::err_unavailable,
90                              diag::err_unavailable_message,
91                              E->getSelectorLoc(0))) {
92      // Calling -zone is meaningless in ARC, change it to nil.
93      Transaction Trans(Pass.TA);
94      Pass.TA.clearDiagnostic(diag::err_unavailable,
95                              diag::err_unavailable_message,
96                              E->getSelectorLoc(0));
97      Pass.TA.replace(E->getSourceRange(), getNilString(Pass));
98    }
99    return true;
100  }
101};
102
103} // anonymous namespace
104
105void trans::checkAPIUses(MigrationPass &pass) {
106  APIChecker(pass).TraverseDecl(pass.Ctx.getTranslationUnitDecl());
107}
108