[clang-tools-extra] ace6440 - [clang-tidy] Implement readability-function-cognitive-complexity check
Roman Lebedev via cfe-commits
cfe-commits at lists.llvm.org
Fri Oct 2 14:31:01 PDT 2020
Author: Roman Lebedev
Date: 2020-10-03T00:27:13+03:00
New Revision: ace644030e67506114d3ac9a221cf8eb5d10159c
URL: https://github.com/llvm/llvm-project/commit/ace644030e67506114d3ac9a221cf8eb5d10159c
DIFF: https://github.com/llvm/llvm-project/commit/ace644030e67506114d3ac9a221cf8eb5d10159c.diff
LOG: [clang-tidy] Implement readability-function-cognitive-complexity check
Currently, there is basically just one clang-tidy check to impose
some sanity limits on functions - `clang-tidy-readability-function-size`.
It is nice, allows to limit line count, total number of statements,
number of branches, number of function parameters (not counting
implicit `this`), nesting level.
However, those are simple generic metrics. It is still trivially possible
to write a function, which does not violate any of these metrics,
yet is still rather unreadable.
Thus, some additional, slightly more complicated metric is needed.
There is a well-known [[ https://en.wikipedia.org/wiki/Cyclomatic_complexity | Cyclomatic complexity]], but certainly has its downsides.
And there is a [[ https://www.sonarsource.com/docs/CognitiveComplexity.pdf | COGNITIVE COMPLEXITY by SonarSource ]], which is available for opensource on https://sonarcloud.io/.
This check checks function Cognitive Complexity metric, and flags
the functions with Cognitive Complexity exceeding the configured limit.
The default limit is `25`, same as in 'upstream'.
The metric is implemented as per [[ https://www.sonarsource.com/docs/CognitiveComplexity.pdf | COGNITIVE COMPLEXITY by SonarSource ]] specification version 1.2 (19 April 2017), with two notable exceptions:
* `preprocessor conditionals` (`#ifdef`, `#if`, `#elif`, `#else`,
`#endif`) are not accounted for.
Could be done. Currently, upstream does not account for them either.
* `each method in a recursion cycle` is not accounted for.
It can't be fully implemented, because cross-translational-unit
analysis would be needed, which is not possible in clang-tidy.
Thus, at least right now, i completely avoided implementing it.
There are some further possible improvements:
* Are GNU statement expressions (`BinaryConditionalOperator`) really free?
They should probably cause nesting level increase,
and complexity level increase when they are nested within eachother.
* Microsoft SEH support
* ???
Reviewed By: aaron.ballman, JonasToth, lattner
Differential Revision: https://reviews.llvm.org/D36836
Added:
clang-tools-extra/clang-tidy/readability/FunctionCognitiveComplexityCheck.cpp
clang-tools-extra/clang-tidy/readability/FunctionCognitiveComplexityCheck.h
clang-tools-extra/docs/clang-tidy/checks/readability-function-cognitive-complexity.rst
clang-tools-extra/test/clang-tidy/checkers/readability-function-cognitive-complexity.cpp
Modified:
clang-tools-extra/clang-tidy/readability/CMakeLists.txt
clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
clang-tools-extra/docs/ReleaseNotes.rst
clang-tools-extra/docs/clang-tidy/checks/list.rst
Removed:
################################################################################
diff --git a/clang-tools-extra/clang-tidy/readability/CMakeLists.txt b/clang-tools-extra/clang-tidy/readability/CMakeLists.txt
index 4539ab177ced..ecf37b5b9157 100644
--- a/clang-tools-extra/clang-tidy/readability/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/readability/CMakeLists.txt
@@ -12,6 +12,7 @@ add_clang_library(clangTidyReadabilityModule
DeleteNullPointerCheck.cpp
DeletedDefaultCheck.cpp
ElseAfterReturnCheck.cpp
+ FunctionCognitiveComplexityCheck.cpp
FunctionSizeCheck.cpp
IdentifierNamingCheck.cpp
ImplicitBoolConversionCheck.cpp
diff --git a/clang-tools-extra/clang-tidy/readability/FunctionCognitiveComplexityCheck.cpp b/clang-tools-extra/clang-tidy/readability/FunctionCognitiveComplexityCheck.cpp
new file mode 100644
index 000000000000..548aec7543ac
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/readability/FunctionCognitiveComplexityCheck.cpp
@@ -0,0 +1,542 @@
+//===--- FunctionCognitiveComplexityCheck.cpp - clang-tidy ------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "FunctionCognitiveComplexityCheck.h"
+#include "../ClangTidyDiagnosticConsumer.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclBase.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/AST/Stmt.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/ASTMatchers/ASTMatchersInternal.h"
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/DiagnosticIDs.h"
+#include "clang/Basic/LLVM.h"
+#include "clang/Basic/SourceLocation.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/ErrorHandling.h"
+#include <array>
+#include <cassert>
+#include <stack>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace readability {
+namespace {
+
+struct CognitiveComplexity final {
+ // Any increment is based on some combination of reasons.
+ // For details you can look at the Specification at
+ // https://www.sonarsource.com/docs/CognitiveComplexity.pdf
+ // or user-facing docs at
+ // http://clang.llvm.org/extra/clang-tidy/checks/readability-function-cognitive-complexity.html
+ // Here are all the possible reasons:
+ enum Criteria : uint8_t {
+ None = 0U,
+
+ // B1, increases cognitive complexity (by 1)
+ // What causes it:
+ // * if, else if, else, ConditionalOperator (not BinaryConditionalOperator)
+ // * SwitchStmt
+ // * ForStmt, CXXForRangeStmt
+ // * WhileStmt, DoStmt
+ // * CXXCatchStmt
+ // * GotoStmt, IndirectGotoStmt (but not BreakStmt, ContinueStmt)
+ // * sequences of binary logical operators (BinOpLAnd, BinOpLOr)
+ // * each method in a recursion cycle (not implemented)
+ Increment = 1U << 0,
+
+ // B2, increases current nesting level (by 1)
+ // What causes it:
+ // * if, else if, else, ConditionalOperator (not BinaryConditionalOperator)
+ // * SwitchStmt
+ // * ForStmt, CXXForRangeStmt
+ // * WhileStmt, DoStmt
+ // * CXXCatchStmt
+ // * nested CXXConstructor, CXXDestructor, CXXMethod (incl. C++11 Lambda)
+ // * GNU Statement Expression
+ // * Apple Block declaration
+ IncrementNesting = 1U << 1,
+
+ // B3, increases cognitive complexity by the current nesting level
+ // Applied before IncrementNesting
+ // What causes it:
+ // * IfStmt, ConditionalOperator (not BinaryConditionalOperator)
+ // * SwitchStmt
+ // * ForStmt, CXXForRangeStmt
+ // * WhileStmt, DoStmt
+ // * CXXCatchStmt
+ PenalizeNesting = 1U << 2,
+
+ All = Increment | PenalizeNesting | IncrementNesting,
+ };
+
+ // The helper struct used to record one increment occurrence, with all the
+ // details nessesary.
+ struct Detail {
+ const SourceLocation Loc; // What caused the increment?
+ const unsigned short Nesting; // How deeply nested is Loc located?
+ const Criteria C; // The criteria of the increment
+
+ Detail(SourceLocation SLoc, unsigned short CurrentNesting, Criteria Crit)
+ : Loc(SLoc), Nesting(CurrentNesting), C(Crit) {}
+
+ // To minimize the sizeof(Detail), we only store the minimal info there.
+ // This function is used to convert from the stored info into the usable
+ // information - what message to output, how much of an increment did this
+ // occurrence actually result in.
+ std::pair<unsigned, unsigned short> process() const {
+ assert(C != Criteria::None && "invalid criteria");
+
+ unsigned MsgId; // The id of the message to output.
+ unsigned short Increment; // How much of an increment?
+
+ if (C == Criteria::All) {
+ Increment = 1 + Nesting;
+ MsgId = 0;
+ } else if (C == (Criteria::Increment | Criteria::IncrementNesting)) {
+ Increment = 1;
+ MsgId = 1;
+ } else if (C == Criteria::Increment) {
+ Increment = 1;
+ MsgId = 2;
+ } else if (C == Criteria::IncrementNesting) {
+ Increment = 0; // Unused in this message.
+ MsgId = 3;
+ } else
+ llvm_unreachable("should not get to here.");
+
+ return std::make_pair(MsgId, Increment);
+ }
+ };
+
+ // Limit of 25 is the "upstream"'s default.
+ static constexpr unsigned DefaultLimit = 25U;
+
+ // Based on the publicly-avaliable numbers for some big open-source projects
+ // https://sonarcloud.io/projects?languages=c%2Ccpp&size=5 we can estimate:
+ // value ~20 would result in no allocs for 98% of functions, ~12 for 96%, ~10
+ // for 91%, ~8 for 88%, ~6 for 84%, ~4 for 77%, ~2 for 64%, and ~1 for 37%.
+ static_assert(sizeof(Detail) <= 8,
+ "Since we use SmallVector to minimize the amount of "
+ "allocations, we also need to consider the price we pay for "
+ "that in terms of stack usage. "
+ "Thus, it is good to minimize the size of the Detail struct.");
+ SmallVector<Detail, DefaultLimit> Details; // 25 elements is 200 bytes.
+ // Yes, 25 is a magic number. This is the seemingly-sane default for the
+ // upper limit for function cognitive complexity. Thus it would make sense
+ // to avoid allocations for any function that does not violate the limit.
+
+ // The grand total Cognitive Complexity of the function.
+ unsigned Total = 0;
+
+ // The function used to store new increment, calculate the total complexity.
+ void account(SourceLocation Loc, unsigned short Nesting, Criteria C);
+};
+
+// All the possible messages that can be output. The choice of the message
+// to use is based of the combination of the CognitiveComplexity::Criteria.
+// It would be nice to have it in CognitiveComplexity struct, but then it is
+// not static.
+static const std::array<const StringRef, 4> Msgs = {{
+ // B1 + B2 + B3
+ "+%0, including nesting penalty of %1, nesting level increased to %2",
+
+ // B1 + B2
+ "+%0, nesting level increased to %2",
+
+ // B1
+ "+%0",
+
+ // B2
+ "nesting level increased to %2",
+}};
+
+// Criteria is a bitset, thus a few helpers are needed.
+CognitiveComplexity::Criteria operator|(CognitiveComplexity::Criteria LHS,
+ CognitiveComplexity::Criteria RHS) {
+ return static_cast<CognitiveComplexity::Criteria>(
+ static_cast<std::underlying_type<CognitiveComplexity::Criteria>::type>(
+ LHS) |
+ static_cast<std::underlying_type<CognitiveComplexity::Criteria>::type>(
+ RHS));
+}
+CognitiveComplexity::Criteria operator&(CognitiveComplexity::Criteria LHS,
+ CognitiveComplexity::Criteria RHS) {
+ return static_cast<CognitiveComplexity::Criteria>(
+ static_cast<std::underlying_type<CognitiveComplexity::Criteria>::type>(
+ LHS) &
+ static_cast<std::underlying_type<CognitiveComplexity::Criteria>::type>(
+ RHS));
+}
+CognitiveComplexity::Criteria &operator|=(CognitiveComplexity::Criteria &LHS,
+ CognitiveComplexity::Criteria RHS) {
+ LHS = operator|(LHS, RHS);
+ return LHS;
+}
+CognitiveComplexity::Criteria &operator&=(CognitiveComplexity::Criteria &LHS,
+ CognitiveComplexity::Criteria RHS) {
+ LHS = operator&(LHS, RHS);
+ return LHS;
+}
+
+void CognitiveComplexity::account(SourceLocation Loc, unsigned short Nesting,
+ Criteria C) {
+ C &= Criteria::All;
+ assert(C != Criteria::None && "invalid criteria");
+
+ Details.emplace_back(Loc, Nesting, C);
+ const Detail &D = Details.back();
+
+ unsigned MsgId;
+ unsigned short Increase;
+ std::tie(MsgId, Increase) = D.process();
+
+ Total += Increase;
+}
+
+class FunctionASTVisitor final
+ : public RecursiveASTVisitor<FunctionASTVisitor> {
+ using Base = RecursiveASTVisitor<FunctionASTVisitor>;
+
+ // The current nesting level (increased by Criteria::IncrementNesting).
+ unsigned short CurrentNestingLevel = 0;
+
+ // Used to efficiently know the last type of the binary sequence operator
+ // that was encountered. It would make sense for the function call to start
+ // the new sequence, thus it is a stack.
+ using OBO = Optional<BinaryOperator::Opcode>;
+ std::stack<OBO, SmallVector<OBO, 4>> BinaryOperatorsStack;
+
+public:
+ bool TraverseStmtWithIncreasedNestingLevel(Stmt *Node) {
+ ++CurrentNestingLevel;
+ bool ShouldContinue = Base::TraverseStmt(Node);
+ --CurrentNestingLevel;
+ return ShouldContinue;
+ }
+
+ bool TraverseDeclWithIncreasedNestingLevel(Decl *Node) {
+ ++CurrentNestingLevel;
+ bool ShouldContinue = Base::TraverseDecl(Node);
+ --CurrentNestingLevel;
+ return ShouldContinue;
+ }
+
+ bool TraverseIfStmt(IfStmt *Node, bool InElseIf = false) {
+ if (!Node)
+ return Base::TraverseIfStmt(Node);
+
+ {
+ CognitiveComplexity::Criteria Reasons;
+
+ Reasons = CognitiveComplexity::Criteria::None;
+
+ // "If" increases cognitive complexity.
+ Reasons |= CognitiveComplexity::Criteria::Increment;
+ // "If" increases nesting level.
+ Reasons |= CognitiveComplexity::Criteria::IncrementNesting;
+
+ if (!InElseIf) {
+ // "If" receives a nesting increment commensurate with it's nested
+ // depth, if it is not part of "else if".
+ Reasons |= CognitiveComplexity::Criteria::PenalizeNesting;
+ }
+
+ CC.account(Node->getIfLoc(), CurrentNestingLevel, Reasons);
+ }
+
+ // If this IfStmt is *NOT* "else if", then only the body (i.e. "Then" and
+ // "Else") is traversed with increased Nesting level.
+ // However if this IfStmt *IS* "else if", then Nesting level is increased
+ // for the whole IfStmt (i.e. for "Init", "Cond", "Then" and "Else").
+
+ if (!InElseIf) {
+ if (!TraverseStmt(Node->getInit()))
+ return false;
+
+ if (!TraverseStmt(Node->getCond()))
+ return false;
+ } else {
+ if (!TraverseStmtWithIncreasedNestingLevel(Node->getInit()))
+ return false;
+
+ if (!TraverseStmtWithIncreasedNestingLevel(Node->getCond()))
+ return false;
+ }
+
+ // "Then" always increases nesting level.
+ if (!TraverseStmtWithIncreasedNestingLevel(Node->getThen()))
+ return false;
+
+ if (!Node->getElse())
+ return true;
+
+ if (auto *E = dyn_cast<IfStmt>(Node->getElse()))
+ return TraverseIfStmt(E, true);
+
+ {
+ CognitiveComplexity::Criteria Reasons;
+
+ Reasons = CognitiveComplexity::Criteria::None;
+
+ // "Else" increases cognitive complexity.
+ Reasons |= CognitiveComplexity::Criteria::Increment;
+ // "Else" increases nesting level.
+ Reasons |= CognitiveComplexity::Criteria::IncrementNesting;
+ // "Else" DOES NOT receive a nesting increment commensurate with it's
+ // nested depth.
+
+ CC.account(Node->getElseLoc(), CurrentNestingLevel, Reasons);
+ }
+
+ // "Else" always increases nesting level.
+ return TraverseStmtWithIncreasedNestingLevel(Node->getElse());
+ }
+
+// The currently-being-processed stack entry, which is always the top.
+#define CurrentBinaryOperator BinaryOperatorsStack.top()
+
+ // In a sequence of binary logical operators, if the new operator is
diff erent
+ // from the previous one, then the cognitive complexity is increased.
+ bool TraverseBinaryOperator(BinaryOperator *Op) {
+ if (!Op || !Op->isLogicalOp())
+ return Base::TraverseBinaryOperator(Op);
+
+ // Make sure that there is always at least one frame in the stack.
+ if (BinaryOperatorsStack.empty())
+ BinaryOperatorsStack.emplace();
+
+ // If this is the first binary operator that we are processing, or the
+ // previous binary operator was
diff erent, there is an increment.
+ if (!CurrentBinaryOperator || Op->getOpcode() != CurrentBinaryOperator)
+ CC.account(Op->getOperatorLoc(), CurrentNestingLevel,
+ CognitiveComplexity::Criteria::Increment);
+
+ // We might encounter a function call, which starts a new sequence, thus
+ // we need to save the current previous binary operator.
+ const Optional<BinaryOperator::Opcode> BinOpCopy(CurrentBinaryOperator);
+
+ // Record the operator that we are currently processing and traverse it.
+ CurrentBinaryOperator = Op->getOpcode();
+ bool ShouldContinue = Base::TraverseBinaryOperator(Op);
+
+ // And restore the previous binary operator, which might be nonexistent.
+ CurrentBinaryOperator = BinOpCopy;
+
+ return ShouldContinue;
+ }
+
+ // It would make sense for the function call to start the new binary
+ // operator sequence, thus let's make sure that it creates a new stack frame.
+ bool TraverseCallExpr(CallExpr *Node) {
+ // If we are not currently processing any binary operator sequence, then
+ // no Node-handling is needed.
+ if (!Node || BinaryOperatorsStack.empty() || !CurrentBinaryOperator)
+ return Base::TraverseCallExpr(Node);
+
+ // Else, do add [uninitialized] frame to the stack, and traverse call.
+ BinaryOperatorsStack.emplace();
+ bool ShouldContinue = Base::TraverseCallExpr(Node);
+ // And remove the top frame.
+ BinaryOperatorsStack.pop();
+
+ return ShouldContinue;
+ }
+
+#undef CurrentBinaryOperator
+
+ bool TraverseStmt(Stmt *Node) {
+ if (!Node)
+ return Base::TraverseStmt(Node);
+
+ // Three following switch()'es have huge duplication, but it is better to
+ // keep them separate, to simplify comparing them with the Specification.
+
+ CognitiveComplexity::Criteria Reasons = CognitiveComplexity::Criteria::None;
+ SourceLocation Location = Node->getBeginLoc();
+
+ // B1. Increments
+ // There is an increment for each of the following:
+ switch (Node->getStmtClass()) {
+ // if, else if, else are handled in TraverseIfStmt(),
+ // FIXME: "each method in a recursion cycle" Increment is not implemented.
+ case Stmt::ConditionalOperatorClass:
+ case Stmt::SwitchStmtClass:
+ case Stmt::ForStmtClass:
+ case Stmt::CXXForRangeStmtClass:
+ case Stmt::WhileStmtClass:
+ case Stmt::DoStmtClass:
+ case Stmt::CXXCatchStmtClass:
+ case Stmt::GotoStmtClass:
+ case Stmt::IndirectGotoStmtClass:
+ Reasons |= CognitiveComplexity::Criteria::Increment;
+ break;
+ default:
+ // break LABEL, continue LABEL increase cognitive complexity,
+ // but they are not supported in C++ or C.
+ // Regular break/continue do not increase cognitive complexity.
+ break;
+ }
+
+ // B2. Nesting level
+ // The following structures increment the nesting level:
+ switch (Node->getStmtClass()) {
+ // if, else if, else are handled in TraverseIfStmt(),
+ // Nested methods and such are handled in TraverseDecl.
+ case Stmt::ConditionalOperatorClass:
+ case Stmt::SwitchStmtClass:
+ case Stmt::ForStmtClass:
+ case Stmt::CXXForRangeStmtClass:
+ case Stmt::WhileStmtClass:
+ case Stmt::DoStmtClass:
+ case Stmt::CXXCatchStmtClass:
+ case Stmt::LambdaExprClass:
+ case Stmt::StmtExprClass:
+ Reasons |= CognitiveComplexity::Criteria::IncrementNesting;
+ break;
+ default:
+ break;
+ }
+
+ // B3. Nesting increments
+ // The following structures receive a nesting increment
+ // commensurate with their nested depth inside B2 structures:
+ switch (Node->getStmtClass()) {
+ // if, else if, else are handled in TraverseIfStmt().
+ case Stmt::ConditionalOperatorClass:
+ case Stmt::SwitchStmtClass:
+ case Stmt::ForStmtClass:
+ case Stmt::CXXForRangeStmtClass:
+ case Stmt::WhileStmtClass:
+ case Stmt::DoStmtClass:
+ case Stmt::CXXCatchStmtClass:
+ Reasons |= CognitiveComplexity::Criteria::PenalizeNesting;
+ break;
+ default:
+ break;
+ }
+
+ if (Node->getStmtClass() == Stmt::ConditionalOperatorClass) {
+ // A little beautification.
+ // For conditional operator "cond ? true : false" point at the "?"
+ // symbol.
+ ConditionalOperator *COp = dyn_cast<ConditionalOperator>(Node);
+ Location = COp->getQuestionLoc();
+ }
+
+ // If we have found any reasons, let's account it.
+ if (Reasons & CognitiveComplexity::Criteria::All)
+ CC.account(Location, CurrentNestingLevel, Reasons);
+
+ // Did we decide that the nesting level should be increased?
+ if (!(Reasons & CognitiveComplexity::Criteria::IncrementNesting))
+ return Base::TraverseStmt(Node);
+
+ return TraverseStmtWithIncreasedNestingLevel(Node);
+ }
+
+ // The parameter MainAnalyzedFunction is needed to
diff erentiate between the
+ // cases where TraverseDecl() is the entry point from
+ // FunctionCognitiveComplexityCheck::check() and the cases where it was called
+ // from the FunctionASTVisitor itself. Explanation: if we get a function
+ // definition (e.g. constructor, destructor, method), the Cognitive Complexity
+ // specification states that the Nesting level shall be increased. But if this
+ // function is the entry point, then the Nesting level should not be
+ // increased. Thus that parameter is there and is used to fall-through
+ // directly to traversing if this is the main function that is being analyzed.
+ bool TraverseDecl(Decl *Node, bool MainAnalyzedFunction = false) {
+ if (!Node || MainAnalyzedFunction)
+ return Base::TraverseDecl(Node);
+
+ // B2. Nesting level
+ // The following structures increment the nesting level:
+ switch (Node->getKind()) {
+ case Decl::Function:
+ case Decl::CXXMethod:
+ case Decl::CXXConstructor:
+ case Decl::CXXDestructor:
+ case Decl::Block:
+ break;
+ default:
+ // If this is something else, we use early return!
+ return Base::TraverseDecl(Node);
+ break;
+ }
+
+ CC.account(Node->getBeginLoc(), CurrentNestingLevel,
+ CognitiveComplexity::Criteria::IncrementNesting);
+
+ return TraverseDeclWithIncreasedNestingLevel(Node);
+ }
+
+ CognitiveComplexity CC;
+};
+
+} // namespace
+
+FunctionCognitiveComplexityCheck::FunctionCognitiveComplexityCheck(
+ StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ Threshold(Options.get("Threshold", CognitiveComplexity::DefaultLimit)) {}
+
+void FunctionCognitiveComplexityCheck::storeOptions(
+ ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "Threshold", Threshold);
+}
+
+void FunctionCognitiveComplexityCheck::registerMatchers(MatchFinder *Finder) {
+ Finder->addMatcher(
+ functionDecl(
+ allOf(isDefinition(), unless(anyOf(isDefaulted(), isDeleted(),
+ isImplicit(), isInstantiated()))))
+ .bind("func"),
+ this);
+}
+
+void FunctionCognitiveComplexityCheck::check(
+ const MatchFinder::MatchResult &Result) {
+ const auto *Func = Result.Nodes.getNodeAs<FunctionDecl>("func");
+ assert(Func->hasBody() && "The matchers should only match the functions that "
+ "have user-provided body.");
+
+ FunctionASTVisitor Visitor;
+ Visitor.TraverseDecl(const_cast<FunctionDecl *>(Func), true);
+
+ if (Visitor.CC.Total <= Threshold)
+ return;
+
+ diag(Func->getLocation(),
+ "function %0 has cognitive complexity of %1 (threshold %2)")
+ << Func << Visitor.CC.Total << Threshold;
+
+ // Output all the basic increments of complexity.
+ for (const auto &Detail : Visitor.CC.Details) {
+ unsigned MsgId; // The id of the message to output.
+ unsigned short Increase; // How much of an increment?
+ std::tie(MsgId, Increase) = Detail.process();
+ assert(MsgId < Msgs.size() && "MsgId should always be valid");
+ // Increase, on the other hand, can be 0.
+
+ diag(Detail.Loc, Msgs[MsgId], DiagnosticIDs::Note)
+ << Increase << Detail.Nesting << 1 + Detail.Nesting;
+ }
+}
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tools-extra/clang-tidy/readability/FunctionCognitiveComplexityCheck.h b/clang-tools-extra/clang-tidy/readability/FunctionCognitiveComplexityCheck.h
new file mode 100644
index 000000000000..96b6723d2a6a
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/readability/FunctionCognitiveComplexityCheck.h
@@ -0,0 +1,43 @@
+//===--- FunctionCognitiveComplexityCheck.h - clang-tidy --------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_FUNCTIONCOGNITIVECOMPLEXITYCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_FUNCTIONCOGNITIVECOMPLEXITYCHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+/// Checks function Cognitive Complexity metric.
+///
+/// There is only one configuration option:
+///
+/// * `Threshold` - flag functions with Cognitive Complexity exceeding
+/// this number. The default is `25`.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/readability-function-cognitive-complexity.html
+class FunctionCognitiveComplexityCheck : public ClangTidyCheck {
+public:
+ FunctionCognitiveComplexityCheck(StringRef Name, ClangTidyContext *Context);
+
+ void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+ const unsigned Threshold;
+};
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_FUNCTIONCOGNITIVECOMPLEXITYCHECK_H
diff --git a/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp b/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
index 5ff5e2022839..bbd2e24e503b 100644
--- a/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
@@ -17,6 +17,7 @@
#include "DeleteNullPointerCheck.h"
#include "DeletedDefaultCheck.h"
#include "ElseAfterReturnCheck.h"
+#include "FunctionCognitiveComplexityCheck.h"
#include "FunctionSizeCheck.h"
#include "IdentifierNamingCheck.h"
#include "ImplicitBoolConversionCheck.h"
@@ -70,6 +71,8 @@ class ReadabilityModule : public ClangTidyModule {
"readability-deleted-default");
CheckFactories.registerCheck<ElseAfterReturnCheck>(
"readability-else-after-return");
+ CheckFactories.registerCheck<FunctionCognitiveComplexityCheck>(
+ "readability-function-cognitive-complexity");
CheckFactories.registerCheck<FunctionSizeCheck>(
"readability-function-size");
CheckFactories.registerCheck<IdentifierNamingCheck>(
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index da4b57f39a78..ac4802e6d498 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -106,6 +106,11 @@ New checks
Finds condition variables in nested ``if`` statements that were also checked
in the outer ``if`` statement and were not changed.
+- New :doc:`readability-function-cognitive-complexity
+ <clang-tidy/checks/readability-function-cognitive-complexity>` check.
+
+ Flags functions with Cognitive Complexity metric exceeding the configured limit.
+
Changes in existing checks
^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst
index 378e92cb66dd..ec0e200b91d1 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -276,6 +276,7 @@ Clang-Tidy Checks
`readability-delete-null-pointer <readability-delete-null-pointer.html>`_, "Yes"
`readability-deleted-default <readability-deleted-default.html>`_,
`readability-else-after-return <readability-else-after-return.html>`_, "Yes"
+ `readability-function-cognitive-complexity <readability-function-cognitive-complexity.html>`_,
`readability-function-size <readability-function-size.html>`_,
`readability-identifier-naming <readability-identifier-naming.html>`_, "Yes"
`readability-implicit-bool-conversion <readability-implicit-bool-conversion.html>`_, "Yes"
diff --git a/clang-tools-extra/docs/clang-tidy/checks/readability-function-cognitive-complexity.rst b/clang-tools-extra/docs/clang-tidy/checks/readability-function-cognitive-complexity.rst
new file mode 100644
index 000000000000..b863357a2132
--- /dev/null
+++ b/clang-tools-extra/docs/clang-tidy/checks/readability-function-cognitive-complexity.rst
@@ -0,0 +1,146 @@
+.. title:: clang-tidy - readability-function-cognitive-complexity
+
+readability-function-cognitive-complexity
+=========================================
+
+Checks function Cognitive Complexity metric.
+
+The metric is implemented as per the `COGNITIVE COMPLEXITY by SonarSource
+<https://www.sonarsource.com/docs/CognitiveComplexity.pdf>`_ specification
+version 1.2 (19 April 2017).
+
+Options
+-------
+
+.. option:: Threshold
+
+ Flag functions with Cognitive Complexity exceeding this number.
+ The default is `25`.
+
+Building blocks
+---------------
+
+There are three basic building blocks of a Cognitive Complexity metric:
+
+Increment
+^^^^^^^^^
+
+The following structures increase the function's Cognitive Complexity metric
+(by `1`):
+
+* Conditional operators:
+
+ - ``if()``
+ - ``else if()``
+ - ``else``
+ - ``cond ? true : false``
+
+* ``switch()``
+* Loops:
+
+ - ``for()``
+ - C++11 range-based ``for()``
+ - ``while()``
+ - ``do while()``
+
+* ``catch ()``
+* ``goto LABEL``, ``goto *(&&LABEL))``,
+* sequences of binary logical operators:
+
+ - ``boolean1 || boolean2``
+ - ``boolean1 && boolean2``
+
+Nesting level
+^^^^^^^^^^^^^
+
+While by itself the nesting level not change the function's Cognitive Complexity
+metric, it is tracked, and is used by the next, third building block.
+The following structures increase the nesting level (by `1`):
+
+* Conditional operators:
+
+ - ``if()``
+ - ``else if()``
+ - ``else``
+ - ``cond ? true : false``
+
+* ``switch()``
+* Loops:
+
+ - ``for()``
+ - C++11 range-based ``for()``
+ - ``while()``
+ - ``do while()``
+
+* ``catch ()``
+* Nested functions:
+
+ - C++11 Lambda
+ - Nested ``class``
+ - Nested ``struct``
+* GNU statement expression
+* Apple Block Declaration
+
+Nesting increment
+^^^^^^^^^^^^^^^^^
+
+This is where the previous basic building block, `Nesting level`_, matters.
+The following structures increase the function's Cognitive Complexity metric by
+the current `Nesting level`_:
+
+* Conditional operators:
+
+ - ``if()``
+ - ``cond ? true : false``
+
+* ``switch()``
+* Loops:
+
+ - ``for()``
+ - C++11 range-based ``for()``
+ - ``while()``
+ - ``do while()``
+
+* ``catch ()``
+
+Examples
+--------
+
+The simplest case. This function has Cognitive Complexity of `0`.
+
+.. code-block:: c++
+
+ void function0() {}
+
+Slightly better example. This function has Cognitive Complexity of `1`.
+
+.. code-block:: c++
+
+ int function1(bool var) {
+ if(var) // +1, nesting level +1
+ return 42;
+ return 0;
+ }
+
+Full example. This function has Cognitive Complexity of `3`.
+
+.. code-block:: c++
+
+ int function3(bool var1, bool var2) {
+ if(var1) { // +1, nesting level +1
+ if(var2) // +2 (1 + current nesting level of 1), nesting level +1
+ return 42;
+ }
+
+ return 0;
+ }
+
+Limitations
+-----------
+
+The metric is implemented with two notable exceptions:
+ * `preprocessor conditionals` (``#ifdef``, ``#if``, ``#elif``, ``#else``,
+ ``#endif``) are not accounted for.
+ * `each method in a recursion cycle` is not accounted for. It can't be fully
+ implemented, because cross-translational-unit analysis would be needed,
+ which is currently not possible in clang-tidy.
diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability-function-cognitive-complexity.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability-function-cognitive-complexity.cpp
new file mode 100644
index 000000000000..431540c6ee96
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/readability-function-cognitive-complexity.cpp
@@ -0,0 +1,1015 @@
+// RUN: %check_clang_tidy %s readability-function-cognitive-complexity %t -- -config='{CheckOptions: [{key: readability-function-cognitive-complexity.Threshold, value: 0}]}' -- -std=c++11 -fblocks -w
+
+// any function should be checked.
+
+extern int ext_func(int x = 0);
+
+int some_func(int x = 0);
+
+static int some_other_func(int x = 0) {}
+
+template<typename T> void some_templ_func(T x = 0) {}
+
+class SomeClass {
+public:
+ int *begin(int x = 0);
+ int *end(int x = 0);
+ static int func(int x = 0);
+ template<typename T> void some_templ_func(T x = 0) {}
+ SomeClass() = default;
+ SomeClass(SomeClass&) = delete;
+};
+
+// nothing ever decreases cognitive complexity, so we can check all the things
+// in one go. none of the following should increase cognitive complexity:
+void unittest_false() {
+ {};
+ ext_func();
+ some_func();
+ some_other_func();
+ some_templ_func<int>();
+ some_templ_func<bool>();
+ SomeClass::func();
+ SomeClass C;
+ C.some_templ_func<int>();
+ C.some_templ_func<bool>();
+ C.func();
+ C.end();
+ int i = some_func();
+ i = i;
+ i++;
+ --i;
+ i < 0;
+ int j = 0 ?: 1;
+ auto k = new int;
+ delete k;
+ throw i;
+ {
+ throw i;
+ }
+end:
+ return;
+}
+
+#if 1
+#define CC100
+#else
+// this macro has cognitive complexity of 100.
+// it is needed to be able to compare the testcases with the
+// reference Sonar implementation. please place it right after the first
+// CHECK-NOTES in each function
+#define CC100 if(1){if(1){if(1){if(1){if(1){if(1){if(1){if(1){if(1){if(1){if(1){if(1){if(1){}}}}}if(1){}}}}}}}}}
+#endif
+
+//----------------------------------------------------------------------------//
+//------------------------------ B1. Increments ------------------------------//
+//----------------------------------------------------------------------------//
+// Check that every thing listed in B1 of the specification does indeed //
+// recieve the base increment, and that not-body does not increase nesting //
+//----------------------------------------------------------------------------//
+
+// break does not increase cognitive complexity.
+// only break LABEL does, but it is unavaliable in C or C++
+
+// continue does not increase cognitive complexity.
+// only continue LABEL does, but it is unavaliable in C or C++
+
+void unittest_b1_00() {
+// CHECK-NOTES: :[[@LINE-1]]:6: warning: function 'unittest_b1_00' has cognitive complexity of 33 (threshold 0) [readability-function-cognitive-complexity]
+ CC100;
+
+ if (1 ? 1 : 0) {
+// CHECK-NOTES: :[[@LINE-1]]:3: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+// CHECK-NOTES: :[[@LINE-2]]:9: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+
+ if (1 ? 1 : 0) {
+// CHECK-NOTES: :[[@LINE-1]]:5: note: +2, including nesting penalty of 1, nesting level increased to 2{{$}}
+// CHECK-NOTES: :[[@LINE-2]]:11: note: +2, including nesting penalty of 1, nesting level increased to 2{{$}}
+ } else if (1 ? 1 : 0) {
+// CHECK-NOTES: :[[@LINE-1]]:12: note: +1, nesting level increased to 2{{$}}
+// CHECK-NOTES: :[[@LINE-2]]:18: note: +3, including nesting penalty of 2, nesting level increased to 3{{$}}
+ } else {
+// CHECK-NOTES: :[[@LINE-1]]:7: note: +1, nesting level increased to 2{{$}}
+ }
+ } else if (1 ? 1 : 0) {
+// CHECK-NOTES: :[[@LINE-1]]:10: note: +1, nesting level increased to 1{{$}}
+// CHECK-NOTES: :[[@LINE-2]]:16: note: +2, including nesting penalty of 1, nesting level increased to 2{{$}}
+
+ if (1 ? 1 : 0) {
+// CHECK-NOTES: :[[@LINE-1]]:5: note: +2, including nesting penalty of 1, nesting level increased to 2{{$}}
+// CHECK-NOTES: :[[@LINE-2]]:11: note: +2, including nesting penalty of 1, nesting level increased to 2{{$}}
+ } else if (1 ? 1 : 0) {
+// CHECK-NOTES: :[[@LINE-1]]:12: note: +1, nesting level increased to 2{{$}}
+// CHECK-NOTES: :[[@LINE-2]]:18: note: +3, including nesting penalty of 2, nesting level increased to 3{{$}}
+ } else {
+// CHECK-NOTES: :[[@LINE-1]]:7: note: +1, nesting level increased to 2{{$}}
+ }
+ } else {
+// CHECK-NOTES: :[[@LINE-1]]:5: note: +1, nesting level increased to 1{{$}}
+
+ if (1 ? 1 : 0) {
+// CHECK-NOTES: :[[@LINE-1]]:5: note: +2, including nesting penalty of 1, nesting level increased to 2{{$}}
+// CHECK-NOTES: :[[@LINE-2]]:11: note: +2, including nesting penalty of 1, nesting level increased to 2{{$}}
+ } else if (1 ? 1 : 0) {
+// CHECK-NOTES: :[[@LINE-1]]:12: note: +1, nesting level increased to 2{{$}}
+// CHECK-NOTES: :[[@LINE-2]]:18: note: +3, including nesting penalty of 2, nesting level increased to 3{{$}}
+ } else {
+// CHECK-NOTES: :[[@LINE-1]]:7: note: +1, nesting level increased to 2{{$}}
+ }
+ }
+}
+
+void unittest_b1_01() {
+// CHECK-NOTES: :[[@LINE-1]]:6: warning: function 'unittest_b1_01' has cognitive complexity of 3 (threshold 0) [readability-function-cognitive-complexity]
+ CC100;
+
+ int i = (1 ? 1 : 0) ? 1 : 0;
+// CHECK-NOTES: :[[@LINE-1]]:23: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+// CHECK-NOTES: :[[@LINE-2]]:14: note: +2, including nesting penalty of 1, nesting level increased to 2{{$}}
+}
+
+void unittest_b1_02(int x) {
+// CHECK-NOTES: :[[@LINE-1]]:6: warning: function 'unittest_b1_02' has cognitive complexity of 9 (threshold 0) [readability-function-cognitive-complexity]
+ CC100;
+
+ switch (1 ? 1 : 0) {
+// CHECK-NOTES: :[[@LINE-1]]:3: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+// CHECK-NOTES: :[[@LINE-2]]:13: note: +2, including nesting penalty of 1, nesting level increased to 2{{$}}
+ case -1:
+ return;
+ case 1 ? 1 : 0:
+// CHECK-NOTES: :[[@LINE-1]]:10: note: +2, including nesting penalty of 1, nesting level increased to 2{{$}}
+ return;
+ case (1 ? 2 : 0) ... (1 ? 3 : 0):
+// CHECK-NOTES: :[[@LINE-1]]:11: note: +2, including nesting penalty of 1, nesting level increased to 2{{$}}
+// CHECK-NOTES: :[[@LINE-2]]:27: note: +2, including nesting penalty of 1, nesting level increased to 2{{$}}
+ return;
+ default:
+ break;
+ }
+}
+
+void unittest_b1_03(int x) {
+// CHECK-NOTES: :[[@LINE-1]]:6: warning: function 'unittest_b1_03' has cognitive complexity of 7 (threshold 0) [readability-function-cognitive-complexity]
+ CC100;
+
+ for (x = 1 ? 1 : 0; x < (1 ? 1 : 0); x += 1 ? 1 : 0) {
+// CHECK-NOTES: :[[@LINE-1]]:3: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+// CHECK-NOTES: :[[@LINE-2]]:14: note: +2, including nesting penalty of 1, nesting level increased to 2{{$}}
+// CHECK-NOTES: :[[@LINE-3]]:30: note: +2, including nesting penalty of 1, nesting level increased to 2{{$}}
+// CHECK-NOTES: :[[@LINE-4]]:47: note: +2, including nesting penalty of 1, nesting level increased to 2{{$}}
+ break;
+ continue;
+ }
+}
+
+void unittest_b1_04() {
+// CHECK-NOTES: :[[@LINE-1]]:6: warning: function 'unittest_b1_04' has cognitive complexity of 3 (threshold 0) [readability-function-cognitive-complexity]
+ CC100;
+
+ SomeClass C;
+ for (int i : (1 ? C : C)) {
+// CHECK-NOTES: :[[@LINE-1]]:3: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+// CHECK-NOTES: :[[@LINE-2]]:19: note: +2, including nesting penalty of 1, nesting level increased to 2{{$}}
+ break;
+ continue;
+ }
+}
+
+void unittest_b1_05() {
+// CHECK-NOTES: :[[@LINE-1]]:6: warning: function 'unittest_b1_05' has cognitive complexity of 3 (threshold 0) [readability-function-cognitive-complexity]
+ CC100;
+
+ while (1 ? 1 : 0) {
+// CHECK-NOTES: :[[@LINE-1]]:3: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+// CHECK-NOTES: :[[@LINE-2]]:12: note: +2, including nesting penalty of 1, nesting level increased to 2{{$}}
+ break;
+ continue;
+ }
+}
+
+void unittest_b1_06() {
+// CHECK-NOTES: :[[@LINE-1]]:6: warning: function 'unittest_b1_06' has cognitive complexity of 3 (threshold 0) [readability-function-cognitive-complexity]
+ CC100;
+
+ do {
+// CHECK-NOTES: :[[@LINE-1]]:3: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+ break;
+ continue;
+ } while (1 ? 1 : 0);
+// CHECK-NOTES: :[[@LINE-1]]:14: note: +2, including nesting penalty of 1, nesting level increased to 2{{$}}
+}
+
+void unittest_b1_07() {
+// CHECK-NOTES: :[[@LINE-1]]:6: warning: function 'unittest_b1_07' has cognitive complexity of 1 (threshold 0) [readability-function-cognitive-complexity]
+ CC100;
+
+ try {
+ } catch (...) {
+// CHECK-NOTES: :[[@LINE-1]]:5: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+ }
+}
+
+void unittest_b1_08_00() {
+// CHECK-NOTES: :[[@LINE-1]]:6: warning: function 'unittest_b1_08_00' has cognitive complexity of 1 (threshold 0) [readability-function-cognitive-complexity]
+ CC100;
+
+ goto end;
+// CHECK-NOTES: :[[@LINE-1]]:3: note: +1{{$}}
+end:
+ return;
+}
+
+void unittest_b1_08_01() {
+// CHECK-NOTES: :[[@LINE-1]]:6: warning: function 'unittest_b1_08_01' has cognitive complexity of 1 (threshold 0) [readability-function-cognitive-complexity]
+ CC100;
+
+ void *ptr = &&end;
+ goto *ptr;
+// CHECK-NOTES: :[[@LINE-1]]:3: note: +1{{$}}
+end:
+ return;
+}
+
+void unittest_b1_09_00() {
+// CHECK-NOTES: :[[@LINE-1]]:6: warning: function 'unittest_b1_09_00' has cognitive complexity of 34 (threshold 0) [readability-function-cognitive-complexity]
+ CC100;
+
+ if(1 && 1) {
+// CHECK-NOTES: :[[@LINE-1]]:3: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+// CHECK-NOTES: :[[@LINE-2]]:8: note: +1{{$}}
+ }
+ if(1 && 1 && 1) {
+// CHECK-NOTES: :[[@LINE-1]]:3: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+// CHECK-NOTES: :[[@LINE-2]]:13: note: +1{{$}}
+ }
+ if((1 && 1) && 1) {
+// CHECK-NOTES: :[[@LINE-1]]:3: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+// CHECK-NOTES: :[[@LINE-2]]:15: note: +1{{$}}
+ }
+ if(1 && (1 && 1)) {
+// CHECK-NOTES: :[[@LINE-1]]:3: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+// CHECK-NOTES: :[[@LINE-2]]:8: note: +1{{$}}
+ }
+
+ if(1 && 1 || 1) {
+// CHECK-NOTES: :[[@LINE-1]]:3: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+// CHECK-NOTES: :[[@LINE-2]]:13: note: +1{{$}}
+// CHECK-NOTES: :[[@LINE-3]]:8: note: +1{{$}}
+ }
+ if((1 && 1) || 1) {
+// CHECK-NOTES: :[[@LINE-1]]:3: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+// CHECK-NOTES: :[[@LINE-2]]:15: note: +1{{$}}
+// CHECK-NOTES: :[[@LINE-3]]:9: note: +1{{$}}
+ }
+ if(1 && (1 || 1)) {
+// CHECK-NOTES: :[[@LINE-1]]:3: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+// CHECK-NOTES: :[[@LINE-2]]:8: note: +1{{$}}
+// CHECK-NOTES: :[[@LINE-3]]:14: note: +1{{$}}
+ }
+
+ if(1 || 1) {
+// CHECK-NOTES: :[[@LINE-1]]:3: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+// CHECK-NOTES: :[[@LINE-2]]:8: note: +1{{$}}
+ }
+ if(1 || 1 || 1) {
+// CHECK-NOTES: :[[@LINE-1]]:3: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+// CHECK-NOTES: :[[@LINE-2]]:13: note: +1{{$}}
+ }
+ if((1 || 1) || 1) {
+// CHECK-NOTES: :[[@LINE-1]]:3: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+// CHECK-NOTES: :[[@LINE-2]]:15: note: +1{{$}}
+ }
+ if(1 || (1 || 1)) {
+// CHECK-NOTES: :[[@LINE-1]]:3: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+// CHECK-NOTES: :[[@LINE-2]]:8: note: +1{{$}}
+ }
+
+ if(1 || 1 && 1) {
+// CHECK-NOTES: :[[@LINE-1]]:3: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+// CHECK-NOTES: :[[@LINE-2]]:8: note: +1{{$}}
+// CHECK-NOTES: :[[@LINE-3]]:13: note: +1{{$}}
+ }
+ if((1 || 1) && 1) {
+// CHECK-NOTES: :[[@LINE-1]]:3: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+// CHECK-NOTES: :[[@LINE-2]]:15: note: +1{{$}}
+// CHECK-NOTES: :[[@LINE-3]]:9: note: +1{{$}}
+ }
+ if(1 || (1 && 1)) {
+// CHECK-NOTES: :[[@LINE-1]]:3: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+// CHECK-NOTES: :[[@LINE-2]]:8: note: +1{{$}}
+// CHECK-NOTES: :[[@LINE-3]]:14: note: +1{{$}}
+ }
+}
+
+void unittest_b1_09_01() {
+// CHECK-NOTES: :[[@LINE-1]]:6: warning: function 'unittest_b1_09_01' has cognitive complexity of 40 (threshold 0) [readability-function-cognitive-complexity]
+ CC100;
+
+ if(1 && some_func(1 && 1)) {
+// CHECK-NOTES: :[[@LINE-1]]:3: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+// CHECK-NOTES: :[[@LINE-2]]:8: note: +1{{$}}
+// CHECK-NOTES: :[[@LINE-3]]:23: note: +1{{$}}
+ }
+ if(1 && some_func(1 || 1)) {
+// CHECK-NOTES: :[[@LINE-1]]:3: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+// CHECK-NOTES: :[[@LINE-2]]:8: note: +1{{$}}
+// CHECK-NOTES: :[[@LINE-3]]:23: note: +1{{$}}
+ }
+ if(1 || some_func(1 || 1)) {
+// CHECK-NOTES: :[[@LINE-1]]:3: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+// CHECK-NOTES: :[[@LINE-2]]:8: note: +1{{$}}
+// CHECK-NOTES: :[[@LINE-3]]:23: note: +1{{$}}
+ }
+ if(1 || some_func(1 && 1)) {
+// CHECK-NOTES: :[[@LINE-1]]:3: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+// CHECK-NOTES: :[[@LINE-2]]:8: note: +1{{$}}
+// CHECK-NOTES: :[[@LINE-3]]:23: note: +1{{$}}
+ }
+
+ if(1 && some_func(1 && 1) && 1) {
+// CHECK-NOTES: :[[@LINE-1]]:3: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+// CHECK-NOTES: :[[@LINE-2]]:29: note: +1{{$}}
+// CHECK-NOTES: :[[@LINE-3]]:23: note: +1{{$}}
+ }
+ if(1 && some_func(1 || 1) && 1) {
+// CHECK-NOTES: :[[@LINE-1]]:3: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+// CHECK-NOTES: :[[@LINE-2]]:29: note: +1{{$}}
+// CHECK-NOTES: :[[@LINE-3]]:23: note: +1{{$}}
+ }
+ if(1 || some_func(1 || 1) && 1) {
+// CHECK-NOTES: :[[@LINE-1]]:3: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+// CHECK-NOTES: :[[@LINE-2]]:8: note: +1{{$}}
+// CHECK-NOTES: :[[@LINE-3]]:29: note: +1{{$}}
+// CHECK-NOTES: :[[@LINE-4]]:23: note: +1{{$}}
+ }
+ if(1 || some_func(1 && 1) && 1) {
+// CHECK-NOTES: :[[@LINE-1]]:3: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+// CHECK-NOTES: :[[@LINE-2]]:8: note: +1{{$}}
+// CHECK-NOTES: :[[@LINE-3]]:29: note: +1{{$}}
+// CHECK-NOTES: :[[@LINE-4]]:23: note: +1{{$}}
+ }
+
+ if(1 && some_func(1 && 1) || 1) {
+// CHECK-NOTES: :[[@LINE-1]]:3: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+// CHECK-NOTES: :[[@LINE-2]]:29: note: +1{{$}}
+// CHECK-NOTES: :[[@LINE-3]]:8: note: +1{{$}}
+// CHECK-NOTES: :[[@LINE-4]]:23: note: +1{{$}}
+ }
+ if(1 && some_func(1 || 1) || 1) {
+// CHECK-NOTES: :[[@LINE-1]]:3: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+// CHECK-NOTES: :[[@LINE-2]]:29: note: +1{{$}}
+// CHECK-NOTES: :[[@LINE-3]]:8: note: +1{{$}}
+// CHECK-NOTES: :[[@LINE-4]]:23: note: +1{{$}}
+ }
+ if(1 || some_func(1 || 1) || 1) {
+// CHECK-NOTES: :[[@LINE-1]]:3: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+// CHECK-NOTES: :[[@LINE-2]]:29: note: +1{{$}}
+// CHECK-NOTES: :[[@LINE-3]]:23: note: +1{{$}}
+ }
+ if(1 || some_func(1 && 1) || 1) {
+// CHECK-NOTES: :[[@LINE-1]]:3: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+// CHECK-NOTES: :[[@LINE-2]]:29: note: +1{{$}}
+// CHECK-NOTES: :[[@LINE-3]]:23: note: +1{{$}}
+ }
+}
+
+void unittest_b1_09_02() {
+// CHECK-NOTES: :[[@LINE-1]]:6: warning: function 'unittest_b1_09_02' has cognitive complexity of 12 (threshold 0) [readability-function-cognitive-complexity]
+ CC100;
+
+ if(1 && SomeClass::func(1 && 1)) {
+// CHECK-NOTES: :[[@LINE-1]]:3: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+// CHECK-NOTES: :[[@LINE-2]]:8: note: +1{{$}}
+// CHECK-NOTES: :[[@LINE-3]]:29: note: +1{{$}}
+ }
+ if(1 && SomeClass::func(1 || 1)) {
+// CHECK-NOTES: :[[@LINE-1]]:3: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+// CHECK-NOTES: :[[@LINE-2]]:8: note: +1{{$}}
+// CHECK-NOTES: :[[@LINE-3]]:29: note: +1{{$}}
+ }
+ if(1 || SomeClass::func(1 || 1)) {
+// CHECK-NOTES: :[[@LINE-1]]:3: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+// CHECK-NOTES: :[[@LINE-2]]:8: note: +1{{$}}
+// CHECK-NOTES: :[[@LINE-3]]:29: note: +1{{$}}
+ }
+ if(1 || SomeClass::func(1 && 1)) {
+// CHECK-NOTES: :[[@LINE-1]]:3: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+// CHECK-NOTES: :[[@LINE-2]]:8: note: +1{{$}}
+// CHECK-NOTES: :[[@LINE-3]]:29: note: +1{{$}}
+ }
+}
+
+// FIXME: each method in a recursion cycle
+
+//----------------------------------------------------------------------------//
+//---------------------------- B2. Nesting lebel -----------------------------//
+//----------------------------------------------------------------------------//
+// Check that every thing listed in B2 of the specification does indeed //
+// increase the nesting level //
+//----------------------------------------------------------------------------//
+
+void unittest_b2_00() {
+// CHECK-NOTES: :[[@LINE-1]]:6: warning: function 'unittest_b2_00' has cognitive complexity of 9 (threshold 0) [readability-function-cognitive-complexity]
+ CC100;
+
+ if (true) {
+// CHECK-NOTES: :[[@LINE-1]]:3: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+ if(true) {
+// CHECK-NOTES: :[[@LINE-1]]:5: note: +2, including nesting penalty of 1, nesting level increased to 2{{$}}
+ }
+ } else if (true) {
+// CHECK-NOTES: :[[@LINE-1]]:10: note: +1, nesting level increased to 1{{$}}
+ if(true) {
+// CHECK-NOTES: :[[@LINE-1]]:5: note: +2, including nesting penalty of 1, nesting level increased to 2{{$}}
+ }
+ } else {
+// CHECK-NOTES: :[[@LINE-1]]:5: note: +1, nesting level increased to 1{{$}}
+ if(true) {
+// CHECK-NOTES: :[[@LINE-1]]:5: note: +2, including nesting penalty of 1, nesting level increased to 2{{$}}
+ }
+ }
+}
+
+void unittest_b2_01() {
+// CHECK-NOTES: :[[@LINE-1]]:6: warning: function 'unittest_b2_01' has cognitive complexity of 5 (threshold 0) [readability-function-cognitive-complexity]
+ CC100;
+
+ int i = 1 ? (1 ? 1 : 0) : (1 ? 1 : 0);
+// CHECK-NOTES: :[[@LINE-1]]:13: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+// CHECK-NOTES: :[[@LINE-2]]:18: note: +2, including nesting penalty of 1, nesting level increased to 2{{$}}
+// CHECK-NOTES: :[[@LINE-3]]:32: note: +2, including nesting penalty of 1, nesting level increased to 2{{$}}
+}
+
+void unittest_b2_02(int x) {
+// CHECK-NOTES: :[[@LINE-1]]:6: warning: function 'unittest_b2_02' has cognitive complexity of 5 (threshold 0) [readability-function-cognitive-complexity]
+ CC100;
+
+ switch (x) {
+// CHECK-NOTES: :[[@LINE-1]]:3: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+ case -1:
+ if(true) {
+// CHECK-NOTES: :[[@LINE-1]]:5: note: +2, including nesting penalty of 1, nesting level increased to 2{{$}}
+ }
+ return;
+ default:
+ if(true) {
+// CHECK-NOTES: :[[@LINE-1]]:5: note: +2, including nesting penalty of 1, nesting level increased to 2{{$}}
+ }
+ return;
+ }
+}
+
+void unittest_b2_03() {
+// CHECK-NOTES: :[[@LINE-1]]:6: warning: function 'unittest_b2_03' has cognitive complexity of 3 (threshold 0) [readability-function-cognitive-complexity]
+ CC100;
+
+ for (;;) {
+// CHECK-NOTES: :[[@LINE-1]]:3: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+ if(true) {
+// CHECK-NOTES: :[[@LINE-1]]:5: note: +2, including nesting penalty of 1, nesting level increased to 2{{$}}
+ }
+ }
+}
+
+void unittest_b2_04() {
+// CHECK-NOTES: :[[@LINE-1]]:6: warning: function 'unittest_b2_04' has cognitive complexity of 3 (threshold 0) [readability-function-cognitive-complexity]
+ CC100;
+
+ SomeClass C;
+ for (int i : C) {
+// CHECK-NOTES: :[[@LINE-1]]:3: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+ if(true) {
+// CHECK-NOTES: :[[@LINE-1]]:5: note: +2, including nesting penalty of 1, nesting level increased to 2{{$}}
+ }
+ }
+}
+
+void unittest_b2_05() {
+// CHECK-NOTES: :[[@LINE-1]]:6: warning: function 'unittest_b2_05' has cognitive complexity of 3 (threshold 0) [readability-function-cognitive-complexity]
+ CC100;
+
+ while (true) {
+// CHECK-NOTES: :[[@LINE-1]]:3: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+ if(true) {
+// CHECK-NOTES: :[[@LINE-1]]:5: note: +2, including nesting penalty of 1, nesting level increased to 2{{$}}
+ }
+ }
+}
+
+void unittest_b2_06() {
+// CHECK-NOTES: :[[@LINE-1]]:6: warning: function 'unittest_b2_06' has cognitive complexity of 3 (threshold 0) [readability-function-cognitive-complexity]
+ CC100;
+
+ do {
+// CHECK-NOTES: :[[@LINE-1]]:3: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+ if(true) {
+// CHECK-NOTES: :[[@LINE-1]]:5: note: +2, including nesting penalty of 1, nesting level increased to 2{{$}}
+ }
+ } while (true);
+}
+
+void unittest_b2_07() {
+// CHECK-NOTES: :[[@LINE-1]]:6: warning: function 'unittest_b2_07' has cognitive complexity of 3 (threshold 0) [readability-function-cognitive-complexity]
+ CC100;
+
+ try {
+ } catch (...) {
+// CHECK-NOTES: :[[@LINE-1]]:5: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+ if(true) {
+// CHECK-NOTES: :[[@LINE-1]]:5: note: +2, including nesting penalty of 1, nesting level increased to 2{{$}}
+ }
+ }
+}
+
+void unittest_b2_08_00() {
+// CHECK-NOTES: :[[@LINE-1]]:6: warning: function 'unittest_b2_08_00' has cognitive complexity of 10 (threshold 0) [readability-function-cognitive-complexity]
+ CC100;
+
+ class X {
+ X() {
+// CHECK-NOTES: :[[@LINE-1]]:5: note: nesting level increased to 1{{$}}
+ CC100;
+
+ if (true) {
+// CHECK-NOTES: :[[@LINE-1]]:7: note: +2, including nesting penalty of 1, nesting level increased to 2{{$}}
+ }
+ }
+
+ X &operator=(const X &other) {
+// CHECK-NOTES: :[[@LINE-1]]:5: note: nesting level increased to 1{{$}}
+ CC100;
+
+ if (true) {
+// CHECK-NOTES: :[[@LINE-1]]:7: note: +2, including nesting penalty of 1, nesting level increased to 2{{$}}
+ }
+ }
+
+ ~X() {
+// CHECK-NOTES: :[[@LINE-1]]:5: note: nesting level increased to 1{{$}}
+ CC100;
+
+ if (true) {
+// CHECK-NOTES: :[[@LINE-1]]:7: note: +2, including nesting penalty of 1, nesting level increased to 2{{$}}
+ }
+ }
+
+ void Y() {
+// CHECK-NOTES: :[[@LINE-1]]:5: note: nesting level increased to 1{{$}}
+ CC100;
+
+ if (true) {
+// CHECK-NOTES: :[[@LINE-1]]:7: note: +2, including nesting penalty of 1, nesting level increased to 2{{$}}
+ }
+ }
+
+ static void Z() {
+// CHECK-NOTES: :[[@LINE-1]]:5: note: nesting level increased to 1{{$}}
+ CC100;
+
+ if (true) {
+// CHECK-NOTES: :[[@LINE-1]]:7: note: +2, including nesting penalty of 1, nesting level increased to 2{{$}}
+ }
+ }
+
+// CHECK-NOTES: :[[@LINE-45]]:5: warning: function 'X' has cognitive complexity of 1 (threshold 0) [readability-function-cognitive-complexity]
+// CHECK-NOTES: :[[@LINE-42]]:7: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+
+// CHECK-NOTES: :[[@LINE-39]]:8: warning: function 'operator=' has cognitive complexity of 1 (threshold 0) [readability-function-cognitive-complexity]
+// CHECK-NOTES: :[[@LINE-36]]:7: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+
+// CHECK-NOTES: :[[@LINE-33]]:5: warning: function '~X' has cognitive complexity of 1 (threshold 0) [readability-function-cognitive-complexity]
+// CHECK-NOTES: :[[@LINE-30]]:7: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+
+// CHECK-NOTES: :[[@LINE-27]]:10: warning: function 'Y' has cognitive complexity of 1 (threshold 0) [readability-function-cognitive-complexity]
+// CHECK-NOTES: :[[@LINE-24]]:7: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+
+// CHECK-NOTES: :[[@LINE-21]]:17: warning: function 'Z' has cognitive complexity of 1 (threshold 0) [readability-function-cognitive-complexity]
+// CHECK-NOTES: :[[@LINE-18]]:7: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+ };
+}
+
+void unittest_b2_08_01() {
+// CHECK-NOTES: :[[@LINE-1]]:6: warning: function 'unittest_b2_08_01' has cognitive complexity of 10 (threshold 0) [readability-function-cognitive-complexity]
+ CC100;
+
+ struct X {
+ X() {
+// CHECK-NOTES: :[[@LINE-1]]:5: note: nesting level increased to 1{{$}}
+ CC100;
+
+ if (true) {
+// CHECK-NOTES: :[[@LINE-1]]:7: note: +2, including nesting penalty of 1, nesting level increased to 2{{$}}
+ }
+ }
+
+ X &operator=(const X &other) {
+// CHECK-NOTES: :[[@LINE-1]]:5: note: nesting level increased to 1{{$}}
+ CC100;
+
+ if (true) {
+// CHECK-NOTES: :[[@LINE-1]]:7: note: +2, including nesting penalty of 1, nesting level increased to 2{{$}}
+ }
+ }
+
+ ~X() {
+// CHECK-NOTES: :[[@LINE-1]]:5: note: nesting level increased to 1{{$}}
+ CC100;
+
+ if (true) {
+// CHECK-NOTES: :[[@LINE-1]]:7: note: +2, including nesting penalty of 1, nesting level increased to 2{{$}}
+ }
+ }
+
+ void Y() {
+// CHECK-NOTES: :[[@LINE-1]]:5: note: nesting level increased to 1{{$}}
+ CC100;
+
+ if (true) {
+// CHECK-NOTES: :[[@LINE-1]]:7: note: +2, including nesting penalty of 1, nesting level increased to 2{{$}}
+ }
+ }
+
+ static void Z() {
+// CHECK-NOTES: :[[@LINE-1]]:5: note: nesting level increased to 1{{$}}
+ CC100;
+
+ if (true) {
+// CHECK-NOTES: :[[@LINE-1]]:7: note: +2, including nesting penalty of 1, nesting level increased to 2{{$}}
+ }
+ }
+
+// CHECK-NOTES: :[[@LINE-45]]:5: warning: function 'X' has cognitive complexity of 1 (threshold 0) [readability-function-cognitive-complexity]
+// CHECK-NOTES: :[[@LINE-42]]:7: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+
+// CHECK-NOTES: :[[@LINE-39]]:8: warning: function 'operator=' has cognitive complexity of 1 (threshold 0) [readability-function-cognitive-complexity]
+// CHECK-NOTES: :[[@LINE-36]]:7: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+
+// CHECK-NOTES: :[[@LINE-33]]:5: warning: function '~X' has cognitive complexity of 1 (threshold 0) [readability-function-cognitive-complexity]
+// CHECK-NOTES: :[[@LINE-30]]:7: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+
+// CHECK-NOTES: :[[@LINE-27]]:10: warning: function 'Y' has cognitive complexity of 1 (threshold 0) [readability-function-cognitive-complexity]
+// CHECK-NOTES: :[[@LINE-24]]:7: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+
+// CHECK-NOTES: :[[@LINE-21]]:17: warning: function 'Z' has cognitive complexity of 1 (threshold 0) [readability-function-cognitive-complexity]
+// CHECK-NOTES: :[[@LINE-18]]:7: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+ };
+}
+
+void unittest_b2_08_02() {
+// CHECK-NOTES: :[[@LINE-1]]:6: warning: function 'unittest_b2_08_02' has cognitive complexity of 2 (threshold 0) [readability-function-cognitive-complexity]
+ CC100;
+
+ auto fun = []() {
+// CHECK-NOTES: :[[@LINE-1]]:14: note: nesting level increased to 1{{$}}
+ if (true) {
+// CHECK-NOTES: :[[@LINE-1]]:5: note: +2, including nesting penalty of 1, nesting level increased to 2{{$}}
+ }
+ };
+// CHECK-NOTES: :[[@LINE-6]]:14: warning: function 'operator()' has cognitive complexity of 1 (threshold 0) [readability-function-cognitive-complexity]
+// CHECK-NOTES: :[[@LINE-5]]:5: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+}
+
+void unittest_b2_09() {
+// CHECK-NOTES: :[[@LINE-1]]:6: warning: function 'unittest_b2_09' has cognitive complexity of 2 (threshold 0) [readability-function-cognitive-complexity]
+ CC100;
+
+ ({
+// CHECK-NOTES: :[[@LINE-1]]:3: note: nesting level increased to 1{{$}}
+ if (true) {
+// CHECK-NOTES: :[[@LINE-1]]:5: note: +2, including nesting penalty of 1, nesting level increased to 2{{$}}
+ }
+ });
+}
+
+void unittest_b2_10() {
+// CHECK-NOTES: :[[@LINE-1]]:6: warning: function 'unittest_b2_10' has cognitive complexity of 2 (threshold 0) [readability-function-cognitive-complexity]
+ CC100;
+
+ void (^foo)(void) = ^(void) {
+// CHECK-NOTES: :[[@LINE-1]]:23: note: nesting level increased to 1{{$}}
+ if (true) {
+// CHECK-NOTES: :[[@LINE-1]]:5: note: +2, including nesting penalty of 1, nesting level increased to 2{{$}}
+ }
+ };
+}
+
+//----------------------------------------------------------------------------//
+//-------------------------- B3. Nesting increments --------------------------//
+//----------------------------------------------------------------------------//
+// Check that every thing listed in B3 of the specification does indeed //
+// recieve the penalty of the current nesting level //
+//----------------------------------------------------------------------------//
+
+void unittest_b3_00() {
+// CHECK-NOTES: :[[@LINE-1]]:6: warning: function 'unittest_b3_00' has cognitive complexity of 3 (threshold 0) [readability-function-cognitive-complexity]
+ CC100;
+
+ if (true) {
+// CHECK-NOTES: :[[@LINE-1]]:3: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+ if (true) {
+// CHECK-NOTES: :[[@LINE-1]]:5: note: +2, including nesting penalty of 1, nesting level increased to 2{{$}}
+ }
+ }
+}
+
+void unittest_b3_01() {
+// CHECK-NOTES: :[[@LINE-1]]:6: warning: function 'unittest_b3_01' has cognitive complexity of 3 (threshold 0) [readability-function-cognitive-complexity]
+ CC100;
+
+ if (true) {
+// CHECK-NOTES: :[[@LINE-1]]:3: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+ int i = 1 ? 1 : 0;
+// CHECK-NOTES: :[[@LINE-1]]:15: note: +2, including nesting penalty of 1, nesting level increased to 2{{$}}
+ }
+}
+
+void unittest_b3_02(int x) {
+// CHECK-NOTES: :[[@LINE-1]]:6: warning: function 'unittest_b3_02' has cognitive complexity of 3 (threshold 0) [readability-function-cognitive-complexity]
+ CC100;
+
+ if (true) {
+// CHECK-NOTES: :[[@LINE-1]]:3: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+ switch (x) {
+// CHECK-NOTES: :[[@LINE-1]]:5: note: +2, including nesting penalty of 1, nesting level increased to 2{{$}}
+ case -1:
+ return;
+ default:
+ return;
+ }
+ }
+}
+
+void unittest_b3_03() {
+// CHECK-NOTES: :[[@LINE-1]]:6: warning: function 'unittest_b3_03' has cognitive complexity of 3 (threshold 0) [readability-function-cognitive-complexity]
+ CC100;
+
+ if (true) {
+// CHECK-NOTES: :[[@LINE-1]]:3: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+ for (;;) {
+// CHECK-NOTES: :[[@LINE-1]]:5: note: +2, including nesting penalty of 1, nesting level increased to 2{{$}}
+ }
+ }
+}
+
+void unittest_b3_04() {
+// CHECK-NOTES: :[[@LINE-1]]:6: warning: function 'unittest_b3_04' has cognitive complexity of 3 (threshold 0) [readability-function-cognitive-complexity]
+ CC100;
+
+ if (true) {
+// CHECK-NOTES: :[[@LINE-1]]:3: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+ SomeClass C;
+ for (int i : C) {
+// CHECK-NOTES: :[[@LINE-1]]:5: note: +2, including nesting penalty of 1, nesting level increased to 2{{$}}
+ }
+ }
+}
+
+void unittest_b3_05() {
+// CHECK-NOTES: :[[@LINE-1]]:6: warning: function 'unittest_b3_05' has cognitive complexity of 3 (threshold 0) [readability-function-cognitive-complexity]
+ CC100;
+
+ if (true) {
+// CHECK-NOTES: :[[@LINE-1]]:3: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+ while (true) {
+// CHECK-NOTES: :[[@LINE-1]]:5: note: +2, including nesting penalty of 1, nesting level increased to 2{{$}}
+ }
+ }
+}
+
+void unittest_b3_06() {
+// CHECK-NOTES: :[[@LINE-1]]:6: warning: function 'unittest_b3_06' has cognitive complexity of 3 (threshold 0) [readability-function-cognitive-complexity]
+ CC100;
+
+ if (true) {
+// CHECK-NOTES: :[[@LINE-1]]:3: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+ do {
+// CHECK-NOTES: :[[@LINE-1]]:5: note: +2, including nesting penalty of 1, nesting level increased to 2{{$}}
+ } while (true);
+ }
+}
+
+void unittest_b3_07() {
+// CHECK-NOTES: :[[@LINE-1]]:6: warning: function 'unittest_b3_07' has cognitive complexity of 3 (threshold 0) [readability-function-cognitive-complexity]
+ CC100;
+
+ if (true) {
+// CHECK-NOTES: :[[@LINE-1]]:3: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+ try {
+ } catch (...) {
+// CHECK-NOTES: :[[@LINE-1]]:7: note: +2, including nesting penalty of 1, nesting level increased to 2{{$}}
+ }
+ }
+}
+
+//----------------------------------------------------------------------------//
+// Check that functions are being checked //
+//----------------------------------------------------------------------------//
+
+class CheckClass {
+ CheckClass(int x) {
+// CHECK-NOTES: :[[@LINE-1]]:3: warning: function 'CheckClass' has cognitive complexity of 1 (threshold 0) [readability-function-cognitive-complexity]
+ CC100;
+
+ try {
+ } catch (...) {
+// CHECK-NOTES: :[[@LINE-1]]:7: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+ }
+ }
+
+ void PrivateMemberFunction() {
+// CHECK-NOTES: :[[@LINE-1]]:8: warning: function 'PrivateMemberFunction' has cognitive complexity of 1 (threshold 0) [readability-function-cognitive-complexity]
+ CC100;
+
+ try {
+ } catch (...) {
+// CHECK-NOTES: :[[@LINE-1]]:7: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+ }
+ }
+
+ void PrivateConstMemberFunction() const {
+// CHECK-NOTES: :[[@LINE-1]]:8: warning: function 'PrivateConstMemberFunction' has cognitive complexity of 1 (threshold 0) [readability-function-cognitive-complexity]
+ CC100;
+
+ try {
+ } catch (...) {
+// CHECK-NOTES: :[[@LINE-1]]:7: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+ }
+ }
+
+ static void PrivateStaticMemberFunction() {
+// CHECK-NOTES: :[[@LINE-1]]:15: warning: function 'PrivateStaticMemberFunction' has cognitive complexity of 1 (threshold 0) [readability-function-cognitive-complexity]
+ CC100;
+
+ try {
+ } catch (...) {
+// CHECK-NOTES: :[[@LINE-1]]:7: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+ }
+ }
+
+public:
+ CheckClass() {
+// CHECK-NOTES: :[[@LINE-1]]:3: warning: function 'CheckClass' has cognitive complexity of 1 (threshold 0) [readability-function-cognitive-complexity]
+ CC100;
+
+ try {
+ } catch (...) {
+// CHECK-NOTES: :[[@LINE-1]]:7: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+ }
+ }
+
+ operator bool() const {
+// CHECK-NOTES: :[[@LINE-1]]:3: warning: function 'operator bool' has cognitive complexity of 1 (threshold 0) [readability-function-cognitive-complexity]
+ CC100;
+
+ try {
+ } catch (...) {
+// CHECK-NOTES: :[[@LINE-1]]:7: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+ }
+ }
+
+ ~CheckClass() {
+// CHECK-NOTES: :[[@LINE-1]]:3: warning: function '~CheckClass' has cognitive complexity of 1 (threshold 0) [readability-function-cognitive-complexity]
+ CC100;
+
+ try {
+ } catch (...) {
+// CHECK-NOTES: :[[@LINE-1]]:7: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+ }
+ }
+
+ void PublicMemberFunction() {
+// CHECK-NOTES: :[[@LINE-1]]:8: warning: function 'PublicMemberFunction' has cognitive complexity of 1 (threshold 0) [readability-function-cognitive-complexity]
+ CC100;
+
+ try {
+ } catch (...) {
+// CHECK-NOTES: :[[@LINE-1]]:7: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+ }
+ }
+
+ void PublicConstMemberFunction() const {
+// CHECK-NOTES: :[[@LINE-1]]:8: warning: function 'PublicConstMemberFunction' has cognitive complexity of 1 (threshold 0) [readability-function-cognitive-complexity]
+ CC100;
+
+ try {
+ } catch (...) {
+// CHECK-NOTES: :[[@LINE-1]]:7: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+ }
+ }
+
+ static void PublicStaticMemberFunction() {
+// CHECK-NOTES: :[[@LINE-1]]:15: warning: function 'PublicStaticMemberFunction' has cognitive complexity of 1 (threshold 0) [readability-function-cognitive-complexity]
+ CC100;
+
+ try {
+ } catch (...) {
+// CHECK-NOTES: :[[@LINE-1]]:7: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+ }
+ }
+
+ void PublicFunctionDefinition();
+
+protected:
+ CheckClass(bool b) {
+// CHECK-NOTES: :[[@LINE-1]]:3: warning: function 'CheckClass' has cognitive complexity of 1 (threshold 0) [readability-function-cognitive-complexity]
+ CC100;
+
+ try {
+ } catch (...) {
+// CHECK-NOTES: :[[@LINE-1]]:7: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+ }
+ }
+
+ void ProtectedMemberFunction() {
+// CHECK-NOTES: :[[@LINE-1]]:8: warning: function 'ProtectedMemberFunction' has cognitive complexity of 1 (threshold 0) [readability-function-cognitive-complexity]
+ CC100;
+
+ try {
+ } catch (...) {
+// CHECK-NOTES: :[[@LINE-1]]:7: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+ }
+ }
+
+ void ProtectedConstMemberFunction() const {
+// CHECK-NOTES: :[[@LINE-1]]:8: warning: function 'ProtectedConstMemberFunction' has cognitive complexity of 1 (threshold 0) [readability-function-cognitive-complexity]
+ CC100;
+
+ try {
+ } catch (...) {
+// CHECK-NOTES: :[[@LINE-1]]:7: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+ }
+ }
+
+ static void ProtectedStaticMemberFunction() {
+// CHECK-NOTES: :[[@LINE-1]]:15: warning: function 'ProtectedStaticMemberFunction' has cognitive complexity of 1 (threshold 0) [readability-function-cognitive-complexity]
+ CC100;
+
+ try {
+ } catch (...) {
+// CHECK-NOTES: :[[@LINE-1]]:7: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+ }
+ }
+};
+
+void CheckClass::PublicFunctionDefinition() {
+// CHECK-NOTES: :[[@LINE-1]]:18: warning: function 'PublicFunctionDefinition' has cognitive complexity of 1 (threshold 0) [readability-function-cognitive-complexity]
+ CC100;
+
+ try {
+ } catch (...) {
+// CHECK-NOTES: :[[@LINE-1]]:5: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+ }
+}
+
+#define uglyfunctionmacro(name) \
+ void name() { \
+ CC100; \
+ \
+ if (true) { \
+ try { \
+ } catch (...) { \
+ } \
+ } \
+ }
+
+uglyfunctionmacro(MacroFunction)
+// CHECK-NOTES: :[[@LINE-1]]:19: warning: function 'MacroFunction' has cognitive complexity of 3 (threshold 0) [readability-function-cognitive-complexity]
+// CHECK-NOTES: :[[@LINE-2]]:1: note: +1, including nesting penalty of 0, nesting level increased to 1
+// CHECK-NOTES: :[[@LINE-10]]:5: note: expanded from macro 'uglyfunctionmacro'
+// CHECK-NOTES: :[[@LINE-4]]:1: note: +2, including nesting penalty of 1, nesting level increased to 2
+// CHECK-NOTES: :[[@LINE-10]]:9: note: expanded from macro 'uglyfunctionmacro'
+
+template<typename T>
+void templatedFunction() {
+// CHECK-NOTES: :[[@LINE-1]]:6: warning: function 'templatedFunction' has cognitive complexity of 1 (threshold 0) [readability-function-cognitive-complexity]
+ CC100;
+
+ try {
+ } catch (...) {
+// CHECK-NOTES: :[[@LINE-1]]:5: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+ }
+}
+
+template<>
+void templatedFunction<bool>() {
+// CHECK-NOTES: :[[@LINE-1]]:6: warning: function 'templatedFunction<bool>' has cognitive complexity of 1 (threshold 0) [readability-function-cognitive-complexity]
+ CC100;
+
+ try {
+ } catch (...) {
+// CHECK-NOTES: :[[@LINE-1]]:5: note: +1, including nesting penalty of 0, nesting level increased to 1{{$}}
+ }
+}
+
+template void templatedFunction<int>();
+
+void functionThatCallsTemplatedFunctions() {
+ templatedFunction<int>();
+
+ templatedFunction<bool>();
+
+ templatedFunction<char>();
+
+ templatedFunction<void*>();
+}
More information about the cfe-commits
mailing list