111 lines
		
	
	
		
			3.6 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			111 lines
		
	
	
		
			3.6 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| //===--- TransAPIUses.cpp - Transformations to ARC mode -------------------===//
 | |
| //
 | |
| //                     The LLVM Compiler Infrastructure
 | |
| //
 | |
| // This file is distributed under the University of Illinois Open Source
 | |
| // License. See LICENSE.TXT for details.
 | |
| //
 | |
| //===----------------------------------------------------------------------===//
 | |
| //
 | |
| // checkAPIUses:
 | |
| //
 | |
| // Emits error/fix with some API uses that are obsolete or not safe in ARC mode:
 | |
| //
 | |
| // - NSInvocation's [get/set]ReturnValue and [get/set]Argument are only safe
 | |
| //   with __unsafe_unretained objects.
 | |
| // - Calling -zone gets replaced with 'nil'.
 | |
| //
 | |
| //===----------------------------------------------------------------------===//
 | |
| 
 | |
| #include "Transforms.h"
 | |
| #include "Internals.h"
 | |
| #include "clang/AST/ASTContext.h"
 | |
| #include "clang/Sema/SemaDiagnostic.h"
 | |
| 
 | |
| using namespace clang;
 | |
| using namespace arcmt;
 | |
| using namespace trans;
 | |
| 
 | |
| namespace {
 | |
| 
 | |
| class APIChecker : public RecursiveASTVisitor<APIChecker> {
 | |
|   MigrationPass &Pass;
 | |
| 
 | |
|   Selector getReturnValueSel, setReturnValueSel;
 | |
|   Selector getArgumentSel, setArgumentSel;
 | |
| 
 | |
|   Selector zoneSel;
 | |
| public:
 | |
|   APIChecker(MigrationPass &pass) : Pass(pass) {
 | |
|     SelectorTable &sels = Pass.Ctx.Selectors;
 | |
|     IdentifierTable &ids = Pass.Ctx.Idents;
 | |
|     getReturnValueSel = sels.getUnarySelector(&ids.get("getReturnValue"));
 | |
|     setReturnValueSel = sels.getUnarySelector(&ids.get("setReturnValue"));
 | |
| 
 | |
|     IdentifierInfo *selIds[2];
 | |
|     selIds[0] = &ids.get("getArgument");
 | |
|     selIds[1] = &ids.get("atIndex");
 | |
|     getArgumentSel = sels.getSelector(2, selIds);
 | |
|     selIds[0] = &ids.get("setArgument");
 | |
|     setArgumentSel = sels.getSelector(2, selIds);
 | |
| 
 | |
|     zoneSel = sels.getNullarySelector(&ids.get("zone"));
 | |
|   }
 | |
| 
 | |
|   bool VisitObjCMessageExpr(ObjCMessageExpr *E) {
 | |
|     // NSInvocation.
 | |
|     if (E->isInstanceMessage() &&
 | |
|         E->getReceiverInterface() &&
 | |
|         E->getReceiverInterface()->getName() == "NSInvocation") {
 | |
|       StringRef selName;
 | |
|       if (E->getSelector() == getReturnValueSel)
 | |
|         selName = "getReturnValue";
 | |
|       else if (E->getSelector() == setReturnValueSel)
 | |
|         selName = "setReturnValue";
 | |
|       else if (E->getSelector() == getArgumentSel)
 | |
|         selName = "getArgument";
 | |
|       else if (E->getSelector() == setArgumentSel)
 | |
|         selName = "setArgument";
 | |
| 
 | |
|       if (selName.empty())
 | |
|         return true;
 | |
| 
 | |
|       Expr *parm = E->getArg(0)->IgnoreParenCasts();
 | |
|       QualType pointee = parm->getType()->getPointeeType();
 | |
|       if (pointee.isNull())
 | |
|         return true;
 | |
| 
 | |
|       if (pointee.getObjCLifetime() > Qualifiers::OCL_ExplicitNone) {
 | |
|         std::string err = "NSInvocation's ";
 | |
|         err += selName;
 | |
|         err += " is not safe to be used with an object with ownership other "
 | |
|             "than __unsafe_unretained";
 | |
|         Pass.TA.reportError(err, parm->getLocStart(), parm->getSourceRange());
 | |
|       }
 | |
|       return true;
 | |
|     }
 | |
| 
 | |
|     // -zone.
 | |
|     if (E->isInstanceMessage() &&
 | |
|         E->getInstanceReceiver() &&
 | |
|         E->getSelector() == zoneSel &&
 | |
|         Pass.TA.hasDiagnostic(diag::err_unavailable,
 | |
|                               diag::err_unavailable_message,
 | |
|                               E->getSelectorLoc(0))) {
 | |
|       // Calling -zone is meaningless in ARC, change it to nil.
 | |
|       Transaction Trans(Pass.TA);
 | |
|       Pass.TA.clearDiagnostic(diag::err_unavailable,
 | |
|                               diag::err_unavailable_message,
 | |
|                               E->getSelectorLoc(0));
 | |
|       Pass.TA.replace(E->getSourceRange(), getNilString(Pass.Ctx));
 | |
|     }
 | |
|     return true;
 | |
|   }
 | |
| };
 | |
| 
 | |
| } // anonymous namespace
 | |
| 
 | |
| void trans::checkAPIUses(MigrationPass &pass) {
 | |
|   APIChecker(pass).TraverseDecl(pass.Ctx.getTranslationUnitDecl());
 | |
| }
 | 
