1 //===--- FunctionSize.cpp - clang-tidy ------------------------------------===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 10 #include "FunctionSizeCheck.h" 11 #include "clang/ASTMatchers/ASTMatchFinder.h" 12 13 using namespace clang::ast_matchers; 14 15 namespace clang { 16 namespace tidy { 17 namespace readability { 18 19 FunctionSizeCheck::FunctionSizeCheck(StringRef Name, ClangTidyContext *Context) 20 : ClangTidyCheck(Name, Context), 21 LineThreshold(Options.get("LineThreshold", -1U)), 22 StatementThreshold(Options.get("StatementThreshold", 800U)), 23 BranchThreshold(Options.get("BranchThreshold", -1U)) {} 24 25 void FunctionSizeCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { 26 Options.store(Opts, "LineThreshold", LineThreshold); 27 Options.store(Opts, "StatementThreshold", StatementThreshold); 28 Options.store(Opts, "BranchThreshold", BranchThreshold); 29 } 30 31 void FunctionSizeCheck::registerMatchers(MatchFinder *Finder) { 32 Finder->addMatcher( 33 functionDecl( 34 unless(isInstantiated()), 35 forEachDescendant( 36 stmt(unless(compoundStmt()), 37 hasParent(stmt(anyOf(compoundStmt(), ifStmt(), 38 anyOf(whileStmt(), doStmt(), 39 cxxForRangeStmt(), forStmt()))))) 40 .bind("stmt"))).bind("func"), 41 this); 42 } 43 44 void FunctionSizeCheck::check(const MatchFinder::MatchResult &Result) { 45 const auto *Func = Result.Nodes.getNodeAs<FunctionDecl>("func"); 46 47 FunctionInfo &FI = FunctionInfos[Func]; 48 49 // Count the lines including whitespace and comments. Really simple. 50 if (!FI.Lines) { 51 if (const Stmt *Body = Func->getBody()) { 52 SourceManager *SM = Result.SourceManager; 53 if (SM->isWrittenInSameFile(Body->getLocStart(), Body->getLocEnd())) { 54 FI.Lines = SM->getSpellingLineNumber(Body->getLocEnd()) - 55 SM->getSpellingLineNumber(Body->getLocStart()); 56 } 57 } 58 } 59 60 const auto *Statement = Result.Nodes.getNodeAs<Stmt>("stmt"); 61 ++FI.Statements; 62 63 // TODO: switch cases, gotos 64 if (isa<IfStmt>(Statement) || isa<WhileStmt>(Statement) || 65 isa<ForStmt>(Statement) || isa<SwitchStmt>(Statement) || 66 isa<DoStmt>(Statement) || isa<CXXForRangeStmt>(Statement)) 67 ++FI.Branches; 68 } 69 70 void FunctionSizeCheck::onEndOfTranslationUnit() { 71 // If we're above the limit emit a warning. 72 for (const auto &P : FunctionInfos) { 73 const FunctionInfo &FI = P.second; 74 if (FI.Lines > LineThreshold || FI.Statements > StatementThreshold || 75 FI.Branches > BranchThreshold) { 76 diag(P.first->getLocation(), 77 "function '%0' exceeds recommended size/complexity thresholds") 78 << P.first->getNameAsString(); 79 } 80 81 if (FI.Lines > LineThreshold) { 82 diag(P.first->getLocation(), 83 "%0 lines including whitespace and comments (threshold %1)", 84 DiagnosticIDs::Note) 85 << FI.Lines << LineThreshold; 86 } 87 88 if (FI.Statements > StatementThreshold) { 89 diag(P.first->getLocation(), "%0 statements (threshold %1)", 90 DiagnosticIDs::Note) 91 << FI.Statements << StatementThreshold; 92 } 93 94 if (FI.Branches > BranchThreshold) { 95 diag(P.first->getLocation(), "%0 branches (threshold %1)", 96 DiagnosticIDs::Note) 97 << FI.Branches << BranchThreshold; 98 } 99 } 100 101 FunctionInfos.clear(); 102 } 103 104 } // namespace readability 105 } // namespace tidy 106 } // namespace clang 107