[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:
parent
3dff20cfa2
commit
8e21557d04
@ -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
|
@ -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
|
@ -5,6 +5,7 @@ set(LLVM_LINK_COMPONENTS
|
||||
|
||||
add_clang_library(clangTidyReadabilityModule
|
||||
AvoidConstParamsInDecls.cpp
|
||||
AvoidNestedConditionalOperatorCheck.cpp
|
||||
AvoidReturnWithVoidValueCheck.cpp
|
||||
AvoidUnconditionalPreprocessorIfCheck.cpp
|
||||
BracesAroundStatementsCheck.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 @@ 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>(
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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>`_.
|
@ -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;
|
Loading…
x
Reference in New Issue
Block a user