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 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( "objc-property-declaration"); + CheckFactories.registerCheck( + "objc-type-encoding-size"); } }; 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("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("decl"); + + std::string TypeEncoding; + if (const auto *IvarDecl = dyn_cast(EncodedDecl)) { + IvarDecl->getASTContext().getObjCEncodingForType(IvarDecl->getType(), + TypeEncoding); + } else if (const auto *PropertyDecl = + dyn_cast(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("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("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(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: docs/ReleaseNotes.rst =================================================================== --- docs/ReleaseNotes.rst +++ docs/ReleaseNotes.rst @@ -174,6 +174,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 + ` check. + + Detects Objective-C type encodings that exceed a configured threshold. + - New :doc:`readability-isolate-decl ` check. Index: docs/clang-tidy/checks/list.rst =================================================================== --- docs/clang-tidy/checks/list.rst +++ docs/clang-tidy/checks/list.rst @@ -218,6 +218,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/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 +======================= + +Detects Objective-C type encodings that exceed a configured threshold. + +Options +------- + +.. option:: Threshold + + Flag Objective-C type encodings that exceed this number of bytes. 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