stephanemoore created this revision.
Herald added subscribers: cfe-commits, jfb, xazax.hun, mgorny.

Objective-C type encodings are normally pretty small but they it is
pretty easy for them to bloat to undesirable levels. Bloated Objective-C
type encodings are particularly common for Objective-C methods with
templated C++ types in their interface in Objective-C++. For example,
in Objective-C type encodings `std::string` expands to 277 bytes and
`std::map<std::string, std::string>` expands to 1219 bytes. The bloat
isn't particularly important for larger binaries but SDKs sometimes
optimize their binary size to ease adoption. This check aims to provide
some level of visibility into the size of generated Objective-C type
encodings so that developers can address them if they want.

Related article:
https://medium.com/@dmaclach/objective-c-encoding-and-you-866624cc02de

Test Notes:
Verified clang-tidy tests pass successfully.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D55640

Files:
  clang-tidy/objc/CMakeLists.txt
  clang-tidy/objc/ObjCTidyModule.cpp
  clang-tidy/objc/TypeEncodingSizeCheck.cpp
  clang-tidy/objc/TypeEncodingSizeCheck.h
  docs/ReleaseNotes.rst
  docs/clang-tidy/checks/list.rst
  docs/clang-tidy/checks/objc-type-encoding-size.rst
  test/clang-tidy/objc-type-encoding-size.m

Index: test/clang-tidy/objc-type-encoding-size.m
===================================================================
--- /dev/null
+++ test/clang-tidy/objc-type-encoding-size.m
@@ -0,0 +1,69 @@
+// RUN: %check_clang_tidy %s objc-type-encoding-size %t \
+// RUN: -config='{CheckOptions: \
+// RUN:  [{key: objc-type-encoding-size.Threshold, value: 15}]}' \
+// RUN: -- -fblocks
+
+typedef struct {
+  int a1;
+  int a2;
+  int a3;
+  int a4;
+  int a5;
+  int a6;
+  int a7;
+  int a8;
+  int a9;
+  int a10;
+  int a11;
+  int a12;
+} SixteenCharStruct;
+
+typedef struct {
+  int a1;
+} FiveCharStruct;
+
+typedef void (^BlockType)(SixteenCharStruct);
+
+@interface Foo {
+  SixteenCharStruct _someStruct;
+  // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: Objective-C type encoding for
+  // '_someStruct' exceeds 15 characters [objc-type-encoding-size]
+
+  int _anInteger;
+}
+
+@property(nonatomic) SixteenCharStruct anotherStruct;
+// CHECK-MESSAGES: :[[@LINE-1]]:40: warning: Objective-C type encoding for
+// 'anotherStruct' exceeds 15 characters [objc-type-encoding-size]
+// CHECK-MESSAGES: :[[@LINE-3]]:40: warning: Objective-C type encoding for
+// 'setAnotherStruct:' exceeds 15 characters [objc-type-encoding-size]
+
+@property(nonatomic, setter=setAsparagus:) FiveCharStruct bananas;
+// CHECK-MESSAGES: :[[@LINE-1]]:59: warning: Objective-C type encoding for
+// 'bananas' exceeds 15 characters [objc-type-encoding-size]
+
+@end
+
+@implementation Foo
+
+- (void)bar:(SixteenCharStruct)a {
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: Objective-C type encoding for 'bar:'
+// exceeds 15 characters [objc-type-encoding-size]
+}
+
+- (int)doThing {
+  int (^block)(SixteenCharStruct) = ^(SixteenCharStruct a) {
+    // CHECK-MESSAGES: :[[@LINE-1]]:37: warning: Objective-C type encoding for
+    // block expression exceeds 15 characters [objc-type-encoding-size]
+    return a.a4 + 5;
+  };
+
+  return block(_someStruct);
+}
+
+- (const char *)str {
+  // We should not flag explicit encoding statements.
+  return @encode(SixteenCharStruct);
+}
+
+@end
Index: docs/clang-tidy/checks/objc-type-encoding-size.rst
===================================================================
--- /dev/null
+++ docs/clang-tidy/checks/objc-type-encoding-size.rst
@@ -0,0 +1,13 @@
+.. title:: clang-tidy - objc-type-encoding-size
+
+objc-type-encoding-size
+=======================
+
+Finds Objective-C type encodings that exceed a configured threshold.
+
+Options
+-------
+
+.. option:: Threshold
+
+   Flag Objective-C type encodings that exceed this number of bytes.
Index: docs/clang-tidy/checks/list.rst
===================================================================
--- docs/clang-tidy/checks/list.rst
+++ docs/clang-tidy/checks/list.rst
@@ -217,6 +217,7 @@
    objc-avoid-spinlock
    objc-forbidden-subclassing
    objc-property-declaration
+   objc-type-encoding-size
    performance-faster-string-find
    performance-for-range-copy
    performance-implicit-conversion-in-loop
Index: docs/ReleaseNotes.rst
===================================================================
--- docs/ReleaseNotes.rst
+++ docs/ReleaseNotes.rst
@@ -167,6 +167,11 @@
   Detects usage of the deprecated member types of ``std::ios_base`` and replaces
   those that have a non-deprecated equivalent.
 
+- New :doc:`objc-type-encoding-size
+  <clang-tidy/checks/objc-type-encoding-size>` check.
+
+  Detects Objective-C type encodings that exceed a configured threshold.
+
 - New :doc:`readability-isolate-decl
   <clang-tidy/checks/readability-isolate-declaration>` check.
 
Index: clang-tidy/objc/TypeEncodingSizeCheck.h
===================================================================
--- /dev/null
+++ clang-tidy/objc/TypeEncodingSizeCheck.h
@@ -0,0 +1,34 @@
+//===--- TypeEncodingSizeCheck.h - clang-tidy -----------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "../ClangTidy.h"
+#include "llvm/ADT/StringRef.h"
+
+namespace clang {
+namespace tidy {
+namespace objc {
+
+/// Finds Objective-C type encodings that exceed a configured size threshold.
+///
+/// For the user-facing documentation see:
+/// http:///clang.llvm.org/extra/clang-tidy/checks/objc-type-encoding-size.html
+class TypeEncodingSizeCheck : public ClangTidyCheck {
+public:
+  TypeEncodingSizeCheck(StringRef Name, ClangTidyContext *Context);
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+  void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+
+private:
+  const unsigned Threshold;
+};
+
+} // namespace objc
+} // namespace tidy
+} // namespace clang
Index: clang-tidy/objc/TypeEncodingSizeCheck.cpp
===================================================================
--- /dev/null
+++ clang-tidy/objc/TypeEncodingSizeCheck.cpp
@@ -0,0 +1,111 @@
+//===--- TypeEncodingSizeCheck.cpp - clang-tidy ---------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "TypeEncodingSizeCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace objc {
+
+TypeEncodingSizeCheck::TypeEncodingSizeCheck(StringRef Name,
+                                             ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context),
+      Threshold(Options.get("Threshold", 2000U)) {}
+
+void TypeEncodingSizeCheck::registerMatchers(MatchFinder *Finder) {
+  // This check should only be applied to Objective-C sources.
+  if (!getLangOpts().ObjC)
+    return;
+
+  Finder->addMatcher(
+      blockExpr(unless(isExpansionInSystemHeader())).bind("block"), this);
+  Finder->addMatcher(
+      objcIvarDecl(unless(isExpansionInSystemHeader())).bind("decl"), this);
+  Finder->addMatcher(
+      objcPropertyDecl(unless(isExpansionInSystemHeader()),
+                       anyOf(hasAncestor(objcInterfaceDecl().bind("interface")),
+                             hasAncestor(objcCategoryDecl().bind("category"))))
+          .bind("decl"),
+      this);
+  Finder->addMatcher(
+      objcMethodDecl(unless(isExpansionInSystemHeader())).bind("decl"), this);
+}
+
+void TypeEncodingSizeCheck::check(const MatchFinder::MatchResult &Result) {
+  if (const auto *Block = Result.Nodes.getNodeAs<BlockExpr>("block")) {
+    std::string BlockTypeEncoding =
+        Block->getBlockDecl()->getASTContext().getObjCEncodingForBlock(Block);
+
+    if (BlockTypeEncoding.size() <= Threshold)
+      return;
+
+    diag(Block->getCaretLocation(),
+         "Objective-C type encoding for block expression exceeds %0 "
+         "characters")
+        << Threshold;
+
+    return;
+  }
+
+  const auto *EncodedDecl = Result.Nodes.getNodeAs<NamedDecl>("decl");
+
+  std::string TypeEncoding;
+  if (const auto *IvarDecl = dyn_cast<ObjCIvarDecl>(EncodedDecl)) {
+    IvarDecl->getASTContext().getObjCEncodingForType(IvarDecl->getType(),
+                                                     TypeEncoding);
+  } else if (const auto *PropertyDecl =
+                 dyn_cast<ObjCPropertyDecl>(EncodedDecl)) {
+    const Decl *ContainerDecl = nullptr;
+
+    // Properties in interfaces and categories need respective implementations
+    // to determine the type encoding.
+    if (const auto *ClassDecl =
+            Result.Nodes.getNodeAs<ObjCInterfaceDecl>("interface")) {
+      ContainerDecl = ClassDecl->getImplementation();
+
+      // Bail out if we cannot find the implementation because this likely
+      // indicates a finding that cannot be acted upon.
+      if (ContainerDecl == nullptr)
+        return;
+    } else if (const auto *CategoryDecl =
+                   Result.Nodes.getNodeAs<ObjCCategoryDecl>("category")) {
+      ContainerDecl = CategoryDecl->getImplementation();
+
+      // Bail out if we cannot find the implementation because this likely
+      // indicates a finding that cannot be acted upon.
+      if (ContainerDecl == nullptr)
+        return;
+    }
+
+    TypeEncoding = PropertyDecl->getASTContext().getObjCEncodingForPropertyDecl(
+        PropertyDecl, ContainerDecl);
+  } else if (const auto *MethodDecl = dyn_cast<ObjCMethodDecl>(EncodedDecl)) {
+    TypeEncoding =
+        MethodDecl->getASTContext().getObjCEncodingForMethodDecl(MethodDecl);
+  }
+
+  if (TypeEncoding.size() <= Threshold)
+    return;
+
+  diag(EncodedDecl->getLocation(),
+       "Objective-C type encoding for %0 exceeds %1 characters")
+      << EncodedDecl << Threshold;
+}
+
+void TypeEncodingSizeCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "Threshold", Threshold);
+}
+
+} // namespace objc
+} // namespace tidy
+} // namespace clang
Index: clang-tidy/objc/ObjCTidyModule.cpp
===================================================================
--- clang-tidy/objc/ObjCTidyModule.cpp
+++ clang-tidy/objc/ObjCTidyModule.cpp
@@ -14,6 +14,7 @@
 #include "AvoidSpinlockCheck.h"
 #include "ForbiddenSubclassingCheck.h"
 #include "PropertyDeclarationCheck.h"
+#include "TypeEncodingSizeCheck.h"
 
 using namespace clang::ast_matchers;
 
@@ -32,6 +33,8 @@
         "objc-forbidden-subclassing");
     CheckFactories.registerCheck<PropertyDeclarationCheck>(
         "objc-property-declaration");
+    CheckFactories.registerCheck<TypeEncodingSizeCheck>(
+        "objc-type-encoding-size");
   }
 };
 
Index: clang-tidy/objc/CMakeLists.txt
===================================================================
--- clang-tidy/objc/CMakeLists.txt
+++ clang-tidy/objc/CMakeLists.txt
@@ -6,6 +6,7 @@
   ForbiddenSubclassingCheck.cpp
   ObjCTidyModule.cpp
   PropertyDeclarationCheck.cpp
+  TypeEncodingSizeCheck.cpp
 
   LINK_LIBS
   clangAST
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to