brintos

brintos / llvm-project-archived public Read only

0
0
Text · 5.7 KiB · 69caaed Raw
145 lines · cpp
1//===----------------------------------------------------------------------===//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-exception6//7//===----------------------------------------------------------------------===//8 9#include "NSInvocationArgumentLifetimeCheck.h"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"27#include <optional>28 29using namespace clang::ast_matchers;30 31namespace clang::tidy::objc {32 33static constexpr StringRef WeakText = "__weak";34static constexpr StringRef StrongText = "__strong";35static constexpr StringRef UnsafeUnretainedText = "__unsafe_unretained";36 37namespace {38 39/// Matches ObjCIvarRefExpr, DeclRefExpr, or MemberExpr that reference40/// Objective-C object (or block) variables or fields whose object lifetimes41/// are not __unsafe_unretained.42AST_POLYMORPHIC_MATCHER(isObjCManagedLifetime,43                        AST_POLYMORPHIC_SUPPORTED_TYPES(ObjCIvarRefExpr,44                                                        DeclRefExpr,45                                                        MemberExpr)) {46  const QualType QT = Node.getType();47  return QT->isScalarType() &&48         (QT->getScalarTypeKind() == Type::STK_ObjCObjectPointer ||49          QT->getScalarTypeKind() == Type::STK_BlockPointer) &&50         QT.getQualifiers().getObjCLifetime() > Qualifiers::OCL_ExplicitNone;51}52 53} // namespace54 55static std::optional<FixItHint>56fixItHintReplacementForOwnershipString(StringRef Text, CharSourceRange Range,57                                       StringRef Ownership) {58  const size_t Index = Text.find(Ownership);59  if (Index == StringRef::npos)60    return std::nullopt;61 62  const SourceLocation Begin = Range.getBegin().getLocWithOffset(Index);63  const SourceLocation End = Begin.getLocWithOffset(Ownership.size());64  return FixItHint::CreateReplacement(SourceRange(Begin, End),65                                      UnsafeUnretainedText);66}67 68static std::optional<FixItHint>69fixItHintForVarDecl(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 std::nullopt;75 76  // Currently there is no way to directly get the source range for the77  // __weak/__strong ObjC lifetime qualifiers, so it's necessary to string78  // search in the source code.79  const 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 supply83    // a fix-it.84    return std::nullopt;85  }86 87  const StringRef VarDeclText = Lexer::getSourceText(Range, SM, LangOpts);88  if (std::optional<FixItHint> Hint =89          fixItHintReplacementForOwnershipString(VarDeclText, Range, WeakText))90    return Hint;91 92  if (std::optional<FixItHint> Hint = fixItHintReplacementForOwnershipString(93          VarDeclText, Range, StrongText))94    return Hint;95 96  return FixItHint::CreateInsertion(Range.getBegin(), "__unsafe_unretained ");97}98 99void NSInvocationArgumentLifetimeCheck::registerMatchers(MatchFinder *Finder) {100  Finder->addMatcher(101      traverse(102          TK_AsIs,103          objcMessageExpr(104              hasReceiverType(asString("NSInvocation *")),105              anyOf(hasSelector("getArgument:atIndex:"),106                    hasSelector("getReturnValue:")),107              hasArgument(108                  0,109                  anyOf(hasDescendant(memberExpr(isObjCManagedLifetime())),110                        hasDescendant(objcIvarRefExpr(isObjCManagedLifetime())),111                        hasDescendant(112                            // Reference to variables, but when dereferencing113                            // to ivars/fields a more-descendent variable114                            // reference (e.g. self) may match with strong115                            // object lifetime, leading to an incorrect match.116                            // Exclude these conditions.117                            declRefExpr(to(varDecl().bind("var")),118                                        unless(hasParent(implicitCastExpr())),119                                        isObjCManagedLifetime())))))120              .bind("call")),121      this);122}123 124void NSInvocationArgumentLifetimeCheck::check(125    const MatchFinder::MatchResult &Result) {126  const auto *MatchedExpr = Result.Nodes.getNodeAs<ObjCMessageExpr>("call");127 128  auto Diag = diag(MatchedExpr->getArg(0)->getBeginLoc(),129                   "NSInvocation %objcinstance0 should only pass pointers to "130                   "objects with ownership __unsafe_unretained")131              << MatchedExpr->getSelector();132 133  // Only provide fix-it hints for references to local variables; fixes for134  // instance variable references don't have as clear an automated fix.135  const auto *VD = Result.Nodes.getNodeAs<VarDecl>("var");136  if (!VD)137    return;138 139  if (auto Hint = fixItHintForVarDecl(VD, *Result.SourceManager,140                                      Result.Context->getLangOpts()))141    Diag << *Hint;142}143 144} // namespace clang::tidy::objc145