Skip to content
1 change: 1 addition & 0 deletions clang-tools-extra/clang-tidy/readability/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ add_clang_library(clangTidyReadabilityModule STATIC
AvoidReturnWithVoidValueCheck.cpp
AvoidUnconditionalPreprocessorIfCheck.cpp
BracesAroundStatementsCheck.cpp
ConditionalToIfCheck.cpp
ConstReturnTypeCheck.cpp
ContainerContainsCheck.cpp
ContainerDataPointerCheck.cpp
Expand Down
89 changes: 89 additions & 0 deletions clang-tools-extra/clang-tidy/readability/ConditionalToIfCheck.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// This check detects ternary conditional (?:) expressions and replaces them
// with equivalent if/else statements to improve code readability.
//
// Example:
//
// int x = cond ? 1 : 2;
//
// Becomes:
//
// int x;
// if (cond)
// x = 1;
// else
// x = 2;
//
//===----------------------------------------------------------------------===//

#include "ConditionalToIfCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Lex/Lexer.h" // <-- ADD THIS INCLUDE
#include "clang/Rewrite/Core/Rewriter.h"

using namespace clang::ast_matchers;

namespace clang::tidy::readability {

void ConditionalToIfCheck::registerMatchers(MatchFinder *Finder) {
// Match ternary conditional operators (?:)
Finder->addMatcher(
conditionalOperator(hasTrueExpression(expr().bind("trueExpr")),
hasFalseExpression(expr().bind("falseExpr")),
hasCondition(expr().bind("condExpr")))
.bind("ternary"),
this);
}

void ConditionalToIfCheck::check(const MatchFinder::MatchResult &Result) {
const auto *Ternary = Result.Nodes.getNodeAs<ConditionalOperator>("ternary");
if (!Ternary)
return;

const Expr *Cond = Result.Nodes.getNodeAs<Expr>("condExpr");
const Expr *TrueExpr = Result.Nodes.getNodeAs<Expr>("trueExpr");
const Expr *FalseExpr = Result.Nodes.getNodeAs<Expr>("falseExpr");

if (!Cond || !TrueExpr || !FalseExpr)
return;

const SourceManager &SM = *Result.SourceManager;

auto Diag =
diag(Ternary->getBeginLoc(),
"replace ternary operator with if/else statement for readability");

// Extract source text for condition, true and false expressions
const std::string CondStr = Lexer::getSourceText(CharSourceRange::getTokenRange(
Cond->getSourceRange()),
SM, Result.Context->getLangOpts())
.str();

const std::string TrueStr =
Lexer::getSourceText(
CharSourceRange::getTokenRange(TrueExpr->getSourceRange()), SM,
Result.Context->getLangOpts())
.str();

const std::string FalseStr =
Lexer::getSourceText(
CharSourceRange::getTokenRange(FalseExpr->getSourceRange()), SM,
Result.Context->getLangOpts())
.str();

// Construct the replacement code
const std::string Replacement =
"{ if (" + CondStr + ") " + TrueStr + "; else " + FalseStr + "; }";

Diag << FixItHint::CreateReplacement(Ternary->getSourceRange(), Replacement);
}

} // namespace clang::tidy::readability
34 changes: 34 additions & 0 deletions clang-tools-extra/clang-tidy/readability/ConditionalToIfCheck.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// This file defines the ConditionalToIfCheck class, which converts conditional
// (?:) expressions into equivalent if/else statements for improved readability.
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_CONDITIONALTOIFCHECK_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_CONDITIONALTOIFCHECK_H

#include "../ClangTidyCheck.h"

namespace clang::tidy::readability {

/// A readability check that replaces ternary operators with equivalent
/// if/else statements.
class ConditionalToIfCheck : public ClangTidyCheck {
public:
ConditionalToIfCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}

void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
};

} // namespace clang::tidy::readability

#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_CONDITIONALTOIFCHECK_H
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include "AvoidReturnWithVoidValueCheck.h"
#include "AvoidUnconditionalPreprocessorIfCheck.h"
#include "BracesAroundStatementsCheck.h"
#include "ConditionalToIfCheck.h"
#include "ConstReturnTypeCheck.h"
#include "ContainerContainsCheck.h"
#include "ContainerDataPointerCheck.h"
Expand Down Expand Up @@ -84,6 +85,8 @@ class ReadabilityModule : public ClangTidyModule {
"readability-avoid-unconditional-preprocessor-if");
CheckFactories.registerCheck<BracesAroundStatementsCheck>(
"readability-braces-around-statements");
CheckFactories.registerCheck<ConditionalToIfCheck>(
"readability-conditional-to-if");
CheckFactories.registerCheck<ConstReturnTypeCheck>(
"readability-const-return-type");
CheckFactories.registerCheck<ContainerContainsCheck>(
Expand Down