1249423Sdim//===--- TransUnbridgedCasts.cpp - Transformations to ARC mode ------------===// 2224135Sdim// 3224135Sdim// The LLVM Compiler Infrastructure 4224135Sdim// 5224135Sdim// This file is distributed under the University of Illinois Open Source 6224135Sdim// License. See LICENSE.TXT for details. 7224135Sdim// 8224135Sdim//===----------------------------------------------------------------------===// 9224135Sdim// 10224135Sdim// rewriteUnbridgedCasts: 11224135Sdim// 12224135Sdim// A cast of non-objc pointer to an objc one is checked. If the non-objc pointer 13224135Sdim// is from a file-level variable, __bridge cast is used to convert it. 14224135Sdim// For the result of a function call that we know is +1/+0, 15239462Sdim// __bridge/CFBridgingRelease is used. 16224135Sdim// 17224135Sdim// NSString *str = (NSString *)kUTTypePlainText; 18224135Sdim// str = b ? kUTTypeRTF : kUTTypePlainText; 19224135Sdim// NSString *_uuidString = (NSString *)CFUUIDCreateString(kCFAllocatorDefault, 20224135Sdim// _uuid); 21224135Sdim// ----> 22224135Sdim// NSString *str = (__bridge NSString *)kUTTypePlainText; 23224135Sdim// str = (__bridge NSString *)(b ? kUTTypeRTF : kUTTypePlainText); 24239462Sdim// NSString *_uuidString = (NSString *) 25239462Sdim// CFBridgingRelease(CFUUIDCreateString(kCFAllocatorDefault, _uuid)); 26224135Sdim// 27224135Sdim// For a C pointer to ObjC, for casting 'self', __bridge is used. 28224135Sdim// 29224135Sdim// CFStringRef str = (CFStringRef)self; 30224135Sdim// ----> 31224135Sdim// CFStringRef str = (__bridge CFStringRef)self; 32224135Sdim// 33249423Sdim// Uses of Block_copy/Block_release macros are rewritten: 34249423Sdim// 35249423Sdim// c = Block_copy(b); 36249423Sdim// Block_release(c); 37249423Sdim// ----> 38249423Sdim// c = [b copy]; 39249423Sdim// <removed> 40249423Sdim// 41224135Sdim//===----------------------------------------------------------------------===// 42224135Sdim 43224135Sdim#include "Transforms.h" 44224135Sdim#include "Internals.h" 45239462Sdim#include "clang/AST/ASTContext.h" 46249423Sdim#include "clang/AST/Attr.h" 47226633Sdim#include "clang/AST/ParentMap.h" 48249423Sdim#include "clang/Analysis/DomainSpecific/CocoaConventions.h" 49224135Sdim#include "clang/Basic/SourceManager.h" 50239462Sdim#include "clang/Lex/Lexer.h" 51239462Sdim#include "clang/Sema/SemaDiagnostic.h" 52234353Sdim#include "llvm/ADT/SmallString.h" 53224135Sdim 54224135Sdimusing namespace clang; 55224135Sdimusing namespace arcmt; 56224135Sdimusing namespace trans; 57224135Sdim 58224135Sdimnamespace { 59224135Sdim 60224135Sdimclass UnbridgedCastRewriter : public RecursiveASTVisitor<UnbridgedCastRewriter>{ 61224135Sdim MigrationPass &Pass; 62224135Sdim IdentifierInfo *SelfII; 63234353Sdim OwningPtr<ParentMap> StmtMap; 64239462Sdim Decl *ParentD; 65249423Sdim Stmt *Body; 66249423Sdim mutable OwningPtr<ExprSet> Removables; 67226633Sdim 68224135Sdimpublic: 69249423Sdim UnbridgedCastRewriter(MigrationPass &pass) : Pass(pass), ParentD(0), Body(0) { 70224135Sdim SelfII = &Pass.Ctx.Idents.get("self"); 71224135Sdim } 72224135Sdim 73239462Sdim void transformBody(Stmt *body, Decl *ParentD) { 74239462Sdim this->ParentD = ParentD; 75249423Sdim Body = body; 76226633Sdim StmtMap.reset(new ParentMap(body)); 77226633Sdim TraverseStmt(body); 78226633Sdim } 79226633Sdim 80224135Sdim bool VisitCastExpr(CastExpr *E) { 81249423Sdim if (E->getCastKind() != CK_CPointerToObjCPointerCast && 82249423Sdim E->getCastKind() != CK_BitCast && 83249423Sdim E->getCastKind() != CK_AnyPointerToBlockPointerCast) 84224135Sdim return true; 85224135Sdim 86224135Sdim QualType castType = E->getType(); 87224135Sdim Expr *castExpr = E->getSubExpr(); 88224135Sdim QualType castExprType = castExpr->getType(); 89224135Sdim 90249423Sdim if (castType->isObjCRetainableType() == castExprType->isObjCRetainableType()) 91224135Sdim return true; 92224135Sdim 93224135Sdim bool exprRetainable = castExprType->isObjCIndirectLifetimeType(); 94224135Sdim bool castRetainable = castType->isObjCIndirectLifetimeType(); 95224135Sdim if (exprRetainable == castRetainable) return true; 96224135Sdim 97224135Sdim if (castExpr->isNullPointerConstant(Pass.Ctx, 98224135Sdim Expr::NPC_ValueDependentIsNull)) 99224135Sdim return true; 100224135Sdim 101224135Sdim SourceLocation loc = castExpr->getExprLoc(); 102224135Sdim if (loc.isValid() && Pass.Ctx.getSourceManager().isInSystemHeader(loc)) 103224135Sdim return true; 104224135Sdim 105249423Sdim if (castType->isObjCRetainableType()) 106224135Sdim transformNonObjCToObjCCast(E); 107224135Sdim else 108224135Sdim transformObjCToNonObjCCast(E); 109224135Sdim 110224135Sdim return true; 111224135Sdim } 112224135Sdim 113224135Sdimprivate: 114224135Sdim void transformNonObjCToObjCCast(CastExpr *E) { 115224135Sdim if (!E) return; 116224135Sdim 117224135Sdim // Global vars are assumed that are cast as unretained. 118224135Sdim if (isGlobalVar(E)) 119224135Sdim if (E->getSubExpr()->getType()->isPointerType()) { 120224135Sdim castToObjCObject(E, /*retained=*/false); 121224135Sdim return; 122224135Sdim } 123224135Sdim 124224135Sdim // If the cast is directly over the result of a Core Foundation function 125224135Sdim // try to figure out whether it should be cast as retained or unretained. 126224135Sdim Expr *inner = E->IgnoreParenCasts(); 127224135Sdim if (CallExpr *callE = dyn_cast<CallExpr>(inner)) { 128224135Sdim if (FunctionDecl *FD = callE->getDirectCallee()) { 129224135Sdim if (FD->getAttr<CFReturnsRetainedAttr>()) { 130224135Sdim castToObjCObject(E, /*retained=*/true); 131224135Sdim return; 132224135Sdim } 133224135Sdim if (FD->getAttr<CFReturnsNotRetainedAttr>()) { 134224135Sdim castToObjCObject(E, /*retained=*/false); 135224135Sdim return; 136224135Sdim } 137224135Sdim if (FD->isGlobal() && 138224135Sdim FD->getIdentifier() && 139224135Sdim ento::cocoa::isRefType(E->getSubExpr()->getType(), "CF", 140224135Sdim FD->getIdentifier()->getName())) { 141224135Sdim StringRef fname = FD->getIdentifier()->getName(); 142224135Sdim if (fname.endswith("Retain") || 143224135Sdim fname.find("Create") != StringRef::npos || 144224135Sdim fname.find("Copy") != StringRef::npos) { 145234353Sdim // Do not migrate to couple of bridge transfer casts which 146234353Sdim // cancel each other out. Leave it unchanged so error gets user 147234353Sdim // attention instead. 148234353Sdim if (FD->getName() == "CFRetain" && 149234353Sdim FD->getNumParams() == 1 && 150234353Sdim FD->getParent()->isTranslationUnit() && 151249423Sdim FD->hasExternalLinkage()) { 152234353Sdim Expr *Arg = callE->getArg(0); 153234353Sdim if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(Arg)) { 154234353Sdim const Expr *sub = ICE->getSubExpr(); 155234353Sdim QualType T = sub->getType(); 156234353Sdim if (T->isObjCObjectPointerType()) 157234353Sdim return; 158234353Sdim } 159234353Sdim } 160224135Sdim castToObjCObject(E, /*retained=*/true); 161224135Sdim return; 162224135Sdim } 163224135Sdim 164224135Sdim if (fname.find("Get") != StringRef::npos) { 165224135Sdim castToObjCObject(E, /*retained=*/false); 166224135Sdim return; 167224135Sdim } 168224135Sdim } 169224135Sdim } 170224135Sdim } 171239462Sdim 172239462Sdim // If returning an ivar or a member of an ivar from a +0 method, use 173239462Sdim // a __bridge cast. 174239462Sdim Expr *base = inner->IgnoreParenImpCasts(); 175239462Sdim while (isa<MemberExpr>(base)) 176239462Sdim base = cast<MemberExpr>(base)->getBase()->IgnoreParenImpCasts(); 177239462Sdim if (isa<ObjCIvarRefExpr>(base) && 178239462Sdim isa<ReturnStmt>(StmtMap->getParentIgnoreParenCasts(E))) { 179239462Sdim if (ObjCMethodDecl *method = dyn_cast_or_null<ObjCMethodDecl>(ParentD)) { 180239462Sdim if (!method->hasAttr<NSReturnsRetainedAttr>()) { 181239462Sdim castToObjCObject(E, /*retained=*/false); 182239462Sdim return; 183239462Sdim } 184239462Sdim } 185239462Sdim } 186224135Sdim } 187224135Sdim 188224135Sdim void castToObjCObject(CastExpr *E, bool retained) { 189224135Sdim rewriteToBridgedCast(E, retained ? OBC_BridgeTransfer : OBC_Bridge); 190224135Sdim } 191224135Sdim 192224135Sdim void rewriteToBridgedCast(CastExpr *E, ObjCBridgeCastKind Kind) { 193226633Sdim Transaction Trans(Pass.TA); 194226633Sdim rewriteToBridgedCast(E, Kind, Trans); 195226633Sdim } 196226633Sdim 197226633Sdim void rewriteToBridgedCast(CastExpr *E, ObjCBridgeCastKind Kind, 198226633Sdim Transaction &Trans) { 199224135Sdim TransformActions &TA = Pass.TA; 200224135Sdim 201224135Sdim // We will remove the compiler diagnostic. 202224135Sdim if (!TA.hasDiagnostic(diag::err_arc_mismatched_cast, 203224135Sdim diag::err_arc_cast_requires_bridge, 204226633Sdim E->getLocStart())) { 205226633Sdim Trans.abort(); 206224135Sdim return; 207226633Sdim } 208224135Sdim 209224135Sdim StringRef bridge; 210224135Sdim switch(Kind) { 211224135Sdim case OBC_Bridge: 212224135Sdim bridge = "__bridge "; break; 213224135Sdim case OBC_BridgeTransfer: 214224135Sdim bridge = "__bridge_transfer "; break; 215224135Sdim case OBC_BridgeRetained: 216224135Sdim bridge = "__bridge_retained "; break; 217224135Sdim } 218224135Sdim 219224135Sdim TA.clearDiagnostic(diag::err_arc_mismatched_cast, 220224135Sdim diag::err_arc_cast_requires_bridge, 221224135Sdim E->getLocStart()); 222239462Sdim if (Kind == OBC_Bridge || !Pass.CFBridgingFunctionsDefined()) { 223239462Sdim if (CStyleCastExpr *CCE = dyn_cast<CStyleCastExpr>(E)) { 224239462Sdim TA.insertAfterToken(CCE->getLParenLoc(), bridge); 225239462Sdim } else { 226239462Sdim SourceLocation insertLoc = E->getSubExpr()->getLocStart(); 227239462Sdim SmallString<128> newCast; 228239462Sdim newCast += '('; 229239462Sdim newCast += bridge; 230239462Sdim newCast += E->getType().getAsString(Pass.Ctx.getPrintingPolicy()); 231239462Sdim newCast += ')'; 232239462Sdim 233239462Sdim if (isa<ParenExpr>(E->getSubExpr())) { 234239462Sdim TA.insert(insertLoc, newCast.str()); 235239462Sdim } else { 236239462Sdim newCast += '('; 237239462Sdim TA.insert(insertLoc, newCast.str()); 238239462Sdim TA.insertAfterToken(E->getLocEnd(), ")"); 239239462Sdim } 240239462Sdim } 241224135Sdim } else { 242239462Sdim assert(Kind == OBC_BridgeTransfer || Kind == OBC_BridgeRetained); 243239462Sdim SmallString<32> BridgeCall; 244224135Sdim 245239462Sdim Expr *WrapE = E->getSubExpr(); 246239462Sdim SourceLocation InsertLoc = WrapE->getLocStart(); 247239462Sdim 248239462Sdim SourceManager &SM = Pass.Ctx.getSourceManager(); 249239462Sdim char PrevChar = *SM.getCharacterData(InsertLoc.getLocWithOffset(-1)); 250239462Sdim if (Lexer::isIdentifierBodyChar(PrevChar, Pass.Ctx.getLangOpts())) 251239462Sdim BridgeCall += ' '; 252239462Sdim 253239462Sdim if (Kind == OBC_BridgeTransfer) 254239462Sdim BridgeCall += "CFBridgingRelease"; 255239462Sdim else 256239462Sdim BridgeCall += "CFBridgingRetain"; 257239462Sdim 258239462Sdim if (isa<ParenExpr>(WrapE)) { 259239462Sdim TA.insert(InsertLoc, BridgeCall); 260224135Sdim } else { 261239462Sdim BridgeCall += '('; 262239462Sdim TA.insert(InsertLoc, BridgeCall); 263239462Sdim TA.insertAfterToken(WrapE->getLocEnd(), ")"); 264224135Sdim } 265224135Sdim } 266224135Sdim } 267224135Sdim 268226633Sdim void rewriteCastForCFRetain(CastExpr *castE, CallExpr *callE) { 269226633Sdim Transaction Trans(Pass.TA); 270226633Sdim Pass.TA.replace(callE->getSourceRange(), callE->getArg(0)->getSourceRange()); 271226633Sdim rewriteToBridgedCast(castE, OBC_BridgeRetained, Trans); 272226633Sdim } 273226633Sdim 274249423Sdim void getBlockMacroRanges(CastExpr *E, SourceRange &Outer, SourceRange &Inner) { 275249423Sdim SourceManager &SM = Pass.Ctx.getSourceManager(); 276249423Sdim SourceLocation Loc = E->getExprLoc(); 277249423Sdim assert(Loc.isMacroID()); 278249423Sdim SourceLocation MacroBegin, MacroEnd; 279249423Sdim llvm::tie(MacroBegin, MacroEnd) = SM.getImmediateExpansionRange(Loc); 280249423Sdim SourceRange SubRange = E->getSubExpr()->IgnoreParenImpCasts()->getSourceRange(); 281249423Sdim SourceLocation InnerBegin = SM.getImmediateMacroCallerLoc(SubRange.getBegin()); 282249423Sdim SourceLocation InnerEnd = SM.getImmediateMacroCallerLoc(SubRange.getEnd()); 283249423Sdim 284249423Sdim Outer = SourceRange(MacroBegin, MacroEnd); 285249423Sdim Inner = SourceRange(InnerBegin, InnerEnd); 286249423Sdim } 287249423Sdim 288249423Sdim void rewriteBlockCopyMacro(CastExpr *E) { 289249423Sdim SourceRange OuterRange, InnerRange; 290249423Sdim getBlockMacroRanges(E, OuterRange, InnerRange); 291249423Sdim 292249423Sdim Transaction Trans(Pass.TA); 293249423Sdim Pass.TA.replace(OuterRange, InnerRange); 294249423Sdim Pass.TA.insert(InnerRange.getBegin(), "["); 295249423Sdim Pass.TA.insertAfterToken(InnerRange.getEnd(), " copy]"); 296249423Sdim Pass.TA.clearDiagnostic(diag::err_arc_mismatched_cast, 297249423Sdim diag::err_arc_cast_requires_bridge, 298249423Sdim OuterRange); 299249423Sdim } 300249423Sdim 301249423Sdim void removeBlockReleaseMacro(CastExpr *E) { 302249423Sdim SourceRange OuterRange, InnerRange; 303249423Sdim getBlockMacroRanges(E, OuterRange, InnerRange); 304249423Sdim 305249423Sdim Transaction Trans(Pass.TA); 306249423Sdim Pass.TA.clearDiagnostic(diag::err_arc_mismatched_cast, 307249423Sdim diag::err_arc_cast_requires_bridge, 308249423Sdim OuterRange); 309249423Sdim if (!hasSideEffects(E, Pass.Ctx)) { 310249423Sdim if (tryRemoving(cast<Expr>(StmtMap->getParentIgnoreParenCasts(E)))) 311249423Sdim return; 312249423Sdim } 313249423Sdim Pass.TA.replace(OuterRange, InnerRange); 314249423Sdim } 315249423Sdim 316249423Sdim bool tryRemoving(Expr *E) const { 317249423Sdim if (!Removables) { 318249423Sdim Removables.reset(new ExprSet); 319249423Sdim collectRemovables(Body, *Removables); 320249423Sdim } 321249423Sdim 322249423Sdim if (Removables->count(E)) { 323249423Sdim Pass.TA.removeStmt(E); 324249423Sdim return true; 325249423Sdim } 326249423Sdim 327249423Sdim return false; 328249423Sdim } 329249423Sdim 330224135Sdim void transformObjCToNonObjCCast(CastExpr *E) { 331249423Sdim SourceLocation CastLoc = E->getExprLoc(); 332249423Sdim if (CastLoc.isMacroID()) { 333249423Sdim StringRef MacroName = Lexer::getImmediateMacroName(CastLoc, 334249423Sdim Pass.Ctx.getSourceManager(), 335249423Sdim Pass.Ctx.getLangOpts()); 336249423Sdim if (MacroName == "Block_copy") { 337249423Sdim rewriteBlockCopyMacro(E); 338249423Sdim return; 339249423Sdim } 340249423Sdim if (MacroName == "Block_release") { 341249423Sdim removeBlockReleaseMacro(E); 342249423Sdim return; 343249423Sdim } 344249423Sdim } 345249423Sdim 346224135Sdim if (isSelf(E->getSubExpr())) 347224135Sdim return rewriteToBridgedCast(E, OBC_Bridge); 348226633Sdim 349226633Sdim CallExpr *callE; 350226633Sdim if (isPassedToCFRetain(E, callE)) 351226633Sdim return rewriteCastForCFRetain(E, callE); 352226633Sdim 353226633Sdim ObjCMethodFamily family = getFamilyOfMessage(E->getSubExpr()); 354226633Sdim if (family == OMF_retain) 355226633Sdim return rewriteToBridgedCast(E, OBC_BridgeRetained); 356226633Sdim 357226633Sdim if (family == OMF_autorelease || family == OMF_release) { 358226633Sdim std::string err = "it is not safe to cast to '"; 359226633Sdim err += E->getType().getAsString(Pass.Ctx.getPrintingPolicy()); 360226633Sdim err += "' the result of '"; 361226633Sdim err += family == OMF_autorelease ? "autorelease" : "release"; 362226633Sdim err += "' message; a __bridge cast may result in a pointer to a " 363226633Sdim "destroyed object and a __bridge_retained may leak the object"; 364226633Sdim Pass.TA.reportError(err, E->getLocStart(), 365226633Sdim E->getSubExpr()->getSourceRange()); 366226633Sdim Stmt *parent = E; 367226633Sdim do { 368226633Sdim parent = StmtMap->getParentIgnoreParenImpCasts(parent); 369226633Sdim } while (parent && isa<ExprWithCleanups>(parent)); 370226633Sdim 371226633Sdim if (ReturnStmt *retS = dyn_cast_or_null<ReturnStmt>(parent)) { 372226633Sdim std::string note = "remove the cast and change return type of function " 373226633Sdim "to '"; 374226633Sdim note += E->getSubExpr()->getType().getAsString(Pass.Ctx.getPrintingPolicy()); 375226633Sdim note += "' to have the object automatically autoreleased"; 376226633Sdim Pass.TA.reportNote(note, retS->getLocStart()); 377226633Sdim } 378226633Sdim } 379226633Sdim 380234353Sdim Expr *subExpr = E->getSubExpr(); 381234353Sdim 382234353Sdim // Look through pseudo-object expressions. 383234353Sdim if (PseudoObjectExpr *pseudo = dyn_cast<PseudoObjectExpr>(subExpr)) { 384234353Sdim subExpr = pseudo->getResultExpr(); 385234353Sdim assert(subExpr && "no result for pseudo-object of non-void type?"); 386234353Sdim } 387234353Sdim 388234353Sdim if (ImplicitCastExpr *implCE = dyn_cast<ImplicitCastExpr>(subExpr)) { 389226633Sdim if (implCE->getCastKind() == CK_ARCConsumeObject) 390226633Sdim return rewriteToBridgedCast(E, OBC_BridgeRetained); 391226633Sdim if (implCE->getCastKind() == CK_ARCReclaimReturnedObject) 392226633Sdim return rewriteToBridgedCast(E, OBC_Bridge); 393226633Sdim } 394226633Sdim 395226633Sdim bool isConsumed = false; 396226633Sdim if (isPassedToCParamWithKnownOwnership(E, isConsumed)) 397226633Sdim return rewriteToBridgedCast(E, isConsumed ? OBC_BridgeRetained 398226633Sdim : OBC_Bridge); 399224135Sdim } 400224135Sdim 401226633Sdim static ObjCMethodFamily getFamilyOfMessage(Expr *E) { 402226633Sdim E = E->IgnoreParenCasts(); 403226633Sdim if (ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(E)) 404226633Sdim return ME->getMethodFamily(); 405226633Sdim 406226633Sdim return OMF_None; 407226633Sdim } 408226633Sdim 409226633Sdim bool isPassedToCFRetain(Expr *E, CallExpr *&callE) const { 410226633Sdim if ((callE = dyn_cast_or_null<CallExpr>( 411226633Sdim StmtMap->getParentIgnoreParenImpCasts(E)))) 412226633Sdim if (FunctionDecl * 413226633Sdim FD = dyn_cast_or_null<FunctionDecl>(callE->getCalleeDecl())) 414226633Sdim if (FD->getName() == "CFRetain" && FD->getNumParams() == 1 && 415226633Sdim FD->getParent()->isTranslationUnit() && 416249423Sdim FD->hasExternalLinkage()) 417226633Sdim return true; 418226633Sdim 419226633Sdim return false; 420226633Sdim } 421226633Sdim 422226633Sdim bool isPassedToCParamWithKnownOwnership(Expr *E, bool &isConsumed) const { 423226633Sdim if (CallExpr *callE = dyn_cast_or_null<CallExpr>( 424226633Sdim StmtMap->getParentIgnoreParenImpCasts(E))) 425226633Sdim if (FunctionDecl * 426226633Sdim FD = dyn_cast_or_null<FunctionDecl>(callE->getCalleeDecl())) { 427226633Sdim unsigned i = 0; 428226633Sdim for (unsigned e = callE->getNumArgs(); i != e; ++i) { 429226633Sdim Expr *arg = callE->getArg(i); 430226633Sdim if (arg == E || arg->IgnoreParenImpCasts() == E) 431226633Sdim break; 432226633Sdim } 433249423Sdim if (i < callE->getNumArgs() && i < FD->getNumParams()) { 434226633Sdim ParmVarDecl *PD = FD->getParamDecl(i); 435226633Sdim if (PD->getAttr<CFConsumedAttr>()) { 436226633Sdim isConsumed = true; 437226633Sdim return true; 438226633Sdim } 439226633Sdim } 440226633Sdim } 441226633Sdim 442226633Sdim return false; 443226633Sdim } 444226633Sdim 445226633Sdim bool isSelf(Expr *E) const { 446224135Sdim E = E->IgnoreParenLValueCasts(); 447224135Sdim if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) 448226633Sdim if (ImplicitParamDecl *IPD = dyn_cast<ImplicitParamDecl>(DRE->getDecl())) 449226633Sdim if (IPD->getIdentifier() == SelfII) 450226633Sdim return true; 451226633Sdim 452224135Sdim return false; 453224135Sdim } 454224135Sdim}; 455224135Sdim 456224135Sdim} // end anonymous namespace 457224135Sdim 458224135Sdimvoid trans::rewriteUnbridgedCasts(MigrationPass &pass) { 459226633Sdim BodyTransform<UnbridgedCastRewriter> trans(pass); 460224135Sdim trans.TraverseDecl(pass.Ctx.getTranslationUnitDecl()); 461224135Sdim} 462