[PATCH] D36836: [clang-tidy] Implement readability-function-cognitive-complexity check

Aaron Ballman via Phabricator via cfe-commits cfe-commits at lists.llvm.org
Sat Oct 21 06:30:28 PDT 2017


aaron.ballman added inline comments.


================
Comment at: clang-tidy/readability/FunctionCognitiveComplexityCheck.cpp:77
+  const std::array<const StringRef, 4> Msgs = {{
+      // FIXME: these messages somehow trigger an assertion:
+      // Fix conflicts with existing fix! The new replacement overlaps with an
----------------
lebedev.ri wrote:
> aaron.ballman wrote:
> > Are you intending to fix this -- that sounds rather serious?
> I do intend to fix it, as soon as i'm aware of how to fix this.
> Specifically, https://reviews.llvm.org/D36836#889350
> ```
> Question:
> Is it expected that clang-tidy somehow parses the DiagnosticIDs::Note's as FixIt's, and thus fails with the following assert:
> ...
> ```
> I.e. what is the proper way to fix this, should i change the message, or change the code not to parse the message as FixIt?
This is one for @alexfh to answer, I think. My gut says that the code should not be parsing notes as fixits (as a separate commit).


================
Comment at: clang-tidy/readability/FunctionCognitiveComplexityCheck.cpp:99
+  /// details nessesary
+  struct Detail final {
+    const SourceLocation Loc;     /// What caused the increment?
----------------
lebedev.ri wrote:
> aaron.ballman wrote:
> > I don't think the `final` adds value here.
> I don't think it hurts, either?
> I *personally* prefer to specify it always, and remove if subclassing is needed.
> But if you insist i can certainly drop it
That's not the typical coding style for the project, so I would prefer to leave it off. It's reasonable to use when there are vtables involved, however.


================
Comment at: clang-tidy/readability/FunctionCognitiveComplexityCheck.cpp:102
+    const unsigned short Nesting; /// How deeply nested is Loc located?
+    const Criteria C : 3;         /// The criteria of the increment
+
----------------
lebedev.ri wrote:
> aaron.ballman wrote:
> > Why is this turned into a bit-field? If that is important, it should be of type `unsigned` to prevent differences between compilers (MSVC treats bit-fields differently than GCC and Clang).
> The comment right after this member variable should have explained it:
> ```
> /// Criteria C is a bitfield. Even though Criteria is an unsigned char; and
> /// only using 3 bits will still result in padding, the fact that it is a
> /// bitfield is a reminder that it is important to min(sizeof(Detail))
> ```
> It is already `unsigned`:
> ```
> enum Criteria : unsigned char {
> ```
No, it's underlying type is `unsigned char`, but the type is `Criteria`. I meant the field itself needs to be `unsigned`. However, since that means you have to do more type casting, I think making the type `uint8_t` instead of a bit-field would be an improvement.


================
Comment at: clang-tidy/readability/FunctionCognitiveComplexityCheck.cpp:118
+
+      unsigned MsgId;           /// The id of the message to output
+      unsigned short Increment; /// How much of an increment?
----------------
aaron.ballman wrote:
> We use `//` instead of `///` unless we're specifically documenting something for doxygen.
This change is marked as done but wasn't applied consistently. For instance, see lines 130-131 where it switches between uses. I would use `//` consistently everywhere unless documenting a public interface in a header.


================
Comment at: clang-tidy/readability/FunctionCognitiveComplexityCheck.cpp:111
+  };
+  static_assert(sizeof(Detail) <= 8, "it's best to keep the size minimal");
+
----------------
This `static_assert` should come with some further comments explaining that size being small is good, but not critical for correctness.


================
Comment at: clang-tidy/readability/FunctionCognitiveComplexityCheck.cpp:212
+  bool TraverseStmtWithIncreasedNestingLevel(Stmt *Node) {
+    bool ShouldContinue;
+    ++CurrentNestingLevel;
----------------
Declare with its initialization below.


================
Comment at: clang-tidy/readability/FunctionCognitiveComplexityCheck.cpp:220
+  bool TraverseDeclWithIncreasedNestingLevel(Decl *Node) {
+    bool ShouldContinue;
+    ++CurrentNestingLevel;
----------------
Declare with its initialization below.


================
Comment at: clang-tidy/readability/FunctionCognitiveComplexityCheck.cpp:250
+
+    /// If this IfStmt is *NOT* "else if", then only the body (i.e. "Then" and
+    /// "Else" are traversed with increased Nesting level.
----------------
Missing a closing paren in the comment.


================
Comment at: clang-tidy/readability/FunctionCognitiveComplexityCheck.cpp:275-276
+
+    if (isa<IfStmt>(Node->getElse()))
+      return TraverseIfStmt(dyn_cast<IfStmt>(Node->getElse()), true);
+
----------------
Would be better to just get the AST node directly instead of `isa<>()` and `dyn_cast<>()`.

`if (const auto *E = dyn_cast<IfStmt>(Node->getElse()))`


================
Comment at: clang-tidy/readability/FunctionCognitiveComplexityCheck.cpp:317
+                                                                               \
+    /** We might encounter function call, which starts a new sequence, thus    \
+     ** we need to save the current previous binary operator. */               \
----------------
encounter function call -> encounter a function call


================
Comment at: clang-tidy/readability/FunctionCognitiveComplexityCheck.cpp:325
+    const bool ShouldContinue = Base::TraverseBin##CLASS(Op);                  \
+    /** And restore the previous binary operator, which might be nonexistant.  \
+     */                                                                        \
----------------
typo: nonexistent


================
Comment at: clang-tidy/readability/FunctionCognitiveComplexityCheck.cpp:332
+
+  /// In a sequence of binary logical operators, if new operator is different
+  /// from the previous-one, then the cognitive complexity is increased.
----------------
if new operator -> if the new operator


================
Comment at: clang-tidy/readability/FunctionCognitiveComplexityCheck.cpp:333
+  /// In a sequence of binary logical operators, if new operator is different
+  /// from the previous-one, then the cognitive complexity is increased.
+  TraverseBinOp(LAnd);
----------------
previous-one -> previous one


================
Comment at: clang-tidy/readability/FunctionCognitiveComplexityCheck.cpp:338
+  /// It would make sense for the function call to start the new binary
+  /// operator sequence, thus let's make sure that it creates new stack frame.
+  bool TraverseCallExpr(CallExpr *Node) {
----------------
creates new stack -> creates a new stack


================
Comment at: clang-tidy/readability/FunctionCognitiveComplexityCheck.cpp:347
+    BinaryOperatorsStack.emplace();
+    const bool ShouldContinue = Base::TraverseCallExpr(Node);
+    /// And remove the top frame.
----------------
Can drop the `const`.


================
Comment at: clang-tidy/readability/FunctionCognitiveComplexityCheck.cpp:370
+    /// if, else if, else  are handled in TraverseIfStmt(),
+    /// FIXME: each method in a recursion cycle.
+    case Stmt::ConditionalOperatorClass:
----------------
This FIXME doesn't describe what needs to be fixed very well, you might want to expound (or fix it).


================
Comment at: clang-tidy/readability/FunctionCognitiveComplexityCheck.cpp:384
+      /// but they are not supported in C++ or C.
+      /// regular break/continue do not increase cognitive complexity.
+      break;
----------------
regular -> Regular


================
Comment at: clang-tidy/readability/FunctionCognitiveComplexityCheck.cpp:391
+    switch (Node->getStmtClass()) {
+    /// if, else if, else  are handled in TraverseIfStmt(),
+    /// nested methods and such are handled in TraverseDecl.
----------------
else  are -> else are (remove extra space)


================
Comment at: clang-tidy/readability/FunctionCognitiveComplexityCheck.cpp:392
+    /// if, else if, else  are handled in TraverseIfStmt(),
+    /// nested methods and such are handled in TraverseDecl.
+    case Stmt::ConditionalOperatorClass:
----------------
nested -> Nested


================
Comment at: clang-tidy/readability/FunctionCognitiveComplexityCheck.cpp:411
+    switch (Node->getStmtClass()) {
+    /// if, else if, else  are handled in TraverseIfStmt().
+    case Stmt::ConditionalOperatorClass:
----------------
extra space between else and are


================
Comment at: clang-tidy/readability/FunctionCognitiveComplexityCheck.cpp:433
+
+    /// if we have found any reasons, let's account it.
+    if (Reasons & CognitiveComplexity::Criteria::All)
----------------
if -> If


================
Comment at: clang-tidy/readability/FunctionCognitiveComplexityCheck.cpp:444
+
+  /// The second non-standard parameter MainAnalyzedFunction is needed to
+  /// differentiate between the cases where TraverseDecl() is the entry point
----------------
The second non-standard parameter -> The parameter


================
Comment at: clang-tidy/readability/FunctionCognitiveComplexityCheck.cpp:448
+  /// called from the FunctionASTVisitor itself.
+  /// Explaination: if we get a function definition (e.g. constructor,
+  /// destructor, method), the Cognitive Complexity specification states that
----------------
Explaination -> Explanation


================
Comment at: clang-tidy/readability/FunctionCognitiveComplexityCheck.cpp:495
+  Finder->addMatcher(
+      functionDecl(unless(anyOf(isInstantiated(), isImplicit()))).bind("func"),
+      this);
----------------
You only need to match function *definitions* (not declarations), correct? It might be useful to specify that in the matcher so that it matches less code.


================
Comment at: clang-tidy/readability/FunctionCognitiveComplexityCheck.cpp:513-514
+
+  // Now, output all the basic increments, of which this Cognitive Complexity
+  // consists of
+  for (const auto &Detail : Visitor.CC.Details) {
----------------
I would reword this to: Output all the basic increments of complexity.


================
Comment at: clang-tidy/readability/FunctionCognitiveComplexityCheck.cpp:520
+    assert(MsgId < Msgs.size() && "MsgId should always be valid");
+    // Increase on the other hand, can be 0.
+
----------------
Increase on the -> Increase, on the


================
Comment at: docs/clang-tidy/checks/readability-function-cognitive-complexity.rst:8
+
+The metric is implemented as per `COGNITIVE COMPLEXITY by SonarSource
+<https://www.sonarsource.com/docs/CognitiveComplexity.pdf>`_ specification
----------------
as per -> as per the


================
Comment at: docs/clang-tidy/checks/readability-function-cognitive-complexity.rst:85
+
+And this is where the previous basic building block, `Nesting level`_, matters.
+The following structures increase the function's Cognitive Complexity metric by
----------------
And this -> This


================
Comment at: docs/clang-tidy/checks/readability-function-cognitive-complexity.rst:141
+   * `preprocessor conditionals` (``#ifdef``, ``#if``, ``#elif``, ``#else``,
+     ``#endif``) are not accounted for. Could be done.
+   * `each method in a recursion cycle` is not accounted for. It can't be fully
----------------
I would drop `Could be done.`


Repository:
  rL LLVM

https://reviews.llvm.org/D36836





More information about the cfe-commits mailing list