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