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/StringRef.h"
34static constexpr StringRef WeakText =
"__weak";
35static constexpr StringRef StrongText =
"__strong";
36static constexpr StringRef UnsafeUnretainedText =
"__unsafe_unretained";
42 AST_POLYMORPHIC_SUPPORTED_TYPES(ObjCIvarRefExpr,
45 QualType QT =
Node.getType();
46 return QT->isScalarType() &&
47 (QT->getScalarTypeKind() == Type::STK_ObjCObjectPointer ||
48 QT->getScalarTypeKind() == Type::STK_BlockPointer) &&
49 QT.getQualifiers().getObjCLifetime() > Qualifiers::OCL_ExplicitNone;
52static std::optional<FixItHint>
53fixItHintReplacementForOwnershipString(StringRef
Text, CharSourceRange Range,
54 StringRef Ownership) {
55 size_t Index =
Text.find(Ownership);
56 if (Index == StringRef::npos)
59 SourceLocation Begin =
Range.getBegin().getLocWithOffset(Index);
60 SourceLocation End = Begin.getLocWithOffset(Ownership.size());
61 return FixItHint::CreateReplacement(SourceRange(Begin, End),
62 UnsafeUnretainedText);
65static std::optional<FixItHint>
66fixItHintForVarDecl(
const VarDecl *VD,
const SourceManager &SM,
67 const LangOptions &LangOpts) {
68 assert(VD &&
"VarDecl parameter must not be null");
70 if (isa<ParmVarDecl>(VD))
76 CharSourceRange
Range = Lexer::makeFileCharRange(
77 CharSourceRange::getTokenRange(VD->getSourceRange()), SM, LangOpts);
78 if (
Range.isInvalid()) {
84 StringRef VarDeclText = Lexer::getSourceText(Range, SM, LangOpts);
85 if (std::optional<FixItHint> Hint =
86 fixItHintReplacementForOwnershipString(VarDeclText, Range, WeakText))
89 if (std::optional<FixItHint> Hint = fixItHintReplacementForOwnershipString(
90 VarDeclText, Range, StrongText))
93 return FixItHint::CreateInsertion(
Range.getBegin(),
"__unsafe_unretained ");
103 hasReceiverType(asString(
"NSInvocation *")),
104 anyOf(hasSelector(
"getArgument:atIndex:"),
105 hasSelector(
"getReturnValue:")),
108 anyOf(hasDescendant(memberExpr(isObjCManagedLifetime())),
109 hasDescendant(objcIvarRefExpr(isObjCManagedLifetime())),
116 declRefExpr(to(varDecl().bind(
"var")),
117 unless(hasParent(implicitCastExpr())),
118 isObjCManagedLifetime())))))
124 const MatchFinder::MatchResult &Result) {
125 const auto *MatchedExpr = Result.Nodes.getNodeAs<ObjCMessageExpr>(
"call");
127 auto Diag =
diag(MatchedExpr->getArg(0)->getBeginLoc(),
128 "NSInvocation %objcinstance0 should only pass pointers to "
129 "objects with ownership __unsafe_unretained")
130 << MatchedExpr->getSelector();
134 const auto *VD = Result.Nodes.getNodeAs<VarDecl>(
"var");
138 if (
auto Hint = fixItHintForVarDecl(VD, *Result.SourceManager,
139 Result.Context->getLangOpts()))
CharSourceRange Range
SourceRange for the file name.
::clang::DynTypedNode Node
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
AST_POLYMORPHIC_MATCHER(isInAbseilFile, AST_POLYMORPHIC_SUPPORTED_TYPES(Decl, Stmt, TypeLoc, NestedNameSpecifierLoc))
Matches AST nodes that were found within Abseil files.