aboutsummaryrefslogtreecommitdiff
path: root/clang-tools-extra/clang-tidy
diff options
context:
space:
mode:
authorMichael Wyman <michael@mwyman.com>2020-04-05 00:28:34 -0700
committerMichael Wyman <michael@mwyman.com>2020-04-10 08:51:21 -0700
commit89f1321fe4ef203a4674213280b430a274dc2001 (patch)
treeae506c8975d8bf93c05602674e75af4b8cbfe6ef /clang-tools-extra/clang-tidy
parent65b8b643b4ba75a2712ddc210aeaa7c6527f3bb4 (diff)
downloadllvm-89f1321fe4ef203a4674213280b430a274dc2001.zip
llvm-89f1321fe4ef203a4674213280b430a274dc2001.tar.gz
llvm-89f1321fe4ef203a4674213280b430a274dc2001.tar.bz2
[clang-tidy] Add check to find calls to NSInvocation methods under ARC that don't have proper object argument lifetimes.
Summary: This check is similar to an ARC Migration check that warned about this incorrect usage under ARC, but most projects are no longer undergoing migration from pre-ARC code. The documentation for NSInvocation is not explicit about these requirements and incorrect usage has been found in many of our projects. Reviewers: stephanemoore, benhamilton, dmaclach, alexfh, aaron.ballman, hokein, njames93 Reviewed By: stephanemoore, benhamilton, aaron.ballman Subscribers: xazax.hun, Eugene.Zelenko, mgorny, cfe-commits Tags: #clang, #clang-tools-extra Differential Revision: https://reviews.llvm.org/D77571
Diffstat (limited to 'clang-tools-extra/clang-tidy')
-rw-r--r--clang-tools-extra/clang-tidy/objc/CMakeLists.txt1
-rw-r--r--clang-tools-extra/clang-tidy/objc/NSInvocationArgumentLifetimeCheck.cpp146
-rw-r--r--clang-tools-extra/clang-tidy/objc/NSInvocationArgumentLifetimeCheck.h39
-rw-r--r--clang-tools-extra/clang-tidy/objc/ObjCTidyModule.cpp3
4 files changed, 189 insertions, 0 deletions
diff --git a/clang-tools-extra/clang-tidy/objc/CMakeLists.txt b/clang-tools-extra/clang-tidy/objc/CMakeLists.txt
index 9b010f0..5c6c505 100644
--- a/clang-tools-extra/clang-tidy/objc/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/objc/CMakeLists.txt
@@ -8,6 +8,7 @@ add_clang_library(clangTidyObjCModule
DeallocInCategoryCheck.cpp
ForbiddenSubclassingCheck.cpp
MissingHashCheck.cpp
+ NSInvocationArgumentLifetimeCheck.cpp
ObjCTidyModule.cpp
PropertyDeclarationCheck.cpp
SuperSelfCheck.cpp
diff --git a/clang-tools-extra/clang-tidy/objc/NSInvocationArgumentLifetimeCheck.cpp b/clang-tools-extra/clang-tidy/objc/NSInvocationArgumentLifetimeCheck.cpp
new file mode 100644
index 0000000..520bdc3
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/objc/NSInvocationArgumentLifetimeCheck.cpp
@@ -0,0 +1,146 @@
+//===--- NSInvocationArgumentLifetimeCheck.cpp - clang-tidy ---------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "NSInvocationArgumentLifetimeCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Attrs.inc"
+#include "clang/AST/ComputeDependence.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/ExprObjC.h"
+#include "clang/AST/Type.h"
+#include "clang/AST/TypeLoc.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/ASTMatchers/ASTMatchersMacros.h"
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/LLVM.h"
+#include "clang/Basic/LangOptions.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Basic/SourceManager.h"
+#include "llvm/ADT/None.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace objc {
+namespace {
+
+static constexpr StringRef WeakText = "__weak";
+static constexpr StringRef StrongText = "__strong";
+static constexpr StringRef UnsafeUnretainedText = "__unsafe_unretained";
+
+/// Matches ObjCIvarRefExpr, DeclRefExpr, or MemberExpr that reference
+/// Objective-C object (or block) variables or fields whose object lifetimes
+/// are not __unsafe_unretained.
+AST_POLYMORPHIC_MATCHER(isObjCManagedLifetime,
+ AST_POLYMORPHIC_SUPPORTED_TYPES(ObjCIvarRefExpr,
+ DeclRefExpr,
+ MemberExpr)) {
+ QualType QT = Node.getType();
+ return QT->isScalarType() &&
+ (QT->getScalarTypeKind() == Type::STK_ObjCObjectPointer ||
+ QT->getScalarTypeKind() == Type::STK_BlockPointer) &&
+ QT.getQualifiers().getObjCLifetime() > Qualifiers::OCL_ExplicitNone;
+}
+
+static llvm::Optional<FixItHint>
+fixItHintReplacementForOwnershipString(StringRef Text, CharSourceRange Range,
+ StringRef Ownership) {
+ size_t Index = Text.find(Ownership);
+ if (Index == StringRef::npos)
+ return llvm::None;
+
+ SourceLocation Begin = Range.getBegin().getLocWithOffset(Index);
+ SourceLocation End = Begin.getLocWithOffset(Ownership.size());
+ return FixItHint::CreateReplacement(SourceRange(Begin, End),
+ UnsafeUnretainedText);
+}
+
+static llvm::Optional<FixItHint>
+fixItHintForVarDecl(const VarDecl *VD, const SourceManager &SM,
+ const LangOptions &LangOpts) {
+ assert(VD && "VarDecl parameter must not be null");
+ // Don't provide fix-its for any parameter variables at this time.
+ if (isa<ParmVarDecl>(VD))
+ return llvm::None;
+
+ // Currently there is no way to directly get the source range for the
+ // __weak/__strong ObjC lifetime qualifiers, so it's necessary to string
+ // search in the source code.
+ CharSourceRange Range = Lexer::makeFileCharRange(
+ CharSourceRange::getTokenRange(VD->getSourceRange()), SM, LangOpts);
+ if (Range.isInvalid()) {
+ // An invalid range likely means inside a macro, in which case don't supply
+ // a fix-it.
+ return llvm::None;
+ }
+
+ StringRef VarDeclText = Lexer::getSourceText(Range, SM, LangOpts);
+ if (llvm::Optional<FixItHint> Hint =
+ fixItHintReplacementForOwnershipString(VarDeclText, Range, WeakText))
+ return Hint;
+
+ if (llvm::Optional<FixItHint> Hint = fixItHintReplacementForOwnershipString(
+ VarDeclText, Range, StrongText))
+ return Hint;
+
+ return FixItHint::CreateInsertion(Range.getBegin(), "__unsafe_unretained ");
+}
+
+} // namespace
+
+void NSInvocationArgumentLifetimeCheck::registerMatchers(MatchFinder *Finder) {
+ Finder->addMatcher(
+ objcMessageExpr(
+ hasReceiverType(asString("NSInvocation *")),
+ anyOf(hasSelector("getArgument:atIndex:"),
+ hasSelector("getReturnValue:")),
+ hasArgument(
+ 0, anyOf(hasDescendant(memberExpr(isObjCManagedLifetime())),
+ hasDescendant(objcIvarRefExpr(isObjCManagedLifetime())),
+ hasDescendant(
+ // Reference to variables, but when dereferencing
+ // to ivars/fields a more-descendent variable
+ // reference (e.g. self) may match with strong
+ // object lifetime, leading to an incorrect match.
+ // Exclude these conditions.
+ declRefExpr(to(varDecl().bind("var")),
+ unless(hasParent(implicitCastExpr())),
+ isObjCManagedLifetime())))))
+ .bind("call"),
+ this);
+}
+
+void NSInvocationArgumentLifetimeCheck::check(
+ const MatchFinder::MatchResult &Result) {
+ const auto *MatchedExpr = Result.Nodes.getNodeAs<ObjCMessageExpr>("call");
+
+ auto Diag = diag(MatchedExpr->getArg(0)->getBeginLoc(),
+ "NSInvocation %objcinstance0 should only pass pointers to "
+ "objects with ownership __unsafe_unretained")
+ << MatchedExpr->getSelector();
+
+ // Only provide fix-it hints for references to local variables; fixes for
+ // instance variable references don't have as clear an automated fix.
+ const auto *VD = Result.Nodes.getNodeAs<VarDecl>("var");
+ if (!VD)
+ return;
+
+ if (auto Hint = fixItHintForVarDecl(VD, *Result.SourceManager,
+ Result.Context->getLangOpts()))
+ Diag << *Hint;
+}
+
+} // namespace objc
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tools-extra/clang-tidy/objc/NSInvocationArgumentLifetimeCheck.h b/clang-tools-extra/clang-tidy/objc/NSInvocationArgumentLifetimeCheck.h
new file mode 100644
index 0000000..6d0c30c
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/objc/NSInvocationArgumentLifetimeCheck.h
@@ -0,0 +1,39 @@
+//===--- NSInvocationArgumentLifetimeCheck.h - clang-tidy -------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_OBJC_NSINVOCATIONARGUMENTLIFETIMECHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_OBJC_NSINVOCATIONARGUMENTLIFETIMECHECK_H
+
+#include "../ClangTidyCheck.h"
+#include "clang/Basic/LangStandard.h"
+
+namespace clang {
+namespace tidy {
+namespace objc {
+
+/// Finds calls to NSInvocation methods under ARC that don't have proper
+/// argument object lifetimes.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/objc-nsinvocation-argument-lifetime.html
+class NSInvocationArgumentLifetimeCheck : public ClangTidyCheck {
+public:
+ NSInvocationArgumentLifetimeCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+ return LangOpts.ObjC && LangOpts.ObjCAutoRefCount;
+ }
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace objc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_OBJC_NSINVOCATIONARGUMENTLIFETIMECHECK_H
diff --git a/clang-tools-extra/clang-tidy/objc/ObjCTidyModule.cpp b/clang-tools-extra/clang-tidy/objc/ObjCTidyModule.cpp
index ff220b8..56ab0ab 100644
--- a/clang-tools-extra/clang-tidy/objc/ObjCTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/objc/ObjCTidyModule.cpp
@@ -13,6 +13,7 @@
#include "DeallocInCategoryCheck.h"
#include "ForbiddenSubclassingCheck.h"
#include "MissingHashCheck.h"
+#include "NSInvocationArgumentLifetimeCheck.h"
#include "PropertyDeclarationCheck.h"
#include "SuperSelfCheck.h"
@@ -33,6 +34,8 @@ public:
"objc-forbidden-subclassing");
CheckFactories.registerCheck<MissingHashCheck>(
"objc-missing-hash");
+ CheckFactories.registerCheck<NSInvocationArgumentLifetimeCheck>(
+ "objc-nsinvocation-argument-lifetime");
CheckFactories.registerCheck<PropertyDeclarationCheck>(
"objc-property-declaration");
CheckFactories.registerCheck<SuperSelfCheck>(