[clang-tidy]Add new check readability-avoid-nested-conditional-operator (#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.
This commit is contained in:
Congcong Cai 2024-01-15 18:19:47 +08:00 committed by GitHub
parent 3dff20cfa2
commit 8e21557d04
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 140 additions and 0 deletions

View File

@ -0,0 +1,52 @@
//===--- 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/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/Basic/DiagnosticIDs.h"
using namespace clang::ast_matchers;
namespace clang::tidy::readability {
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(),
"conditional operator is used as sub-expression of parent conditional "
"operator, refrain from using nested conditional operators");
diag(CO->getBeginLoc(), "parent conditional operator here",
DiagnosticIDs::Note);
}
} // namespace clang::tidy::readability

View File

@ -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_AVOID_NESTED_CONDITIONAL_OPERATOR_CHECK_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_AVOID_NESTED_CONDITIONAL_OPERATOR_CHECK_H
#include "../ClangTidyCheck.h"
namespace clang::tidy::readability {
/// Identifies instances of nested conditional operators in the code.
///
/// 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_AVOID_NESTED_CONDITIONAL_OPERATOR_CHECK_H

View File

@ -5,6 +5,7 @@ set(LLVM_LINK_COMPONENTS
add_clang_library(clangTidyReadabilityModule
AvoidConstParamsInDecls.cpp
AvoidNestedConditionalOperatorCheck.cpp
AvoidReturnWithVoidValueCheck.cpp
AvoidUnconditionalPreprocessorIfCheck.cpp
BracesAroundStatementsCheck.cpp

View File

@ -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 @@ public:
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>(

View File

@ -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.
Identifies instances of nested conditional operators in the code.
- New :doc:`readability-avoid-return-with-void-value
<clang-tidy/checks/readability/avoid-return-with-void-value>` check.

View File

@ -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>`,
: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"

View File

@ -0,0 +1,21 @@
.. title:: clang-tidy - readability-avoid-nested-conditional-operator
readability-avoid-nested-conditional-operator
=============================================
Identifies instances of nested conditional operators in the code.
Nested conditional operators, also known as ternary operators, can contribute
to reduced code readability and comprehension. So they should be split as
several statements and stored the intermediate results in temporary variable.
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://www.autosar.org/fileadmin/standards/R22-11/AP/AUTOSAR_RS_CPP14Guidelines.pdf>`_.

View File

@ -0,0 +1,24 @@
// RUN: %check_clang_tidy %s readability-avoid-nested-conditional-operator %t
int NestInConditional = (true ? true : false) ? 1 : 2;
// CHECK-MESSAGES: :[[@LINE-1]]:26: warning: conditional operator is used as sub-expression of parent conditional operator, refrain from using nested conditional operators
// CHECK-MESSAGES: :[[@LINE-2]]:25: note: parent conditional operator here
int NestInTrue = true ? (true ? 1 : 2) : 2;
// CHECK-MESSAGES: :[[@LINE-1]]:26: warning: conditional operator is used as sub-expression of parent conditional operator, refrain from using nested conditional operators
// CHECK-MESSAGES: :[[@LINE-2]]:18: note: parent conditional operator here
int NestInFalse = true ? 1 : true ? 1 : 2;
// CHECK-MESSAGES: :[[@LINE-1]]:30: warning: conditional operator is used as sub-expression of parent conditional operator, refrain from using nested conditional operators
// CHECK-MESSAGES: :[[@LINE-2]]:19: note: parent conditional operator here
int NestInFalse2 = true ? 1 : (true ? 1 : 2);
// CHECK-MESSAGES: :[[@LINE-1]]:32: warning: conditional operator is used as sub-expression of parent conditional operator, refrain from using nested conditional operators
// CHECK-MESSAGES: :[[@LINE-2]]:20: note: parent conditional operator here
int NestWithParensis = true ? 1 : ((((true ? 1 : 2))));
// CHECK-MESSAGES: :[[@LINE-1]]:39: warning: conditional operator is used as sub-expression of parent conditional operator, refrain from using nested conditional operators
// CHECK-MESSAGES: :[[@LINE-2]]:24: note: parent conditional operator here
#define CONDITIONAL_EXPR (true ? 1 : 2)
// not diag for macro since it will not reduce readability
int NestWithMacro = true ? CONDITIONAL_EXPR : 2;