diff --git a/clang-tools-extra/clang-tidy/readability/FunctionCognitiveComplexityCheck.h b/clang-tools-extra/clang-tidy/readability/FunctionCognitiveComplexityCheck.h --- a/clang-tools-extra/clang-tidy/readability/FunctionCognitiveComplexityCheck.h +++ b/clang-tools-extra/clang-tidy/readability/FunctionCognitiveComplexityCheck.h @@ -26,6 +26,8 @@ /// diagnostics on every piece of code (loop, `if` statement, etc.) which /// contributes to that complexity. // Default is `true` +/// * `IgnoreMacros` - if set to `true`, the check will ignore code inside +/// macros. Default is `false`. /// /// For the user-facing documentation see: /// http://clang.llvm.org/extra/clang-tidy/checks/readability-function-cognitive-complexity.html @@ -43,6 +45,7 @@ private: const unsigned Threshold; const bool DescribeBasicIncrements; + const bool IgnoreMacros; }; } // namespace readability diff --git a/clang-tools-extra/clang-tidy/readability/FunctionCognitiveComplexityCheck.cpp b/clang-tools-extra/clang-tidy/readability/FunctionCognitiveComplexityCheck.cpp --- a/clang-tools-extra/clang-tidy/readability/FunctionCognitiveComplexityCheck.cpp +++ b/clang-tools-extra/clang-tidy/readability/FunctionCognitiveComplexityCheck.cpp @@ -213,6 +213,9 @@ : public RecursiveASTVisitor { using Base = RecursiveASTVisitor; + // If set to true, macros are ignored during analysis. + const bool IgnoreMacros; + // The current nesting level (increased by Criteria::IncrementNesting). unsigned short CurrentNestingLevel = 0; @@ -223,6 +226,9 @@ std::stack> BinaryOperatorsStack; public: + explicit FunctionASTVisitor(const bool IgnoreMacros) + : IgnoreMacros(IgnoreMacros){}; + bool traverseStmtWithIncreasedNestingLevel(Stmt *Node) { ++CurrentNestingLevel; bool ShouldContinue = Base::TraverseStmt(Node); @@ -364,6 +370,9 @@ if (!Node) return Base::TraverseStmt(Node); + if (IgnoreMacros && Node->getBeginLoc().isMacroID()) + return true; + // Three following switch()'es have huge duplication, but it is better to // keep them separate, to simplify comparing them with the Specification. @@ -493,12 +502,14 @@ StringRef Name, ClangTidyContext *Context) : ClangTidyCheck(Name, Context), Threshold(Options.get("Threshold", CognitiveComplexity::DefaultLimit)), - DescribeBasicIncrements(Options.get("DescribeBasicIncrements", true)) {} + DescribeBasicIncrements(Options.get("DescribeBasicIncrements", true)), + IgnoreMacros(Options.get("IgnoreMacros", false)) {} void FunctionCognitiveComplexityCheck::storeOptions( ClangTidyOptions::OptionMap &Opts) { Options.store(Opts, "Threshold", Threshold); Options.store(Opts, "DescribeBasicIncrements", DescribeBasicIncrements); + Options.store(Opts, "IgnoreMacros", IgnoreMacros); } void FunctionCognitiveComplexityCheck::registerMatchers(MatchFinder *Finder) { @@ -513,7 +524,7 @@ void FunctionCognitiveComplexityCheck::check( const MatchFinder::MatchResult &Result) { - FunctionASTVisitor Visitor; + FunctionASTVisitor Visitor(IgnoreMacros); SourceLocation Loc; const auto *TheDecl = Result.Nodes.getNodeAs("func"); 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 --- 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 @@ -24,6 +24,12 @@ `if` statement, etc.) which contributes to that complexity. See also the examples below. Default is `true`. +.. option:: IgnoreMacros + + If set to `true`, the check will ignore code inside macros. Note, that also + any macro arguments are ignored, even if they should count to the complexity. + Default is `false`. + Building blocks --------------- diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability-function-cognitive-complexity-flags.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability-function-cognitive-complexity-flags.cpp --- a/clang-tools-extra/test/clang-tidy/checkers/readability-function-cognitive-complexity-flags.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/readability-function-cognitive-complexity-flags.cpp @@ -10,9 +10,27 @@ // RUN: value: 5}, \ // RUN: {key: readability-function-cognitive-complexity.DescribeBasicIncrements, \ // RUN: value: "false"} ]}' +// RUN: %check_clang_tidy -check-suffix=IGNORE-MACROS %s readability-function-cognitive-complexity %t -- \ +// RUN: -config='{CheckOptions: \ +// RUN: [{key: readability-function-cognitive-complexity.Threshold, \ +// RUN: value: 0}, \ +// RUN: {key: readability-function-cognitive-complexity.IgnoreMacros, \ +// RUN: value: "true"}, \ +// RUN: {key: readability-function-cognitive-complexity.DescribeBasicIncrements, \ +// RUN: value: "false"} ]}' +// RUN: %check_clang_tidy -check-suffix=GLOBAL-IGNORE-MACROS %s readability-function-cognitive-complexity %t -- \ +// RUN: -config='{CheckOptions: \ +// RUN: [{key: readability-function-cognitive-complexity.Threshold, \ +// RUN: value: 0}, \ +// RUN: {key: IgnoreMacros, \ +// RUN: value: "true"}, \ +// RUN: {key: readability-function-cognitive-complexity.DescribeBasicIncrements, \ +// RUN: value: "false"} ]}' void func_of_complexity_4() { // CHECK-NOTES: :[[@LINE-1]]:6: warning: function 'func_of_complexity_4' has cognitive complexity of 4 (threshold 0) [readability-function-cognitive-complexity] + // CHECK-NOTES-IGNORE-MACROS: :[[@LINE-2]]:6: warning: function 'func_of_complexity_4' has cognitive complexity of 4 (threshold 0) [readability-function-cognitive-complexity] + // CHECK-NOTES-GLOBAL-IGNORE-MACROS: :[[@LINE-3]]:6: warning: function 'func_of_complexity_4' has cognitive complexity of 4 (threshold 0) [readability-function-cognitive-complexity] if (1) { if (1) { } @@ -34,9 +52,44 @@ void function_with_macro() { // CHECK-NOTES: :[[@LINE-1]]:6: warning: function 'function_with_macro' has cognitive complexity of 11 (threshold 0) [readability-function-cognitive-complexity] // CHECK-NOTES-THRESHOLD5: :[[@LINE-2]]:6: warning: function 'function_with_macro' has cognitive complexity of 11 (threshold 5) [readability-function-cognitive-complexity] + // CHECK-NOTES-IGNORE-MACROS: :[[@LINE-3]]:6: warning: function 'function_with_macro' has cognitive complexity of 1 (threshold 0) [readability-function-cognitive-complexity] + // CHECK-NOTES-GLOBAL-IGNORE-MACROS: :[[@LINE-4]]:6: warning: function 'function_with_macro' has cognitive complexity of 11 (threshold 0) [readability-function-cognitive-complexity] MacroOfComplexity10; if (1) { } } + +#define noop \ + {} + +#define SomeMacro(x) \ + if (1) { \ + x; \ + } + +void func_macro_1() { + // CHECK-NOTES: :[[@LINE-1]]:6: warning: function 'func_macro_1' has cognitive complexity of 2 (threshold 0) [readability-function-cognitive-complexity] + // CHECK-NOTES-IGNORE-MACROS: :[[@LINE-2]]:6: warning: function 'func_macro_1' has cognitive complexity of 1 (threshold 0) [readability-function-cognitive-complexity] + // CHECK-NOTES-GLOBAL-IGNORE-MACROS: :[[@LINE-3]]:6: warning: function 'func_macro_1' has cognitive complexity of 2 (threshold 0) [readability-function-cognitive-complexity] + + if (1) { + } + SomeMacro(noop); +} + +void func_macro_2() { + // CHECK-NOTES: :[[@LINE-1]]:6: warning: function 'func_macro_2' has cognitive complexity of 4 (threshold 0) [readability-function-cognitive-complexity] + // CHECK-NOTES-IGNORE-MACROS: :[[@LINE-2]]:6: warning: function 'func_macro_2' has cognitive complexity of 1 (threshold 0) [readability-function-cognitive-complexity] + // CHECK-NOTES-GLOBAL-IGNORE-MACROS: :[[@LINE-3]]:6: warning: function 'func_macro_2' has cognitive complexity of 4 (threshold 0) [readability-function-cognitive-complexity] + + if (1) { + } + // Note that if the IgnoreMacro option is set to 'true', currently also macro + // arguments are ignored. Optimally, macros should be treated like function + // calls, i.e. the arguments account to the complexity so that the overall + // complexity of this function is 2 (1 for the if statement above + 1 for + // the if statement in the argument). + SomeMacro(if (1) { noop; }); +}