Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[clang-tidy]Add new check readability-avoid-nested-conditional-operator #78022

Merged

Conversation

HerrCai0907
Copy link
Contributor

Finds nested conditional operator.
Nested conditional operators lead code hard to understand, so they should be splited as several statement and stored in temporary varibale.

Finds nested conditional operator.
Nested conditional operators lead code hard to understand, so they should be
splited as several statement and stored in temporary varibale.
@llvmbot
Copy link
Collaborator

llvmbot commented Jan 13, 2024

@llvm/pr-subscribers-clang-tidy

Author: Congcong Cai (HerrCai0907)

Changes

Finds nested conditional operator.
Nested conditional operators lead code hard to understand, so they should be splited as several statement and stored in temporary varibale.


Full diff: https://github.com/llvm/llvm-project/pull/78022.diff

8 Files Affected:

  • (added) clang-tools-extra/clang-tidy/readability/AvoidNestedConditionalOperatorCheck.cpp (+56)
  • (added) clang-tools-extra/clang-tidy/readability/AvoidNestedConditionalOperatorCheck.h (+33)
  • (modified) clang-tools-extra/clang-tidy/readability/CMakeLists.txt (+1)
  • (modified) clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp (+3)
  • (modified) clang-tools-extra/docs/ReleaseNotes.rst (+5)
  • (modified) clang-tools-extra/docs/clang-tidy/checks/list.rst (+1)
  • (added) clang-tools-extra/docs/clang-tidy/checks/readability/avoid-nested-conditional-operator.rst (+20)
  • (added) clang-tools-extra/test/clang-tidy/checkers/readability/avoid-nested-conditional-operator.cpp (+23)
diff --git a/clang-tools-extra/clang-tidy/readability/AvoidNestedConditionalOperatorCheck.cpp b/clang-tools-extra/clang-tidy/readability/AvoidNestedConditionalOperatorCheck.cpp
new file mode 100644
index 00000000000000..a0278c3ae32fa7
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/readability/AvoidNestedConditionalOperatorCheck.cpp
@@ -0,0 +1,56 @@
+//===--- AvoidNestedConditionalOperatorCheck.cpp - clang-tidy ----------===//
+//
+// 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 "AvoidNestedConditionalOperatorCheck.h"
+#include "clang/AST/Expr.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Basic/DiagnosticIDs.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::readability {
+
+namespace {
+constexpr const char *Description = "don't use nested conditional operator";
+constexpr const char *OutSideConditionalOperatorNote =
+    "outside conditional operator here";
+} // namespace
+
+void AvoidNestedConditionalOperatorCheck::registerMatchers(
+    MatchFinder *Finder) {
+  Finder->addMatcher(
+      conditionalOperator(
+          anyOf(
+              hasCondition(ignoringParenCasts(
+                  conditionalOperator().bind("nested-conditional-operator"))),
+              hasTrueExpression(ignoringParenCasts(
+                  conditionalOperator().bind("nested-conditional-operator"))),
+              hasFalseExpression(ignoringParenCasts(
+                  conditionalOperator().bind("nested-conditional-operator")))))
+          .bind("conditional-operator"),
+      this);
+}
+
+void AvoidNestedConditionalOperatorCheck::check(
+    const MatchFinder::MatchResult &Result) {
+  const auto *CO =
+      Result.Nodes.getNodeAs<ConditionalOperator>("conditional-operator");
+  const auto *NCO = Result.Nodes.getNodeAs<ConditionalOperator>(
+      "nested-conditional-operator");
+  assert(CO);
+  assert(NCO);
+
+  if (CO->getBeginLoc().isMacroID() || NCO->getBeginLoc().isMacroID())
+    return;
+
+  diag(NCO->getBeginLoc(), Description);
+  diag(CO->getBeginLoc(), OutSideConditionalOperatorNote, DiagnosticIDs::Note);
+}
+
+} // namespace clang::tidy::readability
diff --git a/clang-tools-extra/clang-tidy/readability/AvoidNestedConditionalOperatorCheck.h b/clang-tools-extra/clang-tidy/readability/AvoidNestedConditionalOperatorCheck.h
new file mode 100644
index 00000000000000..2f82ea86cd849d
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/readability/AvoidNestedConditionalOperatorCheck.h
@@ -0,0 +1,33 @@
+//===--- AvoidNestedConditionalOperatorCheck.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_AVOIDNestedConditionalOperatorCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_AVOIDNestedConditionalOperatorCHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::readability {
+
+/// Finds nested conditional operator.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/readability/avoid-nested-conditional-operator.html
+class AvoidNestedConditionalOperatorCheck : public ClangTidyCheck {
+public:
+  AvoidNestedConditionalOperatorCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+  std::optional<TraversalKind> getCheckTraversalKind() const override {
+    return TK_IgnoreUnlessSpelledInSource;
+  }
+};
+
+} // namespace clang::tidy::readability
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_AVOIDNestedConditionalOperatorCHECK_H
diff --git a/clang-tools-extra/clang-tidy/readability/CMakeLists.txt b/clang-tools-extra/clang-tidy/readability/CMakeLists.txt
index 408c822b861c5f..fa571d5dd7650d 100644
--- a/clang-tools-extra/clang-tidy/readability/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/readability/CMakeLists.txt
@@ -5,6 +5,7 @@ set(LLVM_LINK_COMPONENTS
 
 add_clang_library(clangTidyReadabilityModule
   AvoidConstParamsInDecls.cpp
+  AvoidNestedConditionalOperatorCheck.cpp
   AvoidReturnWithVoidValueCheck.cpp
   AvoidUnconditionalPreprocessorIfCheck.cpp
   BracesAroundStatementsCheck.cpp
diff --git a/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp b/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
index 0b0aad7c0dcb36..f769752c5de5fa 100644
--- a/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
@@ -10,6 +10,7 @@
 #include "../ClangTidyModule.h"
 #include "../ClangTidyModuleRegistry.h"
 #include "AvoidConstParamsInDecls.h"
+#include "AvoidNestedConditionalOperatorCheck.h"
 #include "AvoidReturnWithVoidValueCheck.h"
 #include "AvoidUnconditionalPreprocessorIfCheck.h"
 #include "BracesAroundStatementsCheck.h"
@@ -64,6 +65,8 @@ class ReadabilityModule : public ClangTidyModule {
   void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
     CheckFactories.registerCheck<AvoidConstParamsInDecls>(
         "readability-avoid-const-params-in-decls");
+    CheckFactories.registerCheck<AvoidNestedConditionalOperatorCheck>(
+        "readability-avoid-nested-conditional-operator");
     CheckFactories.registerCheck<AvoidReturnWithVoidValueCheck>(
         "readability-avoid-return-with-void-value");
     CheckFactories.registerCheck<AvoidUnconditionalPreprocessorIfCheck>(
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 3437b6cf9b59e9..7ed4bcf1ff92ac 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -224,6 +224,11 @@ New checks
   Recommends the smallest possible underlying type for an ``enum`` or ``enum``
   class based on the range of its enumerators.
 
+- New :doc:`readability-avoid-nested-conditional-operator
+  <clang-tidy/checks/readability/avoid-nested-conditional-operator>` check.
+
+  Finds nested conditional operator.
+
 - New :doc:`readability-reference-to-constructed-temporary
   <clang-tidy/checks/readability/reference-to-constructed-temporary>` check.
 
diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst
index 2f86121ad87299..fd65bdc1e43f8d 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -337,6 +337,7 @@ Clang-Tidy Checks
    :doc:`portability-simd-intrinsics <portability/simd-intrinsics>`,
    :doc:`portability-std-allocator-const <portability/std-allocator-const>`,
    :doc:`readability-avoid-const-params-in-decls <readability/avoid-const-params-in-decls>`, "Yes"
+   :doc:`readability-avoid-nested-conditional-operator <readability/avoid-nested-conditional-operator>`, "Yes"
    :doc:`readability-avoid-return-with-void-value <readability/avoid-return-with-void-value>`,
    :doc:`readability-avoid-unconditional-preprocessor-if <readability/avoid-unconditional-preprocessor-if>`,
    :doc:`readability-braces-around-statements <readability/braces-around-statements>`, "Yes"
diff --git a/clang-tools-extra/docs/clang-tidy/checks/readability/avoid-nested-conditional-operator.rst b/clang-tools-extra/docs/clang-tidy/checks/readability/avoid-nested-conditional-operator.rst
new file mode 100644
index 00000000000000..db4ce7de87a9e7
--- /dev/null
+++ b/clang-tools-extra/docs/clang-tidy/checks/readability/avoid-nested-conditional-operator.rst
@@ -0,0 +1,20 @@
+.. title:: clang-tidy - readability-avoid-nested-conditional-operator
+
+readability-avoid-nested-conditional-operator
+=================================================
+
+Finds nested conditional operator.
+
+Nested conditional operators lead code hard to understand, so they should be
+splited as several statement and stored in temporary varibale.
+
+Examples:
+
+.. code-block:: c++
+
+  int NestInConditional = (condition1 ? true1 : false1) ? true2 : false2;
+  int NestInTrue = condition1 ? (condition2 ? true1 : false1) : false2;
+  int NestInFalse = condition1 ? true1 : condition2 ? true2 : false1;
+
+This check implements part of `AUTOSAR C++14 Rule A5-16-1
+<https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rc-constref>`_.
diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/avoid-nested-conditional-operator.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/avoid-nested-conditional-operator.cpp
new file mode 100644
index 00000000000000..fd2d592544ddd0
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/readability/avoid-nested-conditional-operator.cpp
@@ -0,0 +1,23 @@
+// RUN: %check_clang_tidy %s readability-avoid-nested-conditional-operator %t
+
+int NestInConditional = (true ? true : false) ? 1 : 2;
+// CHECK-MESSAGES: :[[@LINE-1]]:26: warning: don't use nested conditional operator
+// CHECK-MESSAGES: :[[@LINE-2]]:25: note: outside conditional operator here
+
+int NestInTrue = true ? (true ? 1 : 2) : 2;
+// CHECK-MESSAGES: :[[@LINE-1]]:26: warning: don't use nested conditional operator
+// CHECK-MESSAGES: :[[@LINE-2]]:18: note: outside conditional operator here
+
+int NestInFalse = true ? 1 : true ? 1 : 2;
+// CHECK-MESSAGES: :[[@LINE-1]]:30: warning: don't use nested conditional operator
+// CHECK-MESSAGES: :[[@LINE-2]]:19: note: outside conditional operator here
+int NestInFalse2 = true ? 1 : (true ? 1 : 2);
+// CHECK-MESSAGES: :[[@LINE-1]]:32: warning: don't use nested conditional operator
+// CHECK-MESSAGES: :[[@LINE-2]]:20: note: outside conditional operator here
+
+int NestWithParensis = true ? 1 : ((((true ? 1 : 2))));
+// CHECK-MESSAGES: :[[@LINE-1]]:39: warning: don't use nested conditional operator
+// CHECK-MESSAGES: :[[@LINE-2]]:24: note: outside conditional operator here
+
+#define CONDITIONAL_EXPR (true ? 1 : 2)
+int NestWithMacro = true ? CONDITIONAL_EXPR : 2;

Copy link
Member

@PiotrZSL PiotrZSL left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just few nits, from functional point of view check is good even now.

Copy link
Member

@PiotrZSL PiotrZSL left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM.
Sync first line of release notes, doc, doxygen and could land.

- New :doc:`readability-avoid-nested-conditional-operator
<clang-tidy/checks/readability/avoid-nested-conditional-operator>` check.

Finds nested conditional operator.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// Identifies instances of nested conditional operators in the code.

@HerrCai0907 HerrCai0907 merged commit 8e21557 into main Jan 15, 2024
4 of 5 checks passed
@HerrCai0907 HerrCai0907 deleted the users/ccc/create-AvoidnestedternaryconditionaloperatorCheck branch January 15, 2024 10:19
@@ -0,0 +1,52 @@
//===--- AvoidNestedConditionalOperatorCheck.cpp - clang-tidy ----------===//
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add -

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed in 10602c2

justinfargnoli pushed a commit to justinfargnoli/llvm-project that referenced this pull request Jan 28, 2024
…or (llvm#78022)

Finds nested conditional operator.
Nested conditional operators lead code hard to understand, so they
should be splited as several statement and stored in temporary varibale.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants