clang-tools  14.0.0git
NSInvocationArgumentLifetimeCheck.cpp
Go to the documentation of this file.
1 //===--- NSInvocationArgumentLifetimeCheck.cpp - clang-tidy ---------===//
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 
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"
29 
30 using namespace clang::ast_matchers;
31 
32 namespace clang {
33 namespace tidy {
34 namespace objc {
35 namespace {
36 
37 static constexpr StringRef WeakText = "__weak";
38 static constexpr StringRef StrongText = "__strong";
39 static constexpr StringRef UnsafeUnretainedText = "__unsafe_unretained";
40 
41 /// Matches ObjCIvarRefExpr, DeclRefExpr, or MemberExpr that reference
42 /// Objective-C object (or block) variables or fields whose object lifetimes
43 /// are not __unsafe_unretained.
44 AST_POLYMORPHIC_MATCHER(isObjCManagedLifetime,
45  AST_POLYMORPHIC_SUPPORTED_TYPES(ObjCIvarRefExpr,
46  DeclRefExpr,
47  MemberExpr)) {
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;
53 }
54 
55 static llvm::Optional<FixItHint>
56 fixItHintReplacementForOwnershipString(StringRef Text, CharSourceRange Range,
57  StringRef Ownership) {
58  size_t Index = Text.find(Ownership);
59  if (Index == StringRef::npos)
60  return llvm::None;
61 
62  SourceLocation Begin = Range.getBegin().getLocWithOffset(Index);
63  SourceLocation End = Begin.getLocWithOffset(Ownership.size());
64  return FixItHint::CreateReplacement(SourceRange(Begin, End),
65  UnsafeUnretainedText);
66 }
67 
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");
72  // Don't provide fix-its for any parameter variables at this time.
73  if (isa<ParmVarDecl>(VD))
74  return llvm::None;
75 
76  // Currently there is no way to directly get the source range for the
77  // __weak/__strong ObjC lifetime qualifiers, so it's necessary to string
78  // search in the source code.
79  CharSourceRange Range = Lexer::makeFileCharRange(
80  CharSourceRange::getTokenRange(VD->getSourceRange()), SM, LangOpts);
81  if (Range.isInvalid()) {
82  // An invalid range likely means inside a macro, in which case don't supply
83  // a fix-it.
84  return llvm::None;
85  }
86 
87  StringRef VarDeclText = Lexer::getSourceText(Range, SM, LangOpts);
88  if (llvm::Optional<FixItHint> Hint =
89  fixItHintReplacementForOwnershipString(VarDeclText, Range, WeakText))
90  return Hint;
91 
92  if (llvm::Optional<FixItHint> Hint = fixItHintReplacementForOwnershipString(
93  VarDeclText, Range, StrongText))
94  return Hint;
95 
96  return FixItHint::CreateInsertion(Range.getBegin(), "__unsafe_unretained ");
97 }
98 
99 } // namespace
100 
101 void NSInvocationArgumentLifetimeCheck::registerMatchers(MatchFinder *Finder) {
102  Finder->addMatcher(
103  traverse(
104  TK_AsIs,
105  objcMessageExpr(
106  hasReceiverType(asString("NSInvocation *")),
107  anyOf(hasSelector("getArgument:atIndex:"),
108  hasSelector("getReturnValue:")),
109  hasArgument(
110  0,
111  anyOf(hasDescendant(memberExpr(isObjCManagedLifetime())),
112  hasDescendant(objcIvarRefExpr(isObjCManagedLifetime())),
113  hasDescendant(
114  // Reference to variables, but when dereferencing
115  // to ivars/fields a more-descendent variable
116  // reference (e.g. self) may match with strong
117  // object lifetime, leading to an incorrect match.
118  // Exclude these conditions.
119  declRefExpr(to(varDecl().bind("var")),
120  unless(hasParent(implicitCastExpr())),
121  isObjCManagedLifetime())))))
122  .bind("call")),
123  this);
124 }
125 
127  const MatchFinder::MatchResult &Result) {
128  const auto *MatchedExpr = Result.Nodes.getNodeAs<ObjCMessageExpr>("call");
129 
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();
134 
135  // Only provide fix-it hints for references to local variables; fixes for
136  // instance variable references don't have as clear an automated fix.
137  const auto *VD = Result.Nodes.getNodeAs<VarDecl>("var");
138  if (!VD)
139  return;
140 
141  if (auto Hint = fixItHintForVarDecl(VD, *Result.SourceManager,
142  Result.Context->getLangOpts()))
143  Diag << *Hint;
144 }
145 
146 } // namespace objc
147 } // namespace tidy
148 } // namespace clang
Range
CharSourceRange Range
SourceRange for the file name.
Definition: IncludeOrderCheck.cpp:38
NSInvocationArgumentLifetimeCheck.h
clang::tidy::cppcoreguidelines::getSourceText
static std::string getSourceText(const CXXDestructorDecl &Destructor)
Definition: VirtualClassDestructorCheck.cpp:112
Text
std::string Text
Definition: HTMLGenerator.cpp:80
clang::ast_matchers
Definition: AbseilMatcher.h:14
clang::clangd::check
bool check(llvm::StringRef File, llvm::function_ref< bool(const Position &)> ShouldCheckLine, const ThreadsafeFS &TFS, const ClangdLSPServer::Options &Opts, bool EnableCodeCompletion)
Definition: Check.cpp:258
Index
const SymbolIndex * Index
Definition: Dexp.cpp:99
clang::ast_matchers::AST_POLYMORPHIC_MATCHER
AST_POLYMORPHIC_MATCHER(isInAbseilFile, AST_POLYMORPHIC_SUPPORTED_TYPES(Decl, Stmt, TypeLoc, NestedNameSpecifierLoc))
Matches AST nodes that were found within Abseil files.
Definition: AbseilMatcher.h:31
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27