10 #include "clang/AST/ASTContext.h"
11 #include "clang/AST/ComputeDependence.h"
12 #include "clang/AST/Decl.h"
13 #include "clang/AST/Expr.h"
14 #include "clang/AST/ExprObjC.h"
15 #include "clang/AST/Type.h"
16 #include "clang/AST/TypeLoc.h"
17 #include "clang/ASTMatchers/ASTMatchFinder.h"
18 #include "clang/ASTMatchers/ASTMatchers.h"
19 #include "clang/ASTMatchers/ASTMatchersMacros.h"
20 #include "clang/Basic/Diagnostic.h"
21 #include "clang/Basic/LLVM.h"
22 #include "clang/Basic/LangOptions.h"
23 #include "clang/Basic/SourceLocation.h"
24 #include "clang/Basic/SourceManager.h"
25 #include "clang/Lex/Lexer.h"
26 #include "llvm/ADT/None.h"
27 #include "llvm/ADT/Optional.h"
28 #include "llvm/ADT/StringRef.h"
37 static constexpr StringRef WeakText =
"__weak";
38 static constexpr StringRef StrongText =
"__strong";
39 static constexpr StringRef UnsafeUnretainedText =
"__unsafe_unretained";
45 AST_POLYMORPHIC_SUPPORTED_TYPES(ObjCIvarRefExpr,
48 QualType QT = Node.getType();
49 return QT->isScalarType() &&
50 (QT->getScalarTypeKind() == Type::STK_ObjCObjectPointer ||
51 QT->getScalarTypeKind() == Type::STK_BlockPointer) &&
52 QT.getQualifiers().getObjCLifetime() > Qualifiers::OCL_ExplicitNone;
55 static llvm::Optional<FixItHint>
56 fixItHintReplacementForOwnershipString(StringRef Text, CharSourceRange
Range,
57 StringRef Ownership) {
59 if (
Index == StringRef::npos)
62 SourceLocation Begin =
Range.getBegin().getLocWithOffset(
Index);
63 SourceLocation End = Begin.getLocWithOffset(Ownership.size());
64 return FixItHint::CreateReplacement(SourceRange(Begin, End),
65 UnsafeUnretainedText);
68 static llvm::Optional<FixItHint>
69 fixItHintForVarDecl(
const VarDecl *VD,
const SourceManager &SM,
70 const LangOptions &LangOpts) {
71 assert(VD &&
"VarDecl parameter must not be null");
73 if (isa<ParmVarDecl>(VD))
79 CharSourceRange
Range = Lexer::makeFileCharRange(
80 CharSourceRange::getTokenRange(VD->getSourceRange()), SM, LangOpts);
81 if (
Range.isInvalid()) {
87 StringRef VarDeclText = Lexer::getSourceText(
Range, SM, LangOpts);
88 if (llvm::Optional<FixItHint> Hint =
89 fixItHintReplacementForOwnershipString(VarDeclText,
Range, WeakText))
92 if (llvm::Optional<FixItHint> Hint = fixItHintReplacementForOwnershipString(
93 VarDeclText,
Range, StrongText))
96 return FixItHint::CreateInsertion(
Range.getBegin(),
"__unsafe_unretained ");
101 void NSInvocationArgumentLifetimeCheck::registerMatchers(MatchFinder *Finder) {
104 ast_type_traits::TK_AsIs,
106 hasReceiverType(asString(
"NSInvocation *")),
107 anyOf(hasSelector(
"getArgument:atIndex:"),
108 hasSelector(
"getReturnValue:")),
111 anyOf(hasDescendant(memberExpr(isObjCManagedLifetime())),
112 hasDescendant(objcIvarRefExpr(isObjCManagedLifetime())),
119 declRefExpr(to(varDecl().bind(
"var")),
120 unless(hasParent(implicitCastExpr())),
121 isObjCManagedLifetime())))))
126 void NSInvocationArgumentLifetimeCheck::check(
127 const MatchFinder::MatchResult &Result) {
128 const auto *MatchedExpr = Result.Nodes.getNodeAs<ObjCMessageExpr>(
"call");
130 auto Diag = diag(MatchedExpr->getArg(0)->getBeginLoc(),
131 "NSInvocation %objcinstance0 should only pass pointers to "
132 "objects with ownership __unsafe_unretained")
133 << MatchedExpr->getSelector();
137 const auto *VD = Result.Nodes.getNodeAs<VarDecl>(
"var");
141 if (
auto Hint = fixItHintForVarDecl(VD, *Result.SourceManager,
142 Result.Context->getLangOpts()))