Index: clang-tidy/misc/AssertionCountCheck.h =================================================================== --- /dev/null +++ clang-tidy/misc/AssertionCountCheck.h @@ -0,0 +1,60 @@ +//===--- AssertionCountCheck.h - clang-tidy----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_ASSERTION_COUNT_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_ASSERTION_COUNT_H + +#include "../ClangTidy.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include +#include + +namespace clang { +namespace tidy { +namespace misc { + +/// Allows to impose limits on the lines-per-assertions ratio for functions. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/misc-assertion-count.html +class AssertionCountCheck : public ClangTidyCheck { +public: + AssertionCountCheck(StringRef Name, ClangTidyContext *Context); + + void storeOptions(ClangTidyOptions::OptionMap &Opts) override; + + // with certain config parameters, this chech will never output a warning. + // this function allows to skip this check entirely in such a case + bool isNOP() const; + + void registerPPCallbacks(CompilerInstance &Compiler) override; + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + + // given this much lines, what is the minimal number of assertions to have? + unsigned expectedAssertionsMin(unsigned Lines) const; + + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + +private: + const unsigned LineThreshold; + const unsigned AssertsThreshold; + const unsigned LinesStep; + const unsigned AssertsStep; + const std::string RawAssertList; + SmallVector AssertNames; + + std::vector FoundAssertions; +}; + +} // namespace misc +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_ASSERTION_COUNT_H Index: clang-tidy/misc/AssertionCountCheck.cpp =================================================================== --- /dev/null +++ clang-tidy/misc/AssertionCountCheck.cpp @@ -0,0 +1,418 @@ +//===--- AssertionCountCheck.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 "AssertionCountCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Lex/PPCallbacks.h" +#include "clang/Lex/Preprocessor.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace misc { +namespace { + +// checks whether second SourceRange is fully within the first SourceRange +// this does check both the line numbers and the columns +// FIXME: this function appears to be rather convoluted... +bool SourceRangeContains(const SourceManager &SM, const SourceRange &outer, + const SourceRange &inner) { + assert(SM.isWrittenInSameFile(outer.getBegin(), outer.getEnd()) && + SM.isWrittenInSameFile(inner.getBegin(), inner.getEnd()) && + SM.isWrittenInSameFile(outer.getBegin(), inner.getEnd()) && + "don't know how this could work for more than one file"); + + assert((SM.getSpellingLineNumber(outer.getBegin()) <= + SM.getSpellingLineNumber(outer.getEnd())) && + (SM.getSpellingLineNumber(inner.getBegin()) <= + SM.getSpellingLineNumber(inner.getEnd())) && + "why are the ranges all messed up?"); + + // the thought is that if the "inner" block occupies subset of the lines of + // "outer" block, then the inner block is indeed lies within the outer block. + + // handle lines first. much easier + + // if the "inner" block starts first, then it is not inner. + if (SM.getSpellingLineNumber(outer.getBegin()) > + SM.getSpellingLineNumber(inner.getBegin())) + return false; + + // if the "outer" block ends first, then it is not outer. + if (SM.getSpellingLineNumber(outer.getEnd()) < + SM.getSpellingLineNumber(inner.getEnd())) + return false; + + // at this point we know that the outer block begins not after the inner ends + // and that the outer block ends not before the inner block stars + assert((SM.getSpellingLineNumber(outer.getBegin()) <= + SM.getSpellingLineNumber(inner.getEnd())) && + (SM.getSpellingLineNumber(outer.getEnd()) >= + SM.getSpellingLineNumber(inner.getBegin()))); + + // okay, as far as linenumbers are concerned, blocks are ok. + + // if the blocks are properly multi-line then we are all set + // i.e. outer and inner blocks start on different lines and + // outer and inner blocks end on different lines + if ((SM.getSpellingLineNumber(outer.getBegin()) < + SM.getSpellingLineNumber(inner.getBegin())) && + (SM.getSpellingLineNumber(outer.getEnd()) > + SM.getSpellingLineNumber(inner.getEnd()))) { + // and the outer block starts on different line than inner ends + assert(SM.getSpellingLineNumber(outer.getBegin()) < + SM.getSpellingLineNumber(inner.getEnd())); + return true; + } + + // blocks share line[s] where they start and/or end + // will have to check columns + + assert((SM.getSpellingLineNumber(outer.getBegin()) == + SM.getSpellingLineNumber(inner.getBegin())) || + (SM.getSpellingLineNumber(outer.getEnd()) == + SM.getSpellingLineNumber(inner.getEnd()))); + + // if both the blocks begin at the same line, check that "outer" block + // is started earlier in the line + if ((SM.getSpellingLineNumber(outer.getBegin()) == + SM.getSpellingLineNumber(inner.getBegin())) && + (SM.getSpellingColumnNumber(outer.getBegin()) > + SM.getSpellingColumnNumber(inner.getBegin()))) + return false; + + // if both the blocks end at the same line, check that "outer" block + // ends later in the line + if ((SM.getSpellingLineNumber(outer.getEnd()) == + SM.getSpellingLineNumber(inner.getEnd())) && + (SM.getSpellingColumnNumber(outer.getEnd()) < + SM.getSpellingColumnNumber(inner.getEnd()))) + return false; + + // looks ok. + return true; +} + +// post-processes the results of the first part, AssertionMacrosCallbacks. +unsigned CountMacroAssertions(const std::vector &FoundAssertions, + const SourceManager *SM, const Stmt *Body) { + unsigned Count = 0; + + // let's count how many of the found macro instantiations lie in this function + for (const auto &MacroExpansion : FoundAssertions) { + if (!SM->isWrittenInSameFile(MacroExpansion.getBegin(), + MacroExpansion.getEnd())) + continue; + + if (!SM->isWrittenInSameFile(Body->getLocStart(), + MacroExpansion.getBegin())) + continue; + + // is this assertion in this function? + if (!SourceRangeContains(*SM, Body->getSourceRange(), MacroExpansion)) + continue; + + // got valid assertion + ++Count; + } + + return Count; +} + +// part One of the assertion detection. +// a callback that will be triggered each time a macro is expanded, +// and it will record the expansion position (range). it is important to record +// the positions (as in, not just the count), because at this stage, it is +// impossible to know in which function declaration the expansion happens. +class AssertionMacrosCallbacks : public PPCallbacks { + const SmallVector &AssertNames; + std::vector *FoundAssertions; + +public: + AssertionMacrosCallbacks(const SmallVector &Names, + std::vector *Found) + : AssertNames(Names), FoundAssertions(Found) { + assert(FoundAssertions); + } + + void MacroExpands(const Token &MacroNameTok, const MacroDefinition &MD, + SourceRange Range, const MacroArgs *Args) override { + const auto *II = MacroNameTok.getIdentifierInfo(); + + if (!II) + return; + + // if given macro [expansion] matches one of the configured assert names, + // record this position. the check itself will later sieve all the matches, + // and find (count) the ones in the function in question. + if (std::find(AssertNames.begin(), AssertNames.end(), II->getName()) != + AssertNames.end()) + FoundAssertions->emplace_back(Range); + } +}; + +struct FunctionInfo { + unsigned Lines = 0; + unsigned Assertions = 0; +}; + +// part Two of the assertion detection. +// after the results of the first part (AssertionMacrosCallbacks) is processed +// (via CountMacroAssertions), this can be called to walk through the whole AST +// of a given function declaration, and count function-like assertions. +class FunctionASTVisitor : public RecursiveASTVisitor { + using Base = RecursiveASTVisitor; + + const SmallVector &AssertNames; + const std::vector &FoundAssertions; + const SourceManager *SM = nullptr; + const LangOptions LangOpts; + FunctionInfo *FI = nullptr; + + void HandleStmt(const Stmt *Node) { + // let's check if this found interesting node was *NOT* + // introduced as expansion of one of the macros that we care about. + + SourceLocation Loc = Node->getLocStart(); + while (Loc.isValid() && Loc.isMacroID()) { + StringRef MacroName = Lexer::getImmediateMacroName(Loc, *SM, LangOpts); + + // Check if this macro is an assert that we care about. + if (std::find(AssertNames.begin(), AssertNames.end(), MacroName) != + AssertNames.end()) { + return; + } + Loc = SM->getImmediateMacroCallerLoc(Loc); + } + + // this call was not expanded from one of the 'cared' macros, count it. + ++FI->Assertions; + } + + void HandleFunctionCall(Stmt *S, const CallExpr *CExpr) { + const auto *FuncDecl = CExpr->getDirectCallee(); + + if (!FuncDecl) + return; + + if (!FuncDecl->getDeclName().isIdentifier()) + return; + + if (std::find(AssertNames.begin(), AssertNames.end(), + FuncDecl->getName()) == AssertNames.end()) + return; + + // cool. found a call to a function with one of the configured names + HandleStmt(S); + } + + void HandleLambdaCall(Stmt *S, const CXXOperatorCallExpr *OCall) { + // FIXME + } + +public: + FunctionASTVisitor(const SmallVector &Names, + const std::vector &MacroAssertions, + ASTContext *Context, FunctionInfo *Info) + : AssertNames(Names), FoundAssertions(MacroAssertions), + SM(&(Context->getSourceManager())), LangOpts(Context->getLangOpts()), + FI(Info) {} + + // do not traverse into any declarations. + bool TraverseDeclStmt(DeclStmt *DS) { + if (!(DS->isSingleDecl() && isa(DS->getSingleDecl()))) { + // all the assertions located within declarations do NOT contribute + // to the func! so subtract them. + + FI->Assertions -= CountMacroAssertions(FoundAssertions, SM, DS); + + // do not traverse into any declarations. + return true; + } + + // found a static_assert(). + HandleStmt(DS); + + return true; + } + + bool VisitStmt(Stmt *S) { + if (const auto *OCall = dyn_cast(S)) + HandleLambdaCall(S, OCall); + else if (const auto *CExpr = dyn_cast(S)) + HandleFunctionCall(S, CExpr); + + return true; + } +}; + +} // anonymous namespace + +AssertionCountCheck::AssertionCountCheck(StringRef Name, + ClangTidyContext *Context) + : ClangTidyCheck(Name, Context), + LineThreshold(Options.get("LineThreshold", -1U)), + AssertsThreshold(Options.get("AssertsThreshold", 0U)), + LinesStep(Options.get("LinesStep", 0U)), + AssertsStep(Options.get("AssertsStep", 0U)), + RawAssertList(Options.get("AssertNames", "assert")) { + StringRef(RawAssertList).split(AssertNames, ",", -1, false); + // NOTE: default parameters should NOT produce any warnings. isNOP() == true +} + +void AssertionCountCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { + Options.store(Opts, "LineThreshold", LineThreshold); + Options.store(Opts, "AssertsThreshold", AssertsThreshold); + Options.store(Opts, "LinesStep", LinesStep); + Options.store(Opts, "AssertsStep", AssertsStep); + Options.store(Opts, "AssertNames", RawAssertList); +} + +bool AssertionCountCheck::isNOP() const { + // is there any configured assertion limit? + // if no limits are imposed, then no function can violate them. + + // see unittest for the logic behind these checks. + + // if there is no base limit for the asserts count, and no either/both of the + // steps according to which the assertion count expectation would increase, + // then the expected minimal assertion count is always zero. + + return AssertsThreshold == 0 && (LinesStep == 0 || AssertsStep == 0); +} + +void AssertionCountCheck::registerPPCallbacks(CompilerInstance &Compiler) { + // if the check will never complain, then there is no point in bothering. + if (isNOP()) + return; + + Compiler.getPreprocessor().addPPCallbacks( + llvm::make_unique(this->AssertNames, + &(this->FoundAssertions))); +} + +void AssertionCountCheck::registerMatchers(MatchFinder *Finder) { + // if the check will never complain, then there is no point in bothering. + if (isNOP()) + return; + + Finder->addMatcher(functionDecl(unless(isImplicit())).bind("func"), this); +} + +unsigned AssertionCountCheck::expectedAssertionsMin(unsigned Lines) const { + // can NOT check isNOP() here, unittest uses this function! + + // if the function is shorter than the threshold, impose no limits + if (Lines < LineThreshold) + return 0U; + + assert(Lines >= LineThreshold); + + // if the function is no less than threshold lines, it must have at least + // this much assertions. + unsigned AssertionsMin = AssertsThreshold; + + // if the function has exactly the threshold lines, nothing more to do + if (Lines == LineThreshold) + return AssertionsMin; + + // if there are no scales, all functions above threshold should contain + // no less than that ^ much assertions, regardless of their linelength + // NOTE: and if AssertsThreshold is zero, then isNOP() is true + if (LinesStep == 0 || AssertsStep == 0) + return AssertionsMin; + + assert(LinesStep != 0); + assert(AssertsStep != 0); + + // how many more lines does the function have, above the threshold? + Lines -= LineThreshold; + assert(Lines > 0); + + // how many multiples of the LinesStep is that? + // NOTE: this is floor division! any fractional part is ignored! + const unsigned LinesMultiple = Lines / LinesStep; + assert(LinesStep * LinesMultiple <= Lines); + + // are there any multiples? + if (LinesMultiple == 0) + return AssertionsMin; + + // additionally increase expected assertion count + assert(AssertsStep * LinesMultiple > 0); + AssertionsMin += AssertsStep * LinesMultiple; + + return AssertionsMin; +} + +void AssertionCountCheck::check(const MatchFinder::MatchResult &Result) { + assert(!isNOP() && "if config params are NOP, then we should not be here"); + + const auto *Func = Result.Nodes.getNodeAs("func"); + assert(Func); + + FunctionInfo FI; + + const SourceManager *SM = Result.SourceManager; + const Stmt *Body = Func->getBody(); + + if (!Body) + return; + + if (!SM->isWrittenInSameFile(Body->getLocStart(), Body->getLocEnd())) + return; + + // Count the lines including whitespace and comments. Really simple. + FI.Lines = SM->getSpellingLineNumber(Body->getLocEnd()) - + SM->getSpellingLineNumber(Body->getLocStart()); + + // if the function has less lines than threshold, no point in checking + if (FI.Lines < LineThreshold) + return; + + assert(FI.Lines >= LineThreshold); + assert(expectedAssertionsMin(FI.Lines) > 0); + + // out of all the macro expansions, count only the ones in the current func. + FI.Assertions = CountMacroAssertions(FoundAssertions, SM, Body); + + // now, let's chech the AST. e.g. because we need to ignore all the assertions + // that are locate within the lambda functions. + + FunctionASTVisitor Visitor(AssertNames, FoundAssertions, Result.Context, &FI); + Visitor.TraverseDecl(const_cast(Func)); + + if (FI.Assertions >= expectedAssertionsMin(FI.Lines)) { + // good, have enough assertions. + return; + } + + // very well, let's complain then + + diag(Func->getLocation(), + "function %0 falls behind recommended lines-per-assertion thresholds") + << Func; + + diag(Func->getLocation(), + "%0 lines including whitespace and comments (threshold %1)", + DiagnosticIDs::Note) + << FI.Lines << LineThreshold; + + diag(Func->getLocation(), "%0 assertions (recommended %1)", + DiagnosticIDs::Note) + << FI.Assertions << expectedAssertionsMin(FI.Lines); +} + +} // namespace misc +} // namespace tidy +} // namespace clang Index: clang-tidy/misc/CMakeLists.txt =================================================================== --- clang-tidy/misc/CMakeLists.txt +++ clang-tidy/misc/CMakeLists.txt @@ -3,6 +3,7 @@ add_clang_library(clangTidyMiscModule ArgumentCommentCheck.cpp AssertSideEffectCheck.cpp + AssertionCountCheck.cpp ForwardingReferenceOverloadCheck.cpp MisplacedConstCheck.cpp UnconventionalAssignOperatorCheck.cpp Index: clang-tidy/misc/MiscTidyModule.cpp =================================================================== --- clang-tidy/misc/MiscTidyModule.cpp +++ clang-tidy/misc/MiscTidyModule.cpp @@ -12,6 +12,7 @@ #include "../ClangTidyModuleRegistry.h" #include "ArgumentCommentCheck.h" #include "AssertSideEffectCheck.h" +#include "AssertionCountCheck.h" #include "BoolPointerImplicitConversionCheck.h" #include "DanglingHandleCheck.h" #include "DefinitionsInHeadersCheck.h" @@ -66,6 +67,8 @@ CheckFactories.registerCheck("misc-argument-comment"); CheckFactories.registerCheck( "misc-assert-side-effect"); + CheckFactories.registerCheck( + "misc-assertion-count"); CheckFactories.registerCheck( "misc-forwarding-reference-overload"); CheckFactories.registerCheck("misc-misplaced-const"); Index: docs/ReleaseNotes.rst =================================================================== --- docs/ReleaseNotes.rst +++ docs/ReleaseNotes.rst @@ -72,6 +72,12 @@ Allow custom memory management functions to be considered as well. +- New `misc-assertion-count + `_ check + + Finds functions that have more than `LinesThreshold` lines, counts assertions + in them, and if the ratio of lines-per-assert is higher than configured, emits a warning. + - New `misc-forwarding-reference-overload `_ check Index: docs/clang-tidy/checks/list.rst =================================================================== --- docs/clang-tidy/checks/list.rst +++ docs/clang-tidy/checks/list.rst @@ -73,6 +73,7 @@ llvm-twine-local misc-argument-comment misc-assert-side-effect + misc-assertion-count misc-bool-pointer-implicit-conversion misc-dangling-handle misc-definitions-in-headers Index: docs/clang-tidy/checks/misc-assertion-count.rst =================================================================== --- /dev/null +++ docs/clang-tidy/checks/misc-assertion-count.rst @@ -0,0 +1,109 @@ +.. title:: clang-tidy - misc-assertion-count + +misc-assertion-count +==================== + +Allows to impose limits on the lines-per-assertions ratio for functions. + +Finds functions that have more than `LinesThreshold` lines, counts assertions +in them, and if the ratio of lines-per-assert is higher than configured, +emits a warning. + +The lines-per-assert ratio can be controlled via four options, which form +a stair-step curve, with first two options controlling the static part, and +the second two controlling the dynamic part - i.e. how the expected minimal +assertion count changes with the line count. + +Options +------- + +.. option:: LineThreshold + + How many lines the function should have to be checked. This parameter can be + considered as LOC cut-off threshold - if the function is smaller than that, + no check will be performed. The default is `-1` (do not check any functions). + +.. option:: AssertsThreshold + + If the function is checked (i.e. it has no less than `LineThreshold` lines), + then the function should have no less than `AssertsThreshold` assertions. + The default is `0` (there is no static part of the assertion count curve). + +.. option:: LinesStep + + These two options control the dynamic part of the curve, how the expected + minimal assertion count changes with number of lines. + The default is `0` (there is no dynamic part of the curve. i.e. if the + function is checked, then regardless of the function LOC count, it must have + no less than `AssertsThreshold` assertions). + +.. option:: AssertsStep + + These two options control the dynamic part of the curve, how the expected + minimal assertion count changes with number of lines. + `LinesStep` and `AssertsStep` options work together. If the function is + checked (i.e. it has no less than `LineThreshold` lines), then for each + `LinesStep` lines over the `LineThreshold`, the expected minimal assertion + count is increased by `AssertsStep`. + The default is `0` (there is no dynamic part of the curve. i.e. if the + function is checked, then regardless of the function LOC count, it must have + no less than `AssertsStep` assertions). + +.. option:: AssertNames + + A comma-separated list of the names of macros, and non-member functions to + be counted as an assertion. + Additionally, `static_assert()` is also counted as an assertion. + +Note: + If `AssertsThreshold` is zero, and either one (or both) of `LinesStep` and + `AssertsStep` is zero, then no check will be performerd. Because such a + config says that no limits are imposed. + +Config examples: + - Any function over ten lines should have at least one assertion: + ``` + - key: misc-assertion-count.LineThreshold + value: '10' + - key: misc-assertion-count.AssertsThreshold + value: '1' + - key: misc-assertion-count.LinesStep + value: '0' + - key: misc-assertion-count.AssertsStep + value: '0' + ``` + - One assertion per each ten lines of function: + ``` + - key: misc-assertion-count.LineThreshold + value: '0' + - key: misc-assertion-count.AssertsThreshold + value: '0' + - key: misc-assertion-count.LinesStep + value: '10' + - key: misc-assertion-count.AssertsStep + value: '1' + ``` + or, equivalently, any function over ten lines should have at least one + assertion, and then one assertion per each ten lines of code: + ``` + - key: misc-assertion-count.LineThreshold + value: '10' + - key: misc-assertion-count.AssertsThreshold + value: '1' + - key: misc-assertion-count.LinesStep + value: '10' + - key: misc-assertion-count.AssertsStep + value: '1' + ``` + - Any function over ten lines should have at least one assertion, and then + two assertion per each twenty lines of code + ``` + - key: misc-assertion-count.LineThreshold + value: '10' + - key: misc-assertion-count.AssertsThreshold + value: '1' + - key: misc-assertion-count.LinesStep + value: '20' + - key: misc-assertion-count.AssertsStep + value: '2' + ``` Index: test/clang-tidy/Inputs/misc-assertion-count/normal-include.h =================================================================== --- /dev/null +++ test/clang-tidy/Inputs/misc-assertion-count/normal-include.h @@ -0,0 +1 @@ +#define external_assert(...) Index: test/clang-tidy/Inputs/misc-assertion-count/normal-system-include.h =================================================================== --- /dev/null +++ test/clang-tidy/Inputs/misc-assertion-count/normal-system-include.h @@ -0,0 +1 @@ +#define external_system_assert(...) Index: test/clang-tidy/misc-assertion-count.cpp =================================================================== --- /dev/null +++ test/clang-tidy/misc-assertion-count.cpp @@ -0,0 +1,654 @@ +// RUN: %check_clang_tidy %s misc-assertion-count %t \ +// RUN: -- -config="{CheckOptions: [ \ +// RUN: {key: misc-assertion-count.LineThreshold, value: 0}, \ +// RUN: {key: misc-assertion-count.AssertsThreshold, value: 2}, \ +// RUN: {key: misc-assertion-count.LinesStep, value: 0}, \ +// RUN: {key: misc-assertion-count.AssertsStep, value: 0}, \ +// RUN: {key: misc-assertion-count.AssertNames, value: 'assert,assert2,my_assert,convoluted_assert,msvc_assert,convoluted_assert_wrapper,convoluted_assert_wrapper2,dummydefine,external_assert,external_system_assert,external_ignored_assert,external_ignored_system_assert,ThrowException,ThrowExceptionHelper,ThrowIE,justSomeFunction,justSomeOtherFunction,tracked_static_macro,tracked_lambda,tracked_lambda_macro'} \ +// RUN: ]}" \ +// RUN: -line-filter='[{"name":"%t.cpp","lines":[[97,9999]]}]' \ +// RUN: -- -std=c++11 -fexceptions \ +// RUN: -I %S/Inputs/misc-assertion-count -isystem %S/Inputs/misc-assertion-count + +#include "normal-include.h" +#include + +// clang-format off + +//===--- assert definition block ------------------------------------------===// +int abort() { return 0; } + +#ifdef NDEBUG +#define assert(x) 1 +#else +#define assert(x) \ + if (!(x)) \ + (void)abort() +#endif + +void print(...); +#define assert2(e) (__builtin_expect(!(e), 0) ? \ + print (#e, __FILE__, __LINE__) : (void)0) + +#ifdef NDEBUG +#define my_assert(x) 1 +#else +#define my_assert(x) \ + ((void)((x) ? 1 : abort())) +#endif + +#ifdef NDEBUG +#define not_my_assert(x) 1 +#else +#define not_my_assert(x) \ + if (!(x)) \ + (void)abort() +#endif + +#define real_assert(x) ((void)((x) ? 1 : abort())) +#define wrap1(x) real_assert(x) +#define wrap2(x) wrap1(x) +#define convoluted_assert(x) wrap2(x) + +#define msvc_assert(expression) (void)( \ + (!!(expression)) || \ + (abort(), 0) \ + ) +#define convoluted_assert_wrapper2(x) convoluted_assert_wrapper(x) + +#define convoluted_assert_wrapper(x) convoluted_assert(x) + +#define dummydefine(...) + +#define static_macro(x) static_assert(sizeof(x) > 0, "foo") + +#define tracked_static_macro(x) static_macro(x) + +// FIXME: how to detect lambda calls? +auto tracked_lambda = [](int X) { + assert(X); +}; + +#define tracked_lambda_macro(x) tracked_lambda(x) + +//===----------------------------------------------------------------------===// + +//===--- function definition block ----------------------------------------===// +template +void ThrowException(int file, int line) { + print (file, line); + throw T(); +} + +#define ThrowExceptionHelper(CLASS) ThrowException('f', __LINE__) + +#define ThrowIE() \ + do { \ + ThrowExceptionHelper(int); \ + __builtin_unreachable(); \ + } while (false) + +void justSomeFunction(int a); +void justSomeOtherFunction(int b, int c){}; + + +//===----------------------------------------------------------------------===// + +int X = 0; + +void foo0() {} +// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: function 'foo0' falls behind recommended lines-per-assertion thresholds [misc-assertion-count] +// CHECK-MESSAGES: :[[@LINE-2]]:6: note: 0 lines including whitespace and comments (threshold 0) +// CHECK-MESSAGES: :[[@LINE-3]]:6: note: 0 assertions (recommended 2) + +void foo1() { +// one line +} +// CHECK-MESSAGES: :[[@LINE-3]]:6: warning: function 'foo1' falls behind recommended lines-per-assertion thresholds [misc-assertion-count] +// CHECK-MESSAGES: :[[@LINE-4]]:6: note: 2 lines including whitespace and comments (threshold 0) +// CHECK-MESSAGES: :[[@LINE-5]]:6: note: 0 assertions (recommended 2) + +void foo2() {assert(X);} +// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: function 'foo2' falls behind recommended lines-per-assertion thresholds [misc-assertion-count] +// CHECK-MESSAGES: :[[@LINE-2]]:6: note: 0 lines including whitespace and comments (threshold 0) +// CHECK-MESSAGES: :[[@LINE-3]]:6: note: 1 assertions (recommended 2) + +void foo3() { + assert(X); +} +// CHECK-MESSAGES: :[[@LINE-3]]:6: warning: function 'foo3' falls behind recommended lines-per-assertion thresholds [misc-assertion-count] +// CHECK-MESSAGES: :[[@LINE-4]]:6: note: 2 lines including whitespace and comments (threshold 0) +// CHECK-MESSAGES: :[[@LINE-5]]:6: note: 1 assertions (recommended 2) + +void foo4() { // OK + assert(X); + assert(X); +} + +void foo5() { + assert2(X); +} +// CHECK-MESSAGES: :[[@LINE-3]]:6: warning: function 'foo5' falls behind recommended lines-per-assertion thresholds [misc-assertion-count] +// CHECK-MESSAGES: :[[@LINE-4]]:6: note: 2 lines including whitespace and comments (threshold 0) +// CHECK-MESSAGES: :[[@LINE-5]]:6: note: 1 assertions (recommended 2) + +void foo6() { + my_assert(X); +} +// CHECK-MESSAGES: :[[@LINE-3]]:6: warning: function 'foo6' falls behind recommended lines-per-assertion thresholds [misc-assertion-count] +// CHECK-MESSAGES: :[[@LINE-4]]:6: note: 2 lines including whitespace and comments (threshold 0) +// CHECK-MESSAGES: :[[@LINE-5]]:6: note: 1 assertions (recommended 2) + +void foo7() { + wrap2(X); + convoluted_assert(X); +} +// CHECK-MESSAGES: :[[@LINE-4]]:6: warning: function 'foo7' falls behind recommended lines-per-assertion thresholds [misc-assertion-count] +// CHECK-MESSAGES: :[[@LINE-5]]:6: note: 3 lines including whitespace and comments (threshold 0) +// CHECK-MESSAGES: :[[@LINE-6]]:6: note: 1 assertions (recommended 2) + +void foo8() { + msvc_assert(X); +} +// CHECK-MESSAGES: :[[@LINE-3]]:6: warning: function 'foo8' falls behind recommended lines-per-assertion thresholds [misc-assertion-count] +// CHECK-MESSAGES: :[[@LINE-4]]:6: note: 2 lines including whitespace and comments (threshold 0) +// CHECK-MESSAGES: :[[@LINE-5]]:6: note: 1 assertions (recommended 2) + +void foo9() { + convoluted_assert_wrapper(X); +} +// CHECK-MESSAGES: :[[@LINE-3]]:6: warning: function 'foo9' falls behind recommended lines-per-assertion thresholds [misc-assertion-count] +// CHECK-MESSAGES: :[[@LINE-4]]:6: note: 2 lines including whitespace and comments (threshold 0) +// CHECK-MESSAGES: :[[@LINE-5]]:6: note: 1 assertions (recommended 2) + +void foo10() { + convoluted_assert_wrapper2(X); +} +// CHECK-MESSAGES: :[[@LINE-3]]:6: warning: function 'foo10' falls behind recommended lines-per-assertion thresholds [misc-assertion-count] +// CHECK-MESSAGES: :[[@LINE-4]]:6: note: 2 lines including whitespace and comments (threshold 0) +// CHECK-MESSAGES: :[[@LINE-5]]:6: note: 1 assertions (recommended 2) + +void foo11() { + dummydefine(X); +} +// CHECK-MESSAGES: :[[@LINE-3]]:6: warning: function 'foo11' falls behind recommended lines-per-assertion thresholds [misc-assertion-count] +// CHECK-MESSAGES: :[[@LINE-4]]:6: note: 2 lines including whitespace and comments (threshold 0) +// CHECK-MESSAGES: :[[@LINE-5]]:6: note: 1 assertions (recommended 2) + +void foo12() { + external_assert(X); +} +// CHECK-MESSAGES: :[[@LINE-3]]:6: warning: function 'foo12' falls behind recommended lines-per-assertion thresholds [misc-assertion-count] +// CHECK-MESSAGES: :[[@LINE-4]]:6: note: 2 lines including whitespace and comments (threshold 0) +// CHECK-MESSAGES: :[[@LINE-5]]:6: note: 1 assertions (recommended 2) + +void foo13() { + external_system_assert(X); +} +// CHECK-MESSAGES: :[[@LINE-3]]:6: warning: function 'foo13' falls behind recommended lines-per-assertion thresholds [misc-assertion-count] +// CHECK-MESSAGES: :[[@LINE-4]]:6: note: 2 lines including whitespace and comments (threshold 0) +// CHECK-MESSAGES: :[[@LINE-5]]:6: note: 1 assertions (recommended 2) + +inline void foo14() { // OK + assert(X); + msvc_assert(X); +} + +inline void foo15() { // OK + assert(X); + msvc_assert(X); + convoluted_assert(X); +} + +inline void foo16() { // OK + assert(X); + + if(false) + msvc_assert(X); +} + +void foo17() { + foo14(); + foo15(); + foo16(); +} +// CHECK-MESSAGES: :[[@LINE-5]]:6: warning: function 'foo17' falls behind recommended lines-per-assertion thresholds [misc-assertion-count] +// CHECK-MESSAGES: :[[@LINE-6]]:6: note: 4 lines including whitespace and comments (threshold 0) +// CHECK-MESSAGES: :[[@LINE-7]]:6: note: 0 assertions (recommended 2) + +void foo18() { assert(X); } void foo19() { assert(X); } +// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: function 'foo18' falls behind recommended lines-per-assertion thresholds [misc-assertion-count] +// CHECK-MESSAGES: :[[@LINE-2]]:6: note: 0 lines including whitespace and comments (threshold 0) +// CHECK-MESSAGES: :[[@LINE-3]]:6: note: 1 assertions (recommended 2) +// CHECK-MESSAGES: :[[@LINE-4]]:34: warning: function 'foo19' falls behind recommended lines-per-assertion thresholds [misc-assertion-count] +// CHECK-MESSAGES: :[[@LINE-5]]:34: note: 0 lines including whitespace and comments (threshold 0) +// CHECK-MESSAGES: :[[@LINE-6]]:34: note: 1 assertions (recommended 2) + +void foo20() { assert(X); assert(X); } void foo21() { assert(X); } +// CHECK-MESSAGES: :[[@LINE-1]]:45: warning: function 'foo21' falls behind recommended lines-per-assertion thresholds [misc-assertion-count] +// CHECK-MESSAGES: :[[@LINE-2]]:45: note: 0 lines including whitespace and comments (threshold 0) +// CHECK-MESSAGES: :[[@LINE-3]]:45: note: 1 assertions (recommended 2) + +void foo22() { assert(X); } void foo23() { assert(X); assert(X); } +// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: function 'foo22' falls behind recommended lines-per-assertion thresholds [misc-assertion-count] +// CHECK-MESSAGES: :[[@LINE-2]]:6: note: 0 lines including whitespace and comments (threshold 0) +// CHECK-MESSAGES: :[[@LINE-3]]:6: note: 1 assertions (recommended 2) + +void foo24() { assert(X); assert(X); } void foo25() { assert(X); assert(X); } + +void foo26() { + assert(X); } void foo27() { assert(X); } +// CHECK-MESSAGES: :[[@LINE-2]]:6: warning: function 'foo26' falls behind recommended lines-per-assertion thresholds [misc-assertion-count] +// CHECK-MESSAGES: :[[@LINE-3]]:6: note: 1 lines including whitespace and comments (threshold 0) +// CHECK-MESSAGES: :[[@LINE-4]]:6: note: 1 assertions (recommended 2) +// CHECK-MESSAGES: :[[@LINE-4]]:21: warning: function 'foo27' falls behind recommended lines-per-assertion thresholds [misc-assertion-count] +// CHECK-MESSAGES: :[[@LINE-5]]:21: note: 0 lines including whitespace and comments (threshold 0) +// CHECK-MESSAGES: :[[@LINE-6]]:21: note: 1 assertions (recommended 2) + +void foo28() { assert(X); +} void foo29() { assert(X); } +// CHECK-MESSAGES: :[[@LINE-2]]:6: warning: function 'foo28' falls behind recommended lines-per-assertion thresholds [misc-assertion-count] +// CHECK-MESSAGES: :[[@LINE-3]]:6: note: 1 lines including whitespace and comments (threshold 0) +// CHECK-MESSAGES: :[[@LINE-4]]:6: note: 1 assertions (recommended 2) +// CHECK-MESSAGES: :[[@LINE-4]]:8: warning: function 'foo29' falls behind recommended lines-per-assertion thresholds [misc-assertion-count] +// CHECK-MESSAGES: :[[@LINE-5]]:8: note: 0 lines including whitespace and comments (threshold 0) +// CHECK-MESSAGES: :[[@LINE-6]]:8: note: 1 assertions (recommended 2) + +void foo30() { assert(X); } +void foo31() { assert(X); } +// CHECK-MESSAGES: :[[@LINE-2]]:6: warning: function 'foo30' falls behind recommended lines-per-assertion thresholds [misc-assertion-count] +// CHECK-MESSAGES: :[[@LINE-3]]:6: note: 0 lines including whitespace and comments (threshold 0) +// CHECK-MESSAGES: :[[@LINE-4]]:6: note: 1 assertions (recommended 2) +// CHECK-MESSAGES: :[[@LINE-4]]:6: warning: function 'foo31' falls behind recommended lines-per-assertion thresholds [misc-assertion-count] +// CHECK-MESSAGES: :[[@LINE-5]]:6: note: 0 lines including whitespace and comments (threshold 0) +// CHECK-MESSAGES: :[[@LINE-6]]:6: note: 1 assertions (recommended 2) + +void foo32() { assert(X); } void foo33() { + assert(X); } +// CHECK-MESSAGES: :[[@LINE-2]]:6: warning: function 'foo32' falls behind recommended lines-per-assertion thresholds [misc-assertion-count] +// CHECK-MESSAGES: :[[@LINE-3]]:6: note: 0 lines including whitespace and comments (threshold 0) +// CHECK-MESSAGES: :[[@LINE-4]]:6: note: 1 assertions (recommended 2) +// CHECK-MESSAGES: :[[@LINE-5]]:34: warning: function 'foo33' falls behind recommended lines-per-assertion thresholds [misc-assertion-count] +// CHECK-MESSAGES: :[[@LINE-6]]:34: note: 1 lines including whitespace and comments (threshold 0) +// CHECK-MESSAGES: :[[@LINE-7]]:34: note: 1 assertions (recommended 2) + +void foo34() { assert(X); } void foo35() { assert(X); +} +// CHECK-MESSAGES: :[[@LINE-2]]:6: warning: function 'foo34' falls behind recommended lines-per-assertion thresholds [misc-assertion-count] +// CHECK-MESSAGES: :[[@LINE-3]]:6: note: 0 lines including whitespace and comments (threshold 0) +// CHECK-MESSAGES: :[[@LINE-4]]:6: note: 1 assertions (recommended 2) +// CHECK-MESSAGES: :[[@LINE-5]]:34: warning: function 'foo35' falls behind recommended lines-per-assertion thresholds [misc-assertion-count] +// CHECK-MESSAGES: :[[@LINE-6]]:34: note: 1 lines including whitespace and comments (threshold 0) +// CHECK-MESSAGES: :[[@LINE-7]]:34: note: 1 assertions (recommended 2) + +void foo36() { + assert(X); } void foo37() { + assert(X); } +// CHECK-MESSAGES: :[[@LINE-3]]:6: warning: function 'foo36' falls behind recommended lines-per-assertion thresholds [misc-assertion-count] +// CHECK-MESSAGES: :[[@LINE-4]]:6: note: 1 lines including whitespace and comments (threshold 0) +// CHECK-MESSAGES: :[[@LINE-5]]:6: note: 1 assertions (recommended 2) +// CHECK-MESSAGES: :[[@LINE-5]]:21: warning: function 'foo37' falls behind recommended lines-per-assertion thresholds [misc-assertion-count] +// CHECK-MESSAGES: :[[@LINE-6]]:21: note: 1 lines including whitespace and comments (threshold 0) +// CHECK-MESSAGES: :[[@LINE-7]]:21: note: 1 assertions (recommended 2) + +// FIXME +void foo38() { + tracked_lambda(X); +} +// CHECK-MESSAGES: :[[@LINE-3]]:6: warning: function 'foo38' falls behind recommended lines-per-assertion thresholds [misc-assertion-count] +// CHECK-MESSAGES: :[[@LINE-4]]:6: note: 2 lines including whitespace and comments (threshold 0) +// CHECK-MESSAGES: :[[@LINE-5]]:6: note: 0 assertions (recommended 2) + +void foo39() { + tracked_lambda_macro(X); +} +// CHECK-MESSAGES: :[[@LINE-3]]:6: warning: function 'foo39' falls behind recommended lines-per-assertion thresholds [misc-assertion-count] +// CHECK-MESSAGES: :[[@LINE-4]]:6: note: 2 lines including whitespace and comments (threshold 0) +// CHECK-MESSAGES: :[[@LINE-5]]:6: note: 1 assertions (recommended 2) + +//===----------------------------------------------------------------------===// + +void bar0() { + abort(); +} +// CHECK-MESSAGES: :[[@LINE-3]]:6: warning: function 'bar0' falls behind recommended lines-per-assertion thresholds [misc-assertion-count] +// CHECK-MESSAGES: :[[@LINE-4]]:6: note: 2 lines including whitespace and comments (threshold 0) +// CHECK-MESSAGES: :[[@LINE-5]]:6: note: 0 assertions (recommended 2) + +void bar1() { + ThrowException(0, 0); +} +// CHECK-MESSAGES: :[[@LINE-3]]:6: warning: function 'bar1' falls behind recommended lines-per-assertion thresholds [misc-assertion-count] +// CHECK-MESSAGES: :[[@LINE-4]]:6: note: 2 lines including whitespace and comments (threshold 0) +// CHECK-MESSAGES: :[[@LINE-5]]:6: note: 1 assertions (recommended 2) + +void bar2() { + ThrowExceptionHelper(int); +} +// CHECK-MESSAGES: :[[@LINE-3]]:6: warning: function 'bar2' falls behind recommended lines-per-assertion thresholds [misc-assertion-count] +// CHECK-MESSAGES: :[[@LINE-4]]:6: note: 2 lines including whitespace and comments (threshold 0) +// CHECK-MESSAGES: :[[@LINE-5]]:6: note: 1 assertions (recommended 2) + +void bar3() { + ThrowIE(); +} +// CHECK-MESSAGES: :[[@LINE-3]]:6: warning: function 'bar3' falls behind recommended lines-per-assertion thresholds [misc-assertion-count] +// CHECK-MESSAGES: :[[@LINE-4]]:6: note: 2 lines including whitespace and comments (threshold 0) +// CHECK-MESSAGES: :[[@LINE-5]]:6: note: 1 assertions (recommended 2) + +void bar4() { + justSomeFunction(X); +} +// CHECK-MESSAGES: :[[@LINE-3]]:6: warning: function 'bar4' falls behind recommended lines-per-assertion thresholds [misc-assertion-count] +// CHECK-MESSAGES: :[[@LINE-4]]:6: note: 2 lines including whitespace and comments (threshold 0) +// CHECK-MESSAGES: :[[@LINE-5]]:6: note: 1 assertions (recommended 2) + +void bar5() { + justSomeOtherFunction(X, X - X); +} +// CHECK-MESSAGES: :[[@LINE-3]]:6: warning: function 'bar5' falls behind recommended lines-per-assertion thresholds [misc-assertion-count] +// CHECK-MESSAGES: :[[@LINE-4]]:6: note: 2 lines including whitespace and comments (threshold 0) +// CHECK-MESSAGES: :[[@LINE-5]]:6: note: 1 assertions (recommended 2) + +inline void bar6() { // OK + justSomeFunction(X); + justSomeOtherFunction(X, X * X); +} + +inline void bar7() { // OK + assert(X); + justSomeOtherFunction(X, X + 4); +} + +struct QQQ { +inline void bar8() { // OK + { + int Y = 1; + do { + for(;;); + while(0) { + assert(X); + } + if(false) { + justSomeOtherFunction(X, Y); + } + } while(false); + } +} +}; + +void bar9() { + bar6(); + bar7(); +} +// CHECK-MESSAGES: :[[@LINE-4]]:6: warning: function 'bar9' falls behind recommended lines-per-assertion thresholds [misc-assertion-count] +// CHECK-MESSAGES: :[[@LINE-5]]:6: note: 3 lines including whitespace and comments (threshold 0) +// CHECK-MESSAGES: :[[@LINE-6]]:6: note: 0 assertions (recommended 2) + +void bar10() { justSomeFunction(X); } void bar11() { justSomeFunction(X); } +// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: function 'bar10' falls behind recommended lines-per-assertion thresholds [misc-assertion-count] +// CHECK-MESSAGES: :[[@LINE-2]]:6: note: 0 lines including whitespace and comments (threshold 0) +// CHECK-MESSAGES: :[[@LINE-3]]:6: note: 1 assertions (recommended 2) +// CHECK-MESSAGES: :[[@LINE-4]]:44: warning: function 'bar11' falls behind recommended lines-per-assertion thresholds [misc-assertion-count] +// CHECK-MESSAGES: :[[@LINE-5]]:44: note: 0 lines including whitespace and comments (threshold 0) +// CHECK-MESSAGES: :[[@LINE-6]]:44: note: 1 assertions (recommended 2) + +void bar12() { assert(X); justSomeFunction(X); } void bar13() { justSomeFunction(X); } +// CHECK-MESSAGES: :[[@LINE-1]]:55: warning: function 'bar13' falls behind recommended lines-per-assertion thresholds [misc-assertion-count] +// CHECK-MESSAGES: :[[@LINE-2]]:55: note: 0 lines including whitespace and comments (threshold 0) +// CHECK-MESSAGES: :[[@LINE-3]]:55: note: 1 assertions (recommended 2) + +auto bar13andhalf = []() { +}; +// CHECK-MESSAGES: :[[@LINE-2]]:21: warning: function 'operator()' falls behind recommended lines-per-assertion thresholds [misc-assertion-count] +// CHECK-MESSAGES: :[[@LINE-3]]:21: note: 1 lines including whitespace and comments (threshold 0) +// CHECK-MESSAGES: :[[@LINE-4]]:21: note: 0 assertions (recommended 2) + +void bar14() { + auto bar15 = []() { + }; +} +// CHECK-MESSAGES: :[[@LINE-4]]:6: warning: function 'bar14' falls behind recommended lines-per-assertion thresholds [misc-assertion-count] +// CHECK-MESSAGES: :[[@LINE-5]]:6: note: 3 lines including whitespace and comments (threshold 0) +// CHECK-MESSAGES: :[[@LINE-6]]:6: note: 0 assertions (recommended 2) + +void bar16() { + auto bar17 = []() { + auto bar18 = []() { + }; + }; +} +// CHECK-MESSAGES: :[[@LINE-6]]:6: warning: function 'bar16' falls behind recommended lines-per-assertion thresholds [misc-assertion-count] +// CHECK-MESSAGES: :[[@LINE-7]]:6: note: 5 lines including whitespace and comments (threshold 0) +// CHECK-MESSAGES: :[[@LINE-8]]:6: note: 0 assertions (recommended 2) + +void bar19() { + auto bar20 = []() { + }; + auto bar21 = []() { + }; +} +// CHECK-MESSAGES: :[[@LINE-6]]:6: warning: function 'bar19' falls behind recommended lines-per-assertion thresholds [misc-assertion-count] +// CHECK-MESSAGES: :[[@LINE-7]]:6: note: 5 lines including whitespace and comments (threshold 0) +// CHECK-MESSAGES: :[[@LINE-8]]:6: note: 0 assertions (recommended 2) + +void bar22() { + assert(X); + auto bar23 = []() { + }; +} +// CHECK-MESSAGES: :[[@LINE-5]]:6: warning: function 'bar22' falls behind recommended lines-per-assertion thresholds [misc-assertion-count] +// CHECK-MESSAGES: :[[@LINE-6]]:6: note: 4 lines including whitespace and comments (threshold 0) +// CHECK-MESSAGES: :[[@LINE-7]]:6: note: 1 assertions (recommended 2) + +void bar24() { + auto bar25 = []() { + assert(X); + }; +} +// CHECK-MESSAGES: :[[@LINE-5]]:6: warning: function 'bar24' falls behind recommended lines-per-assertion thresholds [misc-assertion-count] +// CHECK-MESSAGES: :[[@LINE-6]]:6: note: 4 lines including whitespace and comments (threshold 0) +// CHECK-MESSAGES: :[[@LINE-7]]:6: note: 0 assertions (recommended 2) + +void bar26() { + assert(X); + auto bar27 = []() { + assert(X); + }; +} +// CHECK-MESSAGES: :[[@LINE-6]]:6: warning: function 'bar26' falls behind recommended lines-per-assertion thresholds [misc-assertion-count] +// CHECK-MESSAGES: :[[@LINE-7]]:6: note: 5 lines including whitespace and comments (threshold 0) +// CHECK-MESSAGES: :[[@LINE-8]]:6: note: 1 assertions (recommended 2) + +void bar28() { + auto bar29 = []() { + assert(X); + auto bar30 = []() { + }; + }; +} +// CHECK-MESSAGES: :[[@LINE-7]]:6: warning: function 'bar28' falls behind recommended lines-per-assertion thresholds [misc-assertion-count] +// CHECK-MESSAGES: :[[@LINE-8]]:6: note: 6 lines including whitespace and comments (threshold 0) +// CHECK-MESSAGES: :[[@LINE-9]]:6: note: 0 assertions (recommended 2) + +void bar31() { + auto bar32 = []() { + auto bar33 = []() { + assert(X); + }; + }; +} +// CHECK-MESSAGES: :[[@LINE-7]]:6: warning: function 'bar31' falls behind recommended lines-per-assertion thresholds [misc-assertion-count] +// CHECK-MESSAGES: :[[@LINE-8]]:6: note: 6 lines including whitespace and comments (threshold 0) +// CHECK-MESSAGES: :[[@LINE-9]]:6: note: 0 assertions (recommended 2) + +void bar34() { + auto bar35 = []() { + assert(X); + auto bar36 = []() { + assert(X); + }; + }; +} +// CHECK-MESSAGES: :[[@LINE-8]]:6: warning: function 'bar34' falls behind recommended lines-per-assertion thresholds [misc-assertion-count] +// CHECK-MESSAGES: :[[@LINE-9]]:6: note: 7 lines including whitespace and comments (threshold 0) +// CHECK-MESSAGES: :[[@LINE-10]]:6: note: 0 assertions (recommended 2) + +void bar37() { + assert(X); + auto bar38 = []() { + assert(X); + auto bar39 = []() { + assert(X); + }; + }; +} +// CHECK-MESSAGES: :[[@LINE-9]]:6: warning: function 'bar37' falls behind recommended lines-per-assertion thresholds [misc-assertion-count] +// CHECK-MESSAGES: :[[@LINE-10]]:6: note: 8 lines including whitespace and comments (threshold 0) +// CHECK-MESSAGES: :[[@LINE-11]]:6: note: 1 assertions (recommended 2) + +void bar40() { + justSomeFunction(X); + auto bar41 = []() { + assert(X); + }; +} +// CHECK-MESSAGES: :[[@LINE-6]]:6: warning: function 'bar40' falls behind recommended lines-per-assertion thresholds [misc-assertion-count] +// CHECK-MESSAGES: :[[@LINE-7]]:6: note: 5 lines including whitespace and comments (threshold 0) +// CHECK-MESSAGES: :[[@LINE-8]]:6: note: 1 assertions (recommended 2) + +void bar42() { + assert(X); + auto bar43 = []() { + justSomeFunction(X); + }; +} +// CHECK-MESSAGES: :[[@LINE-6]]:6: warning: function 'bar42' falls behind recommended lines-per-assertion thresholds [misc-assertion-count] +// CHECK-MESSAGES: :[[@LINE-7]]:6: note: 5 lines including whitespace and comments (threshold 0) +// CHECK-MESSAGES: :[[@LINE-8]]:6: note: 1 assertions (recommended 2) + +//===----------------------------------------------------------------------===// + +// do not warn on implicit declarations. +class C { + void memfunc() {} +}; +C S; +// CHECK-MESSAGES: :[[@LINE-3]]:8: warning: function 'memfunc' falls behind recommended lines-per-assertion thresholds [misc-assertion-count] +// CHECK-MESSAGES: :[[@LINE-4]]:8: note: 0 lines including whitespace and comments (threshold 0) +// CHECK-MESSAGES: :[[@LINE-5]]:8: note: 0 assertions (recommended 2) + +void bas0() { + class C { + void bas1() {} + }; +} +// CHECK-MESSAGES: :[[@LINE-5]]:6: warning: function 'bas0' falls behind recommended lines-per-assertion thresholds [misc-assertion-count] +// CHECK-MESSAGES: :[[@LINE-6]]:6: note: 4 lines including whitespace and comments (threshold 0) +// CHECK-MESSAGES: :[[@LINE-7]]:6: note: 0 assertions (recommended 2) +// CHECK-MESSAGES: :[[@LINE-6]]:10: warning: function 'bas1' falls behind recommended lines-per-assertion thresholds [misc-assertion-count] +// CHECK-MESSAGES: :[[@LINE-7]]:10: note: 0 lines including whitespace and comments (threshold 0) +// CHECK-MESSAGES: :[[@LINE-8]]:10: note: 0 assertions (recommended 2) + +void bas2() { + assert(X); + class C { + void bas3() {} + }; +} +// CHECK-MESSAGES: :[[@LINE-6]]:6: warning: function 'bas2' falls behind recommended lines-per-assertion thresholds [misc-assertion-count] +// CHECK-MESSAGES: :[[@LINE-7]]:6: note: 5 lines including whitespace and comments (threshold 0) +// CHECK-MESSAGES: :[[@LINE-8]]:6: note: 1 assertions (recommended 2) +// CHECK-MESSAGES: :[[@LINE-6]]:10: warning: function 'bas3' falls behind recommended lines-per-assertion thresholds [misc-assertion-count] +// CHECK-MESSAGES: :[[@LINE-7]]:10: note: 0 lines including whitespace and comments (threshold 0) +// CHECK-MESSAGES: :[[@LINE-8]]:10: note: 0 assertions (recommended 2) + +void bas4() { + class C { + void bas5() { + assert(X); + } + }; +} +// CHECK-MESSAGES: :[[@LINE-7]]:6: warning: function 'bas4' falls behind recommended lines-per-assertion thresholds [misc-assertion-count] +// CHECK-MESSAGES: :[[@LINE-8]]:6: note: 6 lines including whitespace and comments (threshold 0) +// CHECK-MESSAGES: :[[@LINE-9]]:6: note: 0 assertions (recommended 2) +// CHECK-MESSAGES: :[[@LINE-8]]:10: warning: function 'bas5' falls behind recommended lines-per-assertion thresholds [misc-assertion-count] +// CHECK-MESSAGES: :[[@LINE-9]]:10: note: 2 lines including whitespace and comments (threshold 0) +// CHECK-MESSAGES: :[[@LINE-10]]:10: note: 1 assertions (recommended 2) + +void bas6() { + assert(X); + class C { + void bas7() { + assert(X); + } + }; +} +// CHECK-MESSAGES: :[[@LINE-8]]:6: warning: function 'bas6' falls behind recommended lines-per-assertion thresholds [misc-assertion-count] +// CHECK-MESSAGES: :[[@LINE-9]]:6: note: 7 lines including whitespace and comments (threshold 0) +// CHECK-MESSAGES: :[[@LINE-10]]:6: note: 1 assertions (recommended 2) +// CHECK-MESSAGES: :[[@LINE-8]]:10: warning: function 'bas7' falls behind recommended lines-per-assertion thresholds [misc-assertion-count] +// CHECK-MESSAGES: :[[@LINE-9]]:10: note: 2 lines including whitespace and comments (threshold 0) +// CHECK-MESSAGES: :[[@LINE-10]]:10: note: 1 assertions (recommended 2) + +//===----------------------------------------------------------------------===// + +void baz0() { + static_assert(sizeof(X) > 0, "foo"); +} +// CHECK-MESSAGES: :[[@LINE-3]]:6: warning: function 'baz0' falls behind recommended lines-per-assertion thresholds [misc-assertion-count] +// CHECK-MESSAGES: :[[@LINE-4]]:6: note: 2 lines including whitespace and comments (threshold 0) +// CHECK-MESSAGES: :[[@LINE-5]]:6: note: 1 assertions (recommended 2) + +void baz1() { + static_macro(X); +} +// CHECK-MESSAGES: :[[@LINE-3]]:6: warning: function 'baz1' falls behind recommended lines-per-assertion thresholds [misc-assertion-count] +// CHECK-MESSAGES: :[[@LINE-4]]:6: note: 2 lines including whitespace and comments (threshold 0) +// CHECK-MESSAGES: :[[@LINE-5]]:6: note: 1 assertions (recommended 2) + +void baz2() { + tracked_static_macro(X); +} +// CHECK-MESSAGES: :[[@LINE-3]]:6: warning: function 'baz2' falls behind recommended lines-per-assertion thresholds [misc-assertion-count] +// CHECK-MESSAGES: :[[@LINE-4]]:6: note: 2 lines including whitespace and comments (threshold 0) +// CHECK-MESSAGES: :[[@LINE-5]]:6: note: 1 assertions (recommended 2) + +void baz3() { // OK + static_assert(sizeof(X) > 0, "foo"); + assert(X); +} + +void baz4() { // OK + static_macro(X); + justSomeFunction(X); +} + +void baz5() { + static_macro(X); + auto baz6 = []() { + }; +} +// CHECK-MESSAGES: :[[@LINE-5]]:6: warning: function 'baz5' falls behind recommended lines-per-assertion thresholds [misc-assertion-count] +// CHECK-MESSAGES: :[[@LINE-6]]:6: note: 4 lines including whitespace and comments (threshold 0) +// CHECK-MESSAGES: :[[@LINE-7]]:6: note: 1 assertions (recommended 2) + +void baz7() { + auto baz8 = []() { + static_macro(X); + }; +} +// CHECK-MESSAGES: :[[@LINE-5]]:6: warning: function 'baz7' falls behind recommended lines-per-assertion thresholds [misc-assertion-count] +// CHECK-MESSAGES: :[[@LINE-6]]:6: note: 4 lines including whitespace and comments (threshold 0) +// CHECK-MESSAGES: :[[@LINE-7]]:6: note: 0 assertions (recommended 2) + +void baz9() { + static_macro(X); + auto baz10 = []() { + static_macro(X); + }; +} +// CHECK-MESSAGES: :[[@LINE-6]]:6: warning: function 'baz9' falls behind recommended lines-per-assertion thresholds [misc-assertion-count] +// CHECK-MESSAGES: :[[@LINE-7]]:6: note: 5 lines including whitespace and comments (threshold 0) +// CHECK-MESSAGES: :[[@LINE-8]]:6: note: 1 assertions (recommended 2) Index: unittests/clang-tidy/CMakeLists.txt =================================================================== --- unittests/clang-tidy/CMakeLists.txt +++ unittests/clang-tidy/CMakeLists.txt @@ -12,6 +12,7 @@ IncludeInserterTest.cpp GoogleModuleTest.cpp LLVMModuleTest.cpp + MiscAssertionCountCheckTest.cpp MiscModuleTest.cpp NamespaceAliaserTest.cpp OverlappingReplacementsTest.cpp Index: unittests/clang-tidy/MiscAssertionCountCheckTest.cpp =================================================================== --- /dev/null +++ unittests/clang-tidy/MiscAssertionCountCheckTest.cpp @@ -0,0 +1,313 @@ +#include "ClangTidyTest.h" +#include "misc/ArgumentCommentCheck.h" +#include "misc/AssertionCountCheck.h" +#include "gtest/gtest.h" + +namespace clang { +namespace tidy { +namespace test { +namespace { + +template +std::unique_ptr +createCheck(const ClangTidyOptions &ExtraOptions = ClangTidyOptions()) { + ClangTidyOptions Options = ExtraOptions; + Options.Checks = "*"; + + ClangTidyContext Context(llvm::make_unique( + ClangTidyGlobalOptions(), Options)); + + return llvm::make_unique("misc-assertion-count", &Context); +} + +// names of the config options used by the check +static const std::array ParamNames = {{ + "misc-assertion-count.LineThreshold", + "misc-assertion-count.AssertsThreshold", "misc-assertion-count.LinesStep", + "misc-assertion-count.AssertsStep", +}}; +// \/ and /\ are the exact same things. +using AssertionCountCheckParams = + std::tuple; + +class AssertionCountCheckTest + : public ::testing::TestWithParam { +protected: + AssertionCountCheckTest() = default; + + virtual void SetUp() { + auto P = GetParam(); + + static_assert(ParamNames.size() == std::tuple_size::value, + "must have the sample number of config options and values " + "for these options"); + + ClangTidyOptions Opts; + + Opts.CheckOptions[ParamNames[0]] = std::to_string(std::get<0>(P)); + Opts.CheckOptions[ParamNames[1]] = std::to_string(std::get<1>(P)); + Opts.CheckOptions[ParamNames[2]] = std::to_string(std::get<2>(P)); + Opts.CheckOptions[ParamNames[3]] = std::to_string(std::get<3>(P)); + + C = createCheck(Opts); + } + + std::unique_ptr C; +}; + +// generates all possible combination of config options. +// the actual test data is stored in map, with key being this tuple. +INSTANTIATE_TEST_CASE_P(AssertionCountCheckTest, AssertionCountCheckTest, + ::testing::Combine(::testing::Values(0U, 10U), + ::testing::Values(0U, 1U), + ::testing::Values(0U, 20U), + ::testing::Values(0U, 2U))); + +// (Lines, expected result) +using AssertionCountCheckPoint = std::pair; + +// keeping the data in map allows to simultaneously actually store the data, and +// to sanity-check that all the data (as in, for all-NOP parameters) is present. +static std::map> + CheckMap{ + { + // *always* at least one assertion + std::make_tuple(0U, 1U, 0U, 0U), + {{ + std::make_pair(0U, 1U), std::make_pair(9U, 1U), + std::make_pair(10U, 1U), std::make_pair(11U, 1U), + std::make_pair(19U, 1U), std::make_pair(20U, 1U), + std::make_pair(21U, 1U), std::make_pair(29U, 1U), + std::make_pair(30U, 1U), std::make_pair(31U, 1U), + std::make_pair(39U, 1U), std::make_pair(40U, 1U), + std::make_pair(41U, 1U), std::make_pair(49U, 1U), + std::make_pair(50U, 1U), std::make_pair(51U, 1U), + std::make_pair(59U, 1U), std::make_pair(60U, 1U), + std::make_pair(61U, 1U), std::make_pair(99U, 1U), + std::make_pair(100U, 1U), std::make_pair(101U, 1U), + std::make_pair(999U, 1U), std::make_pair(1000U, 1U), + std::make_pair(1001U, 1U), std::make_pair(9999U, 1U), + std::make_pair(10000U, 1U), std::make_pair(10001U, 1U), + }}, + }, + { + // same as the last one + // *always* at least one assertion + std::make_tuple(0U, 1U, 0U, 2U), + {{ + std::make_pair(0U, 1U), std::make_pair(9U, 1U), + std::make_pair(10U, 1U), std::make_pair(11U, 1U), + std::make_pair(19U, 1U), std::make_pair(20U, 1U), + std::make_pair(21U, 1U), std::make_pair(29U, 1U), + std::make_pair(30U, 1U), std::make_pair(31U, 1U), + std::make_pair(39U, 1U), std::make_pair(40U, 1U), + std::make_pair(41U, 1U), std::make_pair(49U, 1U), + std::make_pair(50U, 1U), std::make_pair(51U, 1U), + std::make_pair(59U, 1U), std::make_pair(60U, 1U), + std::make_pair(61U, 1U), std::make_pair(99U, 1U), + std::make_pair(100U, 1U), std::make_pair(101U, 1U), + std::make_pair(999U, 1U), std::make_pair(1000U, 1U), + std::make_pair(1001U, 1U), std::make_pair(9999U, 1U), + std::make_pair(10000U, 1U), std::make_pair(10001U, 1U), + }}, + }, + { + // same as the last one + // *always* at least one assertion + std::make_tuple(0U, 1U, 20U, 0U), + {{ + std::make_pair(0U, 1U), std::make_pair(9U, 1U), + std::make_pair(10U, 1U), std::make_pair(11U, 1U), + std::make_pair(19U, 1U), std::make_pair(20U, 1U), + std::make_pair(21U, 1U), std::make_pair(29U, 1U), + std::make_pair(30U, 1U), std::make_pair(31U, 1U), + std::make_pair(39U, 1U), std::make_pair(40U, 1U), + std::make_pair(41U, 1U), std::make_pair(49U, 1U), + std::make_pair(50U, 1U), std::make_pair(51U, 1U), + std::make_pair(59U, 1U), std::make_pair(60U, 1U), + std::make_pair(61U, 1U), std::make_pair(99U, 1U), + std::make_pair(100U, 1U), std::make_pair(101U, 1U), + std::make_pair(999U, 1U), std::make_pair(1000U, 1U), + std::make_pair(1001U, 1U), std::make_pair(9999U, 1U), + std::make_pair(10000U, 1U), std::make_pair(10001U, 1U), + }}, + }, + + { + // if no less than 10 lines, at least one assertion + std::make_tuple(10U, 1U, 0U, 0U), + {{ + std::make_pair(0U, 0U), std::make_pair(9U, 0U), + std::make_pair(10U, 1U), std::make_pair(11U, 1U), + std::make_pair(19U, 1U), std::make_pair(20U, 1U), + std::make_pair(21U, 1U), std::make_pair(29U, 1U), + std::make_pair(30U, 1U), std::make_pair(31U, 1U), + std::make_pair(39U, 1U), std::make_pair(40U, 1U), + std::make_pair(41U, 1U), std::make_pair(49U, 1U), + std::make_pair(50U, 1U), std::make_pair(51U, 1U), + std::make_pair(59U, 1U), std::make_pair(60U, 1U), + std::make_pair(61U, 1U), std::make_pair(99U, 1U), + std::make_pair(100U, 1U), std::make_pair(101U, 1U), + std::make_pair(999U, 1U), std::make_pair(1000U, 1U), + std::make_pair(1001U, 1U), std::make_pair(9999U, 1U), + std::make_pair(10000U, 1U), std::make_pair(10001U, 1U), + }}, + }, + { + // same as the last one + // if no less than 10 lines, at least one assertion + std::make_tuple(10U, 1U, 0U, 2U), + {{ + std::make_pair(0U, 0U), std::make_pair(9U, 0U), + std::make_pair(10U, 1U), std::make_pair(11U, 1U), + std::make_pair(19U, 1U), std::make_pair(20U, 1U), + std::make_pair(21U, 1U), std::make_pair(29U, 1U), + std::make_pair(30U, 1U), std::make_pair(31U, 1U), + std::make_pair(39U, 1U), std::make_pair(40U, 1U), + std::make_pair(41U, 1U), std::make_pair(49U, 1U), + std::make_pair(50U, 1U), std::make_pair(51U, 1U), + std::make_pair(59U, 1U), std::make_pair(60U, 1U), + std::make_pair(61U, 1U), std::make_pair(99U, 1U), + std::make_pair(100U, 1U), std::make_pair(101U, 1U), + std::make_pair(999U, 1U), std::make_pair(1000U, 1U), + std::make_pair(1001U, 1U), std::make_pair(9999U, 1U), + std::make_pair(10000U, 1U), std::make_pair(10001U, 1U), + }}, + }, + { + // same as the last one + // if no less than 10 lines, at least one assertion + std::make_tuple(10U, 1U, 20U, 0U), + {{ + std::make_pair(0U, 0U), std::make_pair(9U, 0U), + std::make_pair(10U, 1U), std::make_pair(11U, 1U), + std::make_pair(19U, 1U), std::make_pair(20U, 1U), + std::make_pair(21U, 1U), std::make_pair(29U, 1U), + std::make_pair(30U, 1U), std::make_pair(31U, 1U), + std::make_pair(39U, 1U), std::make_pair(40U, 1U), + std::make_pair(41U, 1U), std::make_pair(49U, 1U), + std::make_pair(50U, 1U), std::make_pair(51U, 1U), + std::make_pair(59U, 1U), std::make_pair(60U, 1U), + std::make_pair(61U, 1U), std::make_pair(99U, 1U), + std::make_pair(100U, 1U), std::make_pair(101U, 1U), + std::make_pair(999U, 1U), std::make_pair(1000U, 1U), + std::make_pair(1001U, 1U), std::make_pair(9999U, 1U), + std::make_pair(10000U, 1U), std::make_pair(10001U, 1U), + }}, + }, + + { + // if more than 10 lines, 2 assertions per each 20 extra lines + std::make_tuple(10U, 0U, 20U, 2U), + {{ + std::make_pair(0U, 0U), std::make_pair(9U, 0U), + std::make_pair(10U, 0U), std::make_pair(11U, 0U), + std::make_pair(19U, 0U), std::make_pair(20U, 0U), + std::make_pair(21U, 0U), std::make_pair(29U, 0U), + std::make_pair(30U, 2U), std::make_pair(31U, 2U), + std::make_pair(39U, 2U), std::make_pair(40U, 2U), + std::make_pair(41U, 2U), std::make_pair(49U, 2U), + std::make_pair(50U, 4U), std::make_pair(51U, 4U), + std::make_pair(59U, 4U), std::make_pair(60U, 4U), + std::make_pair(61U, 4U), std::make_pair(99U, 8U), + std::make_pair(100U, 8U), std::make_pair(101U, 8U), + std::make_pair(999U, 98U), std::make_pair(1000U, 98U), + std::make_pair(1001U, 98U), std::make_pair(9999U, 998U), + std::make_pair(10000U, 998U), std::make_pair(10001U, 998U), + }}, + }, + + { + // if no less than 10 lines, at least one assertion + // plus, 2 assertions per each 20 extra lines + std::make_tuple(10U, 1U, 20U, 2U), + {{ + std::make_pair(0U, 0U), std::make_pair(9U, 0U), + std::make_pair(10U, 1U), std::make_pair(11U, 1U), + std::make_pair(19U, 1U), std::make_pair(20U, 1U), + std::make_pair(21U, 1U), std::make_pair(29U, 1U), + std::make_pair(30U, 3U), std::make_pair(31U, 3U), + std::make_pair(39U, 3U), std::make_pair(40U, 3U), + std::make_pair(41U, 3U), std::make_pair(49U, 3U), + std::make_pair(50U, 5U), std::make_pair(51U, 5U), + std::make_pair(59U, 5U), std::make_pair(60U, 5U), + std::make_pair(61U, 5U), std::make_pair(99U, 9U), + std::make_pair(100U, 9U), std::make_pair(101U, 9U), + std::make_pair(999U, 99U), std::make_pair(1000U, 99U), + std::make_pair(1001U, 99U), std::make_pair(9999U, 999U), + std::make_pair(10000U, 999U), std::make_pair(10001U, 999U), + }}, + }, + + { + // 2 assertions per each 20 extra lines + std::make_tuple(0U, 0U, 20U, 2U), + {{ + std::make_pair(0U, 0U), std::make_pair(9U, 0U), + std::make_pair(10U, 0U), std::make_pair(11U, 0U), + std::make_pair(19U, 0U), std::make_pair(20U, 2U), + std::make_pair(21U, 2U), std::make_pair(29U, 2U), + std::make_pair(30U, 2U), std::make_pair(31U, 2U), + std::make_pair(39U, 2U), std::make_pair(40U, 4U), + std::make_pair(41U, 4U), std::make_pair(49U, 4U), + std::make_pair(50U, 4U), std::make_pair(51U, 4U), + std::make_pair(59U, 4U), std::make_pair(60U, 6U), + std::make_pair(61U, 6U), std::make_pair(99U, 8U), + std::make_pair(100U, 10U), std::make_pair(101U, 10U), + std::make_pair(999U, 98U), std::make_pair(1000U, 100U), + std::make_pair(1001U, 100U), std::make_pair(9999U, 998U), + std::make_pair(10000U, 1000U), std::make_pair(10001U, 1000U), + }}, + }, + + { + // at least one assertion + // plus, 2 assertions per each 20 extra lines + std::make_tuple(0U, 1U, 20U, 2U), + {{ + std::make_pair(0U, 1U), std::make_pair(9U, 1U), + std::make_pair(10U, 1U), std::make_pair(11U, 1U), + std::make_pair(19U, 1U), std::make_pair(20U, 3U), + std::make_pair(21U, 3U), std::make_pair(29U, 3U), + std::make_pair(30U, 3U), std::make_pair(31U, 3U), + std::make_pair(39U, 3U), std::make_pair(40U, 5U), + std::make_pair(41U, 5U), std::make_pair(49U, 5U), + std::make_pair(50U, 5U), std::make_pair(51U, 5U), + std::make_pair(59U, 5U), std::make_pair(60U, 7U), + std::make_pair(61U, 7U), std::make_pair(99U, 9U), + std::make_pair(100U, 11U), std::make_pair(101U, 11U), + std::make_pair(999U, 99U), std::make_pair(1000U, 101U), + std::make_pair(1001U, 101U), std::make_pair(9999U, 999U), + std::make_pair(10000U, 1001U), std::make_pair(10001U, 1001U), + }}, + }, + }; + +TEST_P(AssertionCountCheckTest, Test) { + // if for some huge number of lines it says zero assertions are expected, + // then these params are NOP + + // this is indeed a magic number + // any value sufficiently larger than 0 should be fine. + + if (C->expectedAssertionsMin(1000) == 0) { + EXPECT_TRUE(C->isNOP()); + EXPECT_TRUE(CheckMap.find(GetParam()) == CheckMap.end()); + } else { + EXPECT_FALSE(C->isNOP()); + + const auto DataPoints = CheckMap.find(GetParam()); + ASSERT_TRUE(DataPoints != CheckMap.end()); + // yes, this is an ASSERT. bad things would happen if this EXPECT would fail + + for (const auto &CheckPoint : DataPoints->second) + EXPECT_EQ(C->expectedAssertionsMin(CheckPoint.first), CheckPoint.second) + << " Where Lines: " << CheckPoint.first; + } +} + +} // anonymous namespace +} // namespace test +} // namespace tidy +} // namespace clang