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 | |
24 | using namespace clang; |
25 | using namespace arcmt; |
26 | using namespace trans; |
27 | |
28 | namespace { |
29 | |
30 | class APIChecker : public RecursiveASTVisitor<APIChecker> { |
31 | MigrationPass &Pass; |
32 | |
33 | Selector getReturnValueSel, setReturnValueSel; |
34 | Selector getArgumentSel, setArgumentSel; |
35 | |
36 | Selector zoneSel; |
37 | public: |
38 | APIChecker(MigrationPass &pass) : Pass(pass) { |
39 | SelectorTable &sels = Pass.Ctx.Selectors; |
40 | IdentifierTable &ids = Pass.Ctx.Idents; |
41 | getReturnValueSel = sels.getUnarySelector(ID: &ids.get(Name: "getReturnValue" )); |
42 | setReturnValueSel = sels.getUnarySelector(ID: &ids.get(Name: "setReturnValue" )); |
43 | |
44 | const IdentifierInfo *selIds[2]; |
45 | selIds[0] = &ids.get(Name: "getArgument" ); |
46 | selIds[1] = &ids.get(Name: "atIndex" ); |
47 | getArgumentSel = sels.getSelector(NumArgs: 2, IIV: selIds); |
48 | selIds[0] = &ids.get(Name: "setArgument" ); |
49 | setArgumentSel = sels.getSelector(NumArgs: 2, IIV: selIds); |
50 | |
51 | zoneSel = sels.getNullarySelector(ID: &ids.get(Name: "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(Arg: 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(loc: parm->getBeginLoc(), |
78 | diagId: diag::err_arcmt_nsinvocation_ownership, |
79 | range: 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(ID1: diag::err_unavailable, |
90 | ID2: diag::err_unavailable_message, |
91 | range: E->getSelectorLoc(Index: 0))) { |
92 | // Calling -zone is meaningless in ARC, change it to nil. |
93 | Transaction Trans(Pass.TA); |
94 | Pass.TA.clearDiagnostic(ID1: diag::err_unavailable, |
95 | ID2: diag::err_unavailable_message, |
96 | range: E->getSelectorLoc(Index: 0)); |
97 | Pass.TA.replace(range: E->getSourceRange(), text: getNilString(Pass)); |
98 | } |
99 | return true; |
100 | } |
101 | }; |
102 | |
103 | } // anonymous namespace |
104 | |
105 | void trans::checkAPIUses(MigrationPass &pass) { |
106 | APIChecker(pass).TraverseDecl(D: pass.Ctx.getTranslationUnitDecl()); |
107 | } |
108 | |