https://github.com/DheerajAgarwal1234 updated https://github.com/llvm/llvm-project/pull/166822
>From 39db2936dd36c8631c190610314079d46bcb10c7 Mon Sep 17 00:00:00 2001 From: Dheeraj <[email protected]> Date: Thu, 6 Nov 2025 23:55:16 +0530 Subject: [PATCH 1/7] refactoring idea --- clang-tools-extra/CMakeLists.txt | 1 + .../clang-convert-ternary-if/CMakeLists.txt | 20 ++++ .../ConvertTernaryIf.cpp | 111 ++++++++++++++++++ .../ConvertTernaryIf.h | 39 ++++++ .../clang-convert-ternary-if/ToolMain.cpp | 80 +++++++++++++ 5 files changed, 251 insertions(+) create mode 100644 clang-tools-extra/clang-convert-ternary-if/CMakeLists.txt create mode 100644 clang-tools-extra/clang-convert-ternary-if/ConvertTernaryIf.cpp create mode 100644 clang-tools-extra/clang-convert-ternary-if/ConvertTernaryIf.h create mode 100644 clang-tools-extra/clang-convert-ternary-if/ToolMain.cpp diff --git a/clang-tools-extra/CMakeLists.txt b/clang-tools-extra/CMakeLists.txt index 87050db4e0e75..c0e6888b6209a 100644 --- a/clang-tools-extra/CMakeLists.txt +++ b/clang-tools-extra/CMakeLists.txt @@ -21,6 +21,7 @@ add_subdirectory(clang-apply-replacements) add_subdirectory(clang-reorder-fields) add_subdirectory(modularize) add_subdirectory(clang-tidy) +add_subdirectory(clang-convert-ternary-if) add_subdirectory(clang-change-namespace) add_subdirectory(clang-doc) diff --git a/clang-tools-extra/clang-convert-ternary-if/CMakeLists.txt b/clang-tools-extra/clang-convert-ternary-if/CMakeLists.txt new file mode 100644 index 0000000000000..780fca4405e64 --- /dev/null +++ b/clang-tools-extra/clang-convert-ternary-if/CMakeLists.txt @@ -0,0 +1,20 @@ +#===- CMakeLists.txt - clang-convert-ternary-if tool ---------------------===# +# This defines the build configuration for the standalone refactoring tool. + +add_clang_executable(clang-convert-ternary-if + ConvertTernaryIf.cpp + ToolMain.cpp +) + +target_link_libraries(clang-convert-ternary-if + PRIVATE + clangTooling + clangBasic + clangAST + clangASTMatchers + clangRewrite + clangFrontend +) + +install(TARGETS clang-convert-ternary-if RUNTIME DESTINATION bin) + diff --git a/clang-tools-extra/clang-convert-ternary-if/ConvertTernaryIf.cpp b/clang-tools-extra/clang-convert-ternary-if/ConvertTernaryIf.cpp new file mode 100644 index 0000000000000..8843510d08b72 --- /dev/null +++ b/clang-tools-extra/clang-convert-ternary-if/ConvertTernaryIf.cpp @@ -0,0 +1,111 @@ +//===--- ConvertTernaryIf.cpp ---------------------------------------------===// +// +// Implements a tool that refactors between ternary (?:) expressions +// and equivalent if/else statements. +// +// Usage Example: +// clang-convert-ternary-if test.cpp -- +// +//===----------------------------------------------------------------------===// + +#include "ConvertTernaryIf.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Rewrite/Core/Rewriter.h" +#include "clang/Lex/Lexer.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace clang::ast_matchers; + +namespace clang { +namespace convertternary { + + +// Callback: Called when a match is found +void ConvertTernaryIfCallback::run(const MatchFinder::MatchResult &Result) { + //Initialize the Rewriter safely (fixes segmentation fault) + if (!IsInitialized) { + if (Result.SourceManager && Result.Context) { + TheRewriter.setSourceMgr(*Result.SourceManager, + Result.Context->getLangOpts()); + IsInitialized = true; + llvm::errs() << "Rewriter initialized successfully.\n"; + } else { + llvm::errs() << "Error: Missing SourceManager or Context.\n"; + return; + } + } + + const auto *CondOp = Result.Nodes.getNodeAs<ConditionalOperator>("condOp"); + const auto *IfStmtNode = Result.Nodes.getNodeAs<IfStmt>("ifStmt"); + + const SourceManager &SM = *Result.SourceManager; + + // === Convert Ternary -> If === + if (CondOp) { + const Expr *Cond = CondOp->getCond(); + const Expr *TrueExpr = CondOp->getTrueExpr(); + const Expr *FalseExpr = CondOp->getFalseExpr(); + + auto getText = [&](const Expr *E) -> std::string { + return Lexer::getSourceText(CharSourceRange::getTokenRange(E->getSourceRange()), SM, + Result.Context->getLangOpts()) + .str(); + }; + + std::string CondText = getText(Cond); + std::string TrueText = getText(TrueExpr); + std::string FalseText = getText(FalseExpr); + + std::string IfReplacement = "if (" + CondText + ") {\n " + TrueText + + ";\n} else {\n " + FalseText + ";\n}"; + + TheRewriter.ReplaceText(CondOp->getSourceRange(), IfReplacement); + llvm::errs() << "Converted ternary to if/else.\n"; + } + + // === Convert If -> Ternary === + if (IfStmtNode) { + const Expr *Cond = IfStmtNode->getCond(); + const Stmt *Then = IfStmtNode->getThen(); + const Stmt *Else = IfStmtNode->getElse(); + + if (!Then || !Else) + return; + + auto getTextStmt = [&](const Stmt *S) -> std::string { + return Lexer::getSourceText(CharSourceRange::getTokenRange(S->getSourceRange()), SM, + Result.Context->getLangOpts()) + .str(); + }; + + std::string CondText = Lexer::getSourceText( + CharSourceRange::getTokenRange(Cond->getSourceRange()), SM, + Result.Context->getLangOpts()) + .str(); + + std::string ThenText = getTextStmt(Then); + std::string ElseText = getTextStmt(Else); + + std::string Ternary = + "(" + CondText + ") ? " + ThenText + " : " + ElseText + ";"; + + TheRewriter.ReplaceText(IfStmtNode->getSourceRange(), Ternary); + llvm::errs() << "Converted if/else to ternary.\n"; + } +} + +// === Register AST Matchers === +void setupMatchers(MatchFinder &Finder, ConvertTernaryIfCallback &Callback) { + Finder.addMatcher( + conditionalOperator(isExpansionInMainFile()).bind("condOp"), &Callback); + + Finder.addMatcher( + ifStmt(hasThen(stmt()), hasElse(stmt()), isExpansionInMainFile()) + .bind("ifStmt"), + &Callback); +} + +} // namespace convertternary +} // namespace clang + diff --git a/clang-tools-extra/clang-convert-ternary-if/ConvertTernaryIf.h b/clang-tools-extra/clang-convert-ternary-if/ConvertTernaryIf.h new file mode 100644 index 0000000000000..2d71cf5f8242d --- /dev/null +++ b/clang-tools-extra/clang-convert-ternary-if/ConvertTernaryIf.h @@ -0,0 +1,39 @@ +//===--- ConvertTernaryIf.h ------------------------------------*- C++ -*-===// +// +// This file declares the refactoring logic that converts +// ternary operators (?:) into if/else statements and vice versa. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_CONVERT_TERNARY_IF_H +#define LLVM_CLANG_CONVERT_TERNARY_IF_H + +#include "clang/AST/AST.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Tooling/Refactoring.h" +#include "clang/Rewrite/Core/Rewriter.h" + +namespace clang { +namespace convertternary { + +class ConvertTernaryIfCallback + : public ast_matchers::MatchFinder::MatchCallback { +public: + ConvertTernaryIfCallback(Rewriter &R) + : TheRewriter(R), IsInitialized(false) {} + + void run(const ast_matchers::MatchFinder::MatchResult &Result) override; + +private: + Rewriter &TheRewriter; + bool IsInitialized; + }; + +void setupMatchers(ast_matchers::MatchFinder &Finder, + ConvertTernaryIfCallback &Callback); + +} // namespace convertternary +} // namespace clang + +#endif + diff --git a/clang-tools-extra/clang-convert-ternary-if/ToolMain.cpp b/clang-tools-extra/clang-convert-ternary-if/ToolMain.cpp new file mode 100644 index 0000000000000..bf9a793309944 --- /dev/null +++ b/clang-tools-extra/clang-convert-ternary-if/ToolMain.cpp @@ -0,0 +1,80 @@ +//===--- ToolMain.cpp - Entry point for clang-convert-ternary-if ----------===// +// +// This tool runs the refactoring logic defined in ConvertTernaryIf.cpp. +// +// Usage: +// clang-convert-ternary-if <source-file> -- +// +// It prints the rewritten (refactored) source code to stdout. +// +//===----------------------------------------------------------------------===// + +#include "ConvertTernaryIf.h" +#include "clang/Tooling/CommonOptionsParser.h" +#include "clang/Tooling/Tooling.h" +#include "clang/Frontend/TextDiagnosticPrinter.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace clang::tooling; +using namespace clang::convertternary; +using namespace llvm; + +static llvm::cl::OptionCategory ToolCategory("convert-ternary-if options"); + +int main(int argc, const char **argv) { + // Parse command-line options + auto ExpectedParser = + CommonOptionsParser::create(argc, argv, ToolCategory, cl::ZeroOrMore); + if (!ExpectedParser) { + llvm::errs() << ExpectedParser.takeError(); + return 1; + } + + CommonOptionsParser &OptionsParser = ExpectedParser.get(); + ClangTool Tool(OptionsParser.getCompilations(), + OptionsParser.getSourcePathList()); + + // Set up the Rewriter and the Matcher + clang::Rewriter Rewrite; + ast_matchers::MatchFinder Finder; + ConvertTernaryIfCallback Callback(Rewrite); + setupMatchers(Finder, Callback); + + llvm::outs() << "=== Running clang-convert-ternary-if ===\n"; + int Result = Tool.run(newFrontendActionFactory(&Finder).get()); + + if (Result != 0) { + llvm::errs() << "Error: Tool execution failed.\n"; + return Result; + } + + // No changes? + if (Rewrite.buffer_begin() == Rewrite.buffer_end()) { + llvm::outs() << "No changes made.\n"; + return 0; + } + + llvm::outs() << "\n=== Rewritten Files ===\n"; + + // Print all rewritten files + for (auto It = Rewrite.buffer_begin(); It != Rewrite.buffer_end(); ++It) { + clang::FileID FID = It->first; + const llvm::RewriteBuffer &RewriteBuf = It->second; + const clang::SourceManager &SM = Rewrite.getSourceMgr(); + + // Get the filename safely + llvm::StringRef FileName = SM.getFilename(SM.getLocForStartOfFile(FID)); + if (FileName.empty()) + FileName = "<unknown file>"; + + llvm::outs() << "\n--- " << FileName << " ---\n"; + RewriteBuf.write(llvm::outs()); + llvm::outs() << "\n"; + } + + llvm::outs() << "\n=== Refactoring complete ===\n"; + return 0; +} + >From bc62ab53a297d6ce354c787d5ee801dbd1b1ff20 Mon Sep 17 00:00:00 2001 From: Dheeraj <[email protected]> Date: Fri, 7 Nov 2025 18:20:40 +0530 Subject: [PATCH 2/7] Add modernize-conditional-to-if clang-tidy check --- clang-tools-extra/CMakeLists.txt | 1 - .../clang-convert-ternary-if/CMakeLists.txt | 20 -- .../ConvertTernaryIf.cpp | 111 ------ .../ConvertTernaryIf.h | 39 --- .../clang-convert-ternary-if/ToolMain.cpp | 80 ----- .../clang-tidy/modernize/CMakeLists.txt | 1 + .../modernize/ConditionalToIfCheck.cpp | 328 ++++++++++++++++++ .../modernize/ConditionalToIfCheck.h | 39 +++ .../modernize/ModernizeTidyModule.cpp | 2 + 9 files changed, 370 insertions(+), 251 deletions(-) delete mode 100644 clang-tools-extra/clang-convert-ternary-if/CMakeLists.txt delete mode 100644 clang-tools-extra/clang-convert-ternary-if/ConvertTernaryIf.cpp delete mode 100644 clang-tools-extra/clang-convert-ternary-if/ConvertTernaryIf.h delete mode 100644 clang-tools-extra/clang-convert-ternary-if/ToolMain.cpp create mode 100644 clang-tools-extra/clang-tidy/modernize/ConditionalToIfCheck.cpp create mode 100644 clang-tools-extra/clang-tidy/modernize/ConditionalToIfCheck.h diff --git a/clang-tools-extra/CMakeLists.txt b/clang-tools-extra/CMakeLists.txt index c0e6888b6209a..87050db4e0e75 100644 --- a/clang-tools-extra/CMakeLists.txt +++ b/clang-tools-extra/CMakeLists.txt @@ -21,7 +21,6 @@ add_subdirectory(clang-apply-replacements) add_subdirectory(clang-reorder-fields) add_subdirectory(modularize) add_subdirectory(clang-tidy) -add_subdirectory(clang-convert-ternary-if) add_subdirectory(clang-change-namespace) add_subdirectory(clang-doc) diff --git a/clang-tools-extra/clang-convert-ternary-if/CMakeLists.txt b/clang-tools-extra/clang-convert-ternary-if/CMakeLists.txt deleted file mode 100644 index 780fca4405e64..0000000000000 --- a/clang-tools-extra/clang-convert-ternary-if/CMakeLists.txt +++ /dev/null @@ -1,20 +0,0 @@ -#===- CMakeLists.txt - clang-convert-ternary-if tool ---------------------===# -# This defines the build configuration for the standalone refactoring tool. - -add_clang_executable(clang-convert-ternary-if - ConvertTernaryIf.cpp - ToolMain.cpp -) - -target_link_libraries(clang-convert-ternary-if - PRIVATE - clangTooling - clangBasic - clangAST - clangASTMatchers - clangRewrite - clangFrontend -) - -install(TARGETS clang-convert-ternary-if RUNTIME DESTINATION bin) - diff --git a/clang-tools-extra/clang-convert-ternary-if/ConvertTernaryIf.cpp b/clang-tools-extra/clang-convert-ternary-if/ConvertTernaryIf.cpp deleted file mode 100644 index 8843510d08b72..0000000000000 --- a/clang-tools-extra/clang-convert-ternary-if/ConvertTernaryIf.cpp +++ /dev/null @@ -1,111 +0,0 @@ -//===--- ConvertTernaryIf.cpp ---------------------------------------------===// -// -// Implements a tool that refactors between ternary (?:) expressions -// and equivalent if/else statements. -// -// Usage Example: -// clang-convert-ternary-if test.cpp -- -// -//===----------------------------------------------------------------------===// - -#include "ConvertTernaryIf.h" -#include "clang/ASTMatchers/ASTMatchFinder.h" -#include "clang/Rewrite/Core/Rewriter.h" -#include "clang/Lex/Lexer.h" -#include "llvm/Support/raw_ostream.h" - -using namespace clang; -using namespace clang::ast_matchers; - -namespace clang { -namespace convertternary { - - -// Callback: Called when a match is found -void ConvertTernaryIfCallback::run(const MatchFinder::MatchResult &Result) { - //Initialize the Rewriter safely (fixes segmentation fault) - if (!IsInitialized) { - if (Result.SourceManager && Result.Context) { - TheRewriter.setSourceMgr(*Result.SourceManager, - Result.Context->getLangOpts()); - IsInitialized = true; - llvm::errs() << "Rewriter initialized successfully.\n"; - } else { - llvm::errs() << "Error: Missing SourceManager or Context.\n"; - return; - } - } - - const auto *CondOp = Result.Nodes.getNodeAs<ConditionalOperator>("condOp"); - const auto *IfStmtNode = Result.Nodes.getNodeAs<IfStmt>("ifStmt"); - - const SourceManager &SM = *Result.SourceManager; - - // === Convert Ternary -> If === - if (CondOp) { - const Expr *Cond = CondOp->getCond(); - const Expr *TrueExpr = CondOp->getTrueExpr(); - const Expr *FalseExpr = CondOp->getFalseExpr(); - - auto getText = [&](const Expr *E) -> std::string { - return Lexer::getSourceText(CharSourceRange::getTokenRange(E->getSourceRange()), SM, - Result.Context->getLangOpts()) - .str(); - }; - - std::string CondText = getText(Cond); - std::string TrueText = getText(TrueExpr); - std::string FalseText = getText(FalseExpr); - - std::string IfReplacement = "if (" + CondText + ") {\n " + TrueText + - ";\n} else {\n " + FalseText + ";\n}"; - - TheRewriter.ReplaceText(CondOp->getSourceRange(), IfReplacement); - llvm::errs() << "Converted ternary to if/else.\n"; - } - - // === Convert If -> Ternary === - if (IfStmtNode) { - const Expr *Cond = IfStmtNode->getCond(); - const Stmt *Then = IfStmtNode->getThen(); - const Stmt *Else = IfStmtNode->getElse(); - - if (!Then || !Else) - return; - - auto getTextStmt = [&](const Stmt *S) -> std::string { - return Lexer::getSourceText(CharSourceRange::getTokenRange(S->getSourceRange()), SM, - Result.Context->getLangOpts()) - .str(); - }; - - std::string CondText = Lexer::getSourceText( - CharSourceRange::getTokenRange(Cond->getSourceRange()), SM, - Result.Context->getLangOpts()) - .str(); - - std::string ThenText = getTextStmt(Then); - std::string ElseText = getTextStmt(Else); - - std::string Ternary = - "(" + CondText + ") ? " + ThenText + " : " + ElseText + ";"; - - TheRewriter.ReplaceText(IfStmtNode->getSourceRange(), Ternary); - llvm::errs() << "Converted if/else to ternary.\n"; - } -} - -// === Register AST Matchers === -void setupMatchers(MatchFinder &Finder, ConvertTernaryIfCallback &Callback) { - Finder.addMatcher( - conditionalOperator(isExpansionInMainFile()).bind("condOp"), &Callback); - - Finder.addMatcher( - ifStmt(hasThen(stmt()), hasElse(stmt()), isExpansionInMainFile()) - .bind("ifStmt"), - &Callback); -} - -} // namespace convertternary -} // namespace clang - diff --git a/clang-tools-extra/clang-convert-ternary-if/ConvertTernaryIf.h b/clang-tools-extra/clang-convert-ternary-if/ConvertTernaryIf.h deleted file mode 100644 index 2d71cf5f8242d..0000000000000 --- a/clang-tools-extra/clang-convert-ternary-if/ConvertTernaryIf.h +++ /dev/null @@ -1,39 +0,0 @@ -//===--- ConvertTernaryIf.h ------------------------------------*- C++ -*-===// -// -// This file declares the refactoring logic that converts -// ternary operators (?:) into if/else statements and vice versa. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_CLANG_CONVERT_TERNARY_IF_H -#define LLVM_CLANG_CONVERT_TERNARY_IF_H - -#include "clang/AST/AST.h" -#include "clang/ASTMatchers/ASTMatchFinder.h" -#include "clang/Tooling/Refactoring.h" -#include "clang/Rewrite/Core/Rewriter.h" - -namespace clang { -namespace convertternary { - -class ConvertTernaryIfCallback - : public ast_matchers::MatchFinder::MatchCallback { -public: - ConvertTernaryIfCallback(Rewriter &R) - : TheRewriter(R), IsInitialized(false) {} - - void run(const ast_matchers::MatchFinder::MatchResult &Result) override; - -private: - Rewriter &TheRewriter; - bool IsInitialized; - }; - -void setupMatchers(ast_matchers::MatchFinder &Finder, - ConvertTernaryIfCallback &Callback); - -} // namespace convertternary -} // namespace clang - -#endif - diff --git a/clang-tools-extra/clang-convert-ternary-if/ToolMain.cpp b/clang-tools-extra/clang-convert-ternary-if/ToolMain.cpp deleted file mode 100644 index bf9a793309944..0000000000000 --- a/clang-tools-extra/clang-convert-ternary-if/ToolMain.cpp +++ /dev/null @@ -1,80 +0,0 @@ -//===--- ToolMain.cpp - Entry point for clang-convert-ternary-if ----------===// -// -// This tool runs the refactoring logic defined in ConvertTernaryIf.cpp. -// -// Usage: -// clang-convert-ternary-if <source-file> -- -// -// It prints the rewritten (refactored) source code to stdout. -// -//===----------------------------------------------------------------------===// - -#include "ConvertTernaryIf.h" -#include "clang/Tooling/CommonOptionsParser.h" -#include "clang/Tooling/Tooling.h" -#include "clang/Frontend/TextDiagnosticPrinter.h" -#include "llvm/Support/CommandLine.h" -#include "llvm/Support/raw_ostream.h" - -using namespace clang; -using namespace clang::tooling; -using namespace clang::convertternary; -using namespace llvm; - -static llvm::cl::OptionCategory ToolCategory("convert-ternary-if options"); - -int main(int argc, const char **argv) { - // Parse command-line options - auto ExpectedParser = - CommonOptionsParser::create(argc, argv, ToolCategory, cl::ZeroOrMore); - if (!ExpectedParser) { - llvm::errs() << ExpectedParser.takeError(); - return 1; - } - - CommonOptionsParser &OptionsParser = ExpectedParser.get(); - ClangTool Tool(OptionsParser.getCompilations(), - OptionsParser.getSourcePathList()); - - // Set up the Rewriter and the Matcher - clang::Rewriter Rewrite; - ast_matchers::MatchFinder Finder; - ConvertTernaryIfCallback Callback(Rewrite); - setupMatchers(Finder, Callback); - - llvm::outs() << "=== Running clang-convert-ternary-if ===\n"; - int Result = Tool.run(newFrontendActionFactory(&Finder).get()); - - if (Result != 0) { - llvm::errs() << "Error: Tool execution failed.\n"; - return Result; - } - - // No changes? - if (Rewrite.buffer_begin() == Rewrite.buffer_end()) { - llvm::outs() << "No changes made.\n"; - return 0; - } - - llvm::outs() << "\n=== Rewritten Files ===\n"; - - // Print all rewritten files - for (auto It = Rewrite.buffer_begin(); It != Rewrite.buffer_end(); ++It) { - clang::FileID FID = It->first; - const llvm::RewriteBuffer &RewriteBuf = It->second; - const clang::SourceManager &SM = Rewrite.getSourceMgr(); - - // Get the filename safely - llvm::StringRef FileName = SM.getFilename(SM.getLocForStartOfFile(FID)); - if (FileName.empty()) - FileName = "<unknown file>"; - - llvm::outs() << "\n--- " << FileName << " ---\n"; - RewriteBuf.write(llvm::outs()); - llvm::outs() << "\n"; - } - - llvm::outs() << "\n=== Refactoring complete ===\n"; - return 0; -} - diff --git a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt index 882f2dc9fb4d8..608a81a7f200c 100644 --- a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt @@ -53,6 +53,7 @@ add_clang_library(clangTidyModernizeModule STATIC UseTransparentFunctorsCheck.cpp UseUncaughtExceptionsCheck.cpp UseUsingCheck.cpp + ConditionalToIfCheck.cpp LINK_LIBS clangTidy diff --git a/clang-tools-extra/clang-tidy/modernize/ConditionalToIfCheck.cpp b/clang-tools-extra/clang-tidy/modernize/ConditionalToIfCheck.cpp new file mode 100644 index 0000000000000..22e6c47058d88 --- /dev/null +++ b/clang-tools-extra/clang-tidy/modernize/ConditionalToIfCheck.cpp @@ -0,0 +1,328 @@ +#include "ConditionalToIfCheck.h" + +#include "clang/AST/ASTContext.h" +#include "clang/AST/Expr.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Lex/Lexer.h" +#include "llvm/ADT/StringSwitch.h" + +using namespace clang; +using namespace clang::ast_matchers; + +namespace clang::tidy::modernize { + +namespace { + +bool isInMacro(const SourceRange &R, const MatchFinder::MatchResult &Res) { + return R.getBegin().isMacroID() || R.getEnd().isMacroID(); +} + +CharSourceRange tokenRange(const SourceRange &R, + const MatchFinder::MatchResult &Res) { + return CharSourceRange::getTokenRange(R); +} + +std::string getTokenText(const SourceRange &R, + const MatchFinder::MatchResult &Res) { + return Lexer::getSourceText(tokenRange(R, Res), + *Res.SourceManager, Res.Context->getLangOpts()) + .str(); +} + +const Expr *strip(const Expr *E) { return E ? E->IgnoreParenImpCasts() : E; } + +// Find the statement that directly contains E (best-effort). +const Stmt *enclosingStmt(const Expr *E, const MatchFinder::MatchResult &Res) { + if (!E) return nullptr; + const Stmt *Cur = E; + while (true) { + auto Parents = Res.Context->getParents(*Cur); + if (Parents.empty()) return Cur; + if (const Stmt *S = Parents[0].get<Stmt>()) { + Cur = S; + // Stop when Cur itself is a statement that could be replaced wholesale. + if (isa<ExprWithCleanups>(Cur) || isa<ReturnStmt>(Cur) || + isa<CompoundStmt>(Cur) || isa<IfStmt>(Cur) || + isa<DeclStmt>(Cur) || isa<Expr>(Cur)) + continue; + return Cur; + } else { + return Cur; + } + } +} + +} // namespace + +ConditionalToIfCheck::ConditionalToIfCheck(StringRef Name, + ClangTidyContext *Ctx) + : ClangTidyCheck(Name, Ctx) { + StringRef V = Options.get("PreferredForm", "if"); + // Use StringSwitch for broad LLVM version compatibility. + PreferredForm = llvm::StringSwitch<Preferred>(V) + .CaseLower("conditional", Preferred::Conditional) + .Default(Preferred::If); +} + +void ConditionalToIfCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { + Options.store(Opts, "PreferredForm", + PreferredForm == Preferred::If ? "if" : "conditional"); +} + +void ConditionalToIfCheck::registerMatchers(MatchFinder *Finder) { + // Only match code written in the main file to avoid noisy headers. + auto InMain = isExpansionInMainFile(); + + if (PreferredForm == Preferred::If) { + // 1) x = cond ? a : b; + Finder->addMatcher( + binaryOperator(InMain, isAssignmentOperator(), + hasRHS(conditionalOperator().bind("condop")), + hasLHS(expr().bind("assignLHS"))) + .bind("assign"), + this); + + // 2) return cond ? a : b; + Finder->addMatcher( + returnStmt(InMain, + hasReturnValue(conditionalOperator().bind("retop"))) + .bind("ret"), + this); + } else { + // Preferred::Conditional + + // 3a) if (cond) x = A; else x = B; + // (Match a simple assignment in each branch; allow it to be wrapped in + // an ExprWithCleanups/ExprStmt or a single-statement compound.) + auto AssignThen = + binaryOperator(isAssignmentOperator()) + .bind("thenA"); + auto AssignElse = + binaryOperator(isAssignmentOperator()) + .bind("elseA"); + + Finder->addMatcher( + ifStmt(InMain, + hasThen(anyOf( + hasDescendant(AssignThen), + compoundStmt(statementCountIs(1), + hasAnySubstatement(hasDescendant(AssignThen))))), + hasElse(anyOf( + hasDescendant(AssignElse), + compoundStmt(statementCountIs(1), + hasAnySubstatement(hasDescendant(AssignElse)))))) + .bind("ifAssign"), + this); + + // 3b) if (cond) return A; else return B; + auto RetThen = returnStmt(hasReturnValue(expr().bind("thenR"))).bind("thenRet"); + auto RetElse = returnStmt(hasReturnValue(expr().bind("elseR"))).bind("elseRet"); + + Finder->addMatcher( + ifStmt(InMain, + hasThen(anyOf(RetThen, + compoundStmt(statementCountIs(1), + hasAnySubstatement(RetThen)))), + hasElse(anyOf(RetElse, + compoundStmt(statementCountIs(1), + hasAnySubstatement(RetElse))))) + .bind("ifReturn"), + this); + } +} + +bool ConditionalToIfCheck::locationsAreOK( + const SourceRange &R, const MatchFinder::MatchResult &Rst) { + if (R.isInvalid()) + return false; + if (isInMacro(R, Rst)) + return false; + if (!Rst.SourceManager->isWrittenInMainFile(R.getBegin())) + return false; + return true; +} + +std::string ConditionalToIfCheck::getText( + const SourceRange &R, const MatchFinder::MatchResult &Rst) { + return getTokenText(R, Rst); +} + +bool ConditionalToIfCheck::hasObviousSideEffects(const Expr *E, + ASTContext &Ctx) { + if (!E) + return true; + E = strip(E); + + // Very conservative: rely on Clang's side-effect query. + if (E->HasSideEffects(Ctx)) + return true; + + // Additional heuristics for common side-effect nodes. + if (isa<CallExpr>(E) || isa<CXXConstructExpr>(E) || isa<CXXOperatorCallExpr>(E)) + return true; + + if (const auto *U = dyn_cast<UnaryOperator>(E)) { + if (U->isIncrementDecrementOp() || U->getOpcode() == UO_AddrOf || + U->getOpcode() == UO_Deref) + return true; + } + if (const auto *B = dyn_cast<BinaryOperator>(E)) { + if (B->isAssignmentOp() || B->getOpcode() == BO_Comma) + return true; + } + return false; +} + +void ConditionalToIfCheck::check(const MatchFinder::MatchResult &Res) { + ASTContext &Ctx = *Res.Context; + + if (PreferredForm == Preferred::If) { + // Handle: return cond ? a : b; + if (const auto *Ret = Res.Nodes.getNodeAs<ReturnStmt>("ret")) { + const auto *CO = Res.Nodes.getNodeAs<ConditionalOperator>("retop"); + if (!CO) return; + + const Expr *Cond = strip(CO->getCond()); + const Expr *TrueE = strip(CO->getTrueExpr()); + const Expr *FalseE = strip(CO->getFalseExpr()); + + if (hasObviousSideEffects(Cond, Ctx) || + hasObviousSideEffects(TrueE, Ctx) || + hasObviousSideEffects(FalseE, Ctx)) + return; + + SourceRange SR = Ret->getSourceRange(); + if (!locationsAreOK(SR, Res)) + return; + + std::string CondS = getText(Cond->getSourceRange(), Res); + std::string TS = getText(TrueE->getSourceRange(), Res); + std::string FS = getText(FalseE->getSourceRange(), Res); + + std::string Replacement = "if (" + CondS + ") {\n return " + TS + + ";\n} else {\n return " + FS + ";\n}"; + diag(Ret->getBeginLoc(), + "replace simple conditional return with if/else") + << FixItHint::CreateReplacement(CharSourceRange::getCharRange(SR), + Replacement); + return; + } + + // Handle: x = cond ? a : b; + if (const auto *Assign = Res.Nodes.getNodeAs<BinaryOperator>("assign")) { + const auto *CO = Res.Nodes.getNodeAs<ConditionalOperator>("condop"); + const auto *LHS = Res.Nodes.getNodeAs<Expr>("assignLHS"); + if (!CO || !LHS) return; + + const Expr *Cond = strip(CO->getCond()); + const Expr *TrueE = strip(CO->getTrueExpr()); + const Expr *FalseE = strip(CO->getFalseExpr()); + + if (hasObviousSideEffects(Cond, Ctx) || + hasObviousSideEffects(TrueE, Ctx) || + hasObviousSideEffects(FalseE, Ctx) || + hasObviousSideEffects(LHS, Ctx)) + return; + + const Stmt *Carrier = enclosingStmt(Assign, Res); + if (!Carrier) + Carrier = Assign; + + SourceRange SR = Carrier->getSourceRange(); + if (!locationsAreOK(SR, Res)) + return; + + std::string LHSS = getText(LHS->getSourceRange(), Res); + std::string CondS = getText(Cond->getSourceRange(), Res); + std::string TS = getText(TrueE->getSourceRange(), Res); + std::string FS = getText(FalseE->getSourceRange(), Res); + + std::string Replacement = "if (" + CondS + ") {\n " + LHSS + " = " + TS + + ";\n} else {\n " + LHSS + " = " + FS + + ";\n}"; + diag(Carrier->getBeginLoc(), + "replace simple conditional assignment with if/else") + << FixItHint::CreateReplacement(CharSourceRange::getCharRange(SR), + Replacement); + return; + } + return; + } + + // PreferredForm == Conditional + + // if (cond) return A; else return B; + if (const auto *IfR = Res.Nodes.getNodeAs<IfStmt>("ifReturn")) { + const Expr *Cond = strip(IfR->getCond()); + const Expr *ThenR = strip(Res.Nodes.getNodeAs<Expr>("thenR")); + const Expr *ElseR = strip(Res.Nodes.getNodeAs<Expr>("elseR")); + if (!Cond || !ThenR || !ElseR) return; + + if (hasObviousSideEffects(Cond, Ctx) || + hasObviousSideEffects(ThenR, Ctx) || + hasObviousSideEffects(ElseR, Ctx)) + return; + + SourceRange SR = IfR->getSourceRange(); + if (!locationsAreOK(SR, Res)) + return; + + std::string CondS = getText(Cond->getSourceRange(), Res); + std::string TS = getText(ThenR->getSourceRange(), Res); + std::string FS = getText(ElseR->getSourceRange(), Res); + + std::string Replacement = + "return (" + CondS + ") ? " + TS + " : " + FS + ";"; + diag(IfR->getBeginLoc(), "replace simple if/else with conditional return") + << FixItHint::CreateReplacement(CharSourceRange::getCharRange(SR), + Replacement); + return; + } + + // if (cond) x = A; else x = B; + if (const auto *IfA = Res.Nodes.getNodeAs<IfStmt>("ifAssign")) { + const Expr *Cond = strip(IfA->getCond()); + const auto *ThenA = Res.Nodes.getNodeAs<BinaryOperator>("thenA"); + const auto *ElseA = Res.Nodes.getNodeAs<BinaryOperator>("elseA"); + if (!Cond || !ThenA || !ElseA) + return; + + const Expr *ThenL = strip(ThenA->getLHS()); + const Expr *ElseL = strip(ElseA->getLHS()); + const Expr *ThenR = strip(ThenA->getRHS()); + const Expr *ElseR = strip(ElseA->getRHS()); + if (!ThenL || !ElseL || !ThenR || !ElseR) + return; + + // LHS must be textually identical (safe & simple). + std::string LThen = getText(ThenL->getSourceRange(), Res); + std::string LElse = getText(ElseL->getSourceRange(), Res); + if (LThen != LElse) + return; + + if (hasObviousSideEffects(Cond, Ctx) || + hasObviousSideEffects(ThenR, Ctx) || + hasObviousSideEffects(ElseR, Ctx) || + hasObviousSideEffects(ThenL, Ctx)) + return; + + SourceRange SR = IfA->getSourceRange(); + if (!locationsAreOK(SR, Res)) + return; + + std::string CondS = getText(Cond->getSourceRange(), Res); + std::string TS = getText(ThenR->getSourceRange(), Res); + std::string FS = getText(ElseR->getSourceRange(), Res); + + std::string Replacement = + LThen + " = (" + CondS + ") ? " + TS + " : " + FS + ";"; + diag(IfA->getBeginLoc(), + "replace simple if/else assignment with conditional expression") + << FixItHint::CreateReplacement(CharSourceRange::getCharRange(SR), + Replacement); + return; + } +} + +} // namespace clang::tidy::modernize diff --git a/clang-tools-extra/clang-tidy/modernize/ConditionalToIfCheck.h b/clang-tools-extra/clang-tidy/modernize/ConditionalToIfCheck.h new file mode 100644 index 0000000000000..95d159b580d37 --- /dev/null +++ b/clang-tools-extra/clang-tidy/modernize/ConditionalToIfCheck.h @@ -0,0 +1,39 @@ +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_CONDITIONALTOIFCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_CONDITIONALTOIFCHECK_H + +#include "../ClangTidyCheck.h" + +namespace clang::tidy::modernize { + +/// Convert between simple conditional (?:) expressions and equivalent if/else +/// statements in safe, syntactically simple cases. +/// +/// Direction is controlled by the option: +/// - modernize-conditional-to-if.PreferredForm: "if" | "conditional" +class ConditionalToIfCheck : public ClangTidyCheck { +public: + ConditionalToIfCheck(llvm::StringRef Name, ClangTidyContext *Context); + + void storeOptions(ClangTidyOptions::OptionMap &Opts) override; + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + +private: + enum class Preferred { If, Conditional }; + Preferred PreferredForm; + + // Conservative side-effect check (needs ASTContext). + static bool hasObviousSideEffects(const Expr *E, ASTContext &Ctx); + + // Fetch exact source text for a range. + static std::string getText(const SourceRange &R, + const ast_matchers::MatchFinder::MatchResult &Rst); + + // Ensure locations are in main file and not in macros. + static bool locationsAreOK(const SourceRange &R, + const ast_matchers::MatchFinder::MatchResult &Rst); +}; + +} // namespace clang::tidy::modernize + +#endif diff --git a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp index 360e2b8434d0c..527040a4fadd2 100644 --- a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp @@ -54,6 +54,7 @@ #include "UseTransparentFunctorsCheck.h" #include "UseUncaughtExceptionsCheck.h" #include "UseUsingCheck.h" +#include "ConditionalToIfCheck.h" using namespace clang::ast_matchers; @@ -134,6 +135,7 @@ class ModernizeModule : public ClangTidyModule { CheckFactories.registerCheck<UseUncaughtExceptionsCheck>( "modernize-use-uncaught-exceptions"); CheckFactories.registerCheck<UseUsingCheck>("modernize-use-using"); + CheckFactories.registerCheck<ConditionalToIfCheck>("modernize-conditional-to-if"); } }; >From 0cc65c5ef48d6c3ca36a8358e95f9a61c62c2c44 Mon Sep 17 00:00:00 2001 From: Dheeraj <[email protected]> Date: Fri, 7 Nov 2025 19:00:52 +0530 Subject: [PATCH 3/7] clang formatted --- .../modernize/ConditionalToIfCheck.cpp | 98 +++++++++---------- .../modernize/ModernizeTidyModule.cpp | 5 +- 2 files changed, 50 insertions(+), 53 deletions(-) diff --git a/clang-tools-extra/clang-tidy/modernize/ConditionalToIfCheck.cpp b/clang-tools-extra/clang-tidy/modernize/ConditionalToIfCheck.cpp index 22e6c47058d88..64de9b1e70af2 100644 --- a/clang-tools-extra/clang-tidy/modernize/ConditionalToIfCheck.cpp +++ b/clang-tools-extra/clang-tidy/modernize/ConditionalToIfCheck.cpp @@ -25,8 +25,8 @@ CharSourceRange tokenRange(const SourceRange &R, std::string getTokenText(const SourceRange &R, const MatchFinder::MatchResult &Res) { - return Lexer::getSourceText(tokenRange(R, Res), - *Res.SourceManager, Res.Context->getLangOpts()) + return Lexer::getSourceText(tokenRange(R, Res), *Res.SourceManager, + Res.Context->getLangOpts()) .str(); } @@ -34,17 +34,19 @@ const Expr *strip(const Expr *E) { return E ? E->IgnoreParenImpCasts() : E; } // Find the statement that directly contains E (best-effort). const Stmt *enclosingStmt(const Expr *E, const MatchFinder::MatchResult &Res) { - if (!E) return nullptr; + if (!E) + return nullptr; const Stmt *Cur = E; while (true) { auto Parents = Res.Context->getParents(*Cur); - if (Parents.empty()) return Cur; + if (Parents.empty()) + return Cur; if (const Stmt *S = Parents[0].get<Stmt>()) { Cur = S; // Stop when Cur itself is a statement that could be replaced wholesale. if (isa<ExprWithCleanups>(Cur) || isa<ReturnStmt>(Cur) || - isa<CompoundStmt>(Cur) || isa<IfStmt>(Cur) || - isa<DeclStmt>(Cur) || isa<Expr>(Cur)) + isa<CompoundStmt>(Cur) || isa<IfStmt>(Cur) || isa<DeclStmt>(Cur) || + isa<Expr>(Cur)) continue; return Cur; } else { @@ -85,8 +87,7 @@ void ConditionalToIfCheck::registerMatchers(MatchFinder *Finder) { // 2) return cond ? a : b; Finder->addMatcher( - returnStmt(InMain, - hasReturnValue(conditionalOperator().bind("retop"))) + returnStmt(InMain, hasReturnValue(conditionalOperator().bind("retop"))) .bind("ret"), this); } else { @@ -95,45 +96,42 @@ void ConditionalToIfCheck::registerMatchers(MatchFinder *Finder) { // 3a) if (cond) x = A; else x = B; // (Match a simple assignment in each branch; allow it to be wrapped in // an ExprWithCleanups/ExprStmt or a single-statement compound.) - auto AssignThen = - binaryOperator(isAssignmentOperator()) - .bind("thenA"); - auto AssignElse = - binaryOperator(isAssignmentOperator()) - .bind("elseA"); + auto AssignThen = binaryOperator(isAssignmentOperator()).bind("thenA"); + auto AssignElse = binaryOperator(isAssignmentOperator()).bind("elseA"); Finder->addMatcher( ifStmt(InMain, - hasThen(anyOf( - hasDescendant(AssignThen), - compoundStmt(statementCountIs(1), - hasAnySubstatement(hasDescendant(AssignThen))))), - hasElse(anyOf( - hasDescendant(AssignElse), - compoundStmt(statementCountIs(1), - hasAnySubstatement(hasDescendant(AssignElse)))))) + hasThen(anyOf(hasDescendant(AssignThen), + compoundStmt(statementCountIs(1), + hasAnySubstatement( + hasDescendant(AssignThen))))), + hasElse(anyOf(hasDescendant(AssignElse), + compoundStmt(statementCountIs(1), + hasAnySubstatement( + hasDescendant(AssignElse)))))) .bind("ifAssign"), this); // 3b) if (cond) return A; else return B; - auto RetThen = returnStmt(hasReturnValue(expr().bind("thenR"))).bind("thenRet"); - auto RetElse = returnStmt(hasReturnValue(expr().bind("elseR"))).bind("elseRet"); + auto RetThen = + returnStmt(hasReturnValue(expr().bind("thenR"))).bind("thenRet"); + auto RetElse = + returnStmt(hasReturnValue(expr().bind("elseR"))).bind("elseRet"); Finder->addMatcher( - ifStmt(InMain, - hasThen(anyOf(RetThen, - compoundStmt(statementCountIs(1), - hasAnySubstatement(RetThen)))), - hasElse(anyOf(RetElse, - compoundStmt(statementCountIs(1), - hasAnySubstatement(RetElse))))) + ifStmt( + InMain, + hasThen(anyOf(RetThen, compoundStmt(statementCountIs(1), + hasAnySubstatement(RetThen)))), + hasElse(anyOf(RetElse, compoundStmt(statementCountIs(1), + hasAnySubstatement(RetElse))))) .bind("ifReturn"), this); } } -bool ConditionalToIfCheck::locationsAreOK( - const SourceRange &R, const MatchFinder::MatchResult &Rst) { +bool ConditionalToIfCheck::locationsAreOK(const SourceRange &R, + const MatchFinder::MatchResult &Rst) { if (R.isInvalid()) return false; if (isInMacro(R, Rst)) @@ -143,8 +141,8 @@ bool ConditionalToIfCheck::locationsAreOK( return true; } -std::string ConditionalToIfCheck::getText( - const SourceRange &R, const MatchFinder::MatchResult &Rst) { +std::string ConditionalToIfCheck::getText(const SourceRange &R, + const MatchFinder::MatchResult &Rst) { return getTokenText(R, Rst); } @@ -159,7 +157,8 @@ bool ConditionalToIfCheck::hasObviousSideEffects(const Expr *E, return true; // Additional heuristics for common side-effect nodes. - if (isa<CallExpr>(E) || isa<CXXConstructExpr>(E) || isa<CXXOperatorCallExpr>(E)) + if (isa<CallExpr>(E) || isa<CXXConstructExpr>(E) || + isa<CXXOperatorCallExpr>(E)) return true; if (const auto *U = dyn_cast<UnaryOperator>(E)) { @@ -181,7 +180,8 @@ void ConditionalToIfCheck::check(const MatchFinder::MatchResult &Res) { // Handle: return cond ? a : b; if (const auto *Ret = Res.Nodes.getNodeAs<ReturnStmt>("ret")) { const auto *CO = Res.Nodes.getNodeAs<ConditionalOperator>("retop"); - if (!CO) return; + if (!CO) + return; const Expr *Cond = strip(CO->getCond()); const Expr *TrueE = strip(CO->getTrueExpr()); @@ -202,8 +202,7 @@ void ConditionalToIfCheck::check(const MatchFinder::MatchResult &Res) { std::string Replacement = "if (" + CondS + ") {\n return " + TS + ";\n} else {\n return " + FS + ";\n}"; - diag(Ret->getBeginLoc(), - "replace simple conditional return with if/else") + diag(Ret->getBeginLoc(), "replace simple conditional return with if/else") << FixItHint::CreateReplacement(CharSourceRange::getCharRange(SR), Replacement); return; @@ -213,7 +212,8 @@ void ConditionalToIfCheck::check(const MatchFinder::MatchResult &Res) { if (const auto *Assign = Res.Nodes.getNodeAs<BinaryOperator>("assign")) { const auto *CO = Res.Nodes.getNodeAs<ConditionalOperator>("condop"); const auto *LHS = Res.Nodes.getNodeAs<Expr>("assignLHS"); - if (!CO || !LHS) return; + if (!CO || !LHS) + return; const Expr *Cond = strip(CO->getCond()); const Expr *TrueE = strip(CO->getTrueExpr()); @@ -221,8 +221,7 @@ void ConditionalToIfCheck::check(const MatchFinder::MatchResult &Res) { if (hasObviousSideEffects(Cond, Ctx) || hasObviousSideEffects(TrueE, Ctx) || - hasObviousSideEffects(FalseE, Ctx) || - hasObviousSideEffects(LHS, Ctx)) + hasObviousSideEffects(FalseE, Ctx) || hasObviousSideEffects(LHS, Ctx)) return; const Stmt *Carrier = enclosingStmt(Assign, Res); @@ -239,8 +238,7 @@ void ConditionalToIfCheck::check(const MatchFinder::MatchResult &Res) { std::string FS = getText(FalseE->getSourceRange(), Res); std::string Replacement = "if (" + CondS + ") {\n " + LHSS + " = " + TS + - ";\n} else {\n " + LHSS + " = " + FS + - ";\n}"; + ";\n} else {\n " + LHSS + " = " + FS + ";\n}"; diag(Carrier->getBeginLoc(), "replace simple conditional assignment with if/else") << FixItHint::CreateReplacement(CharSourceRange::getCharRange(SR), @@ -257,10 +255,10 @@ void ConditionalToIfCheck::check(const MatchFinder::MatchResult &Res) { const Expr *Cond = strip(IfR->getCond()); const Expr *ThenR = strip(Res.Nodes.getNodeAs<Expr>("thenR")); const Expr *ElseR = strip(Res.Nodes.getNodeAs<Expr>("elseR")); - if (!Cond || !ThenR || !ElseR) return; + if (!Cond || !ThenR || !ElseR) + return; - if (hasObviousSideEffects(Cond, Ctx) || - hasObviousSideEffects(ThenR, Ctx) || + if (hasObviousSideEffects(Cond, Ctx) || hasObviousSideEffects(ThenR, Ctx) || hasObviousSideEffects(ElseR, Ctx)) return; @@ -301,10 +299,8 @@ void ConditionalToIfCheck::check(const MatchFinder::MatchResult &Res) { if (LThen != LElse) return; - if (hasObviousSideEffects(Cond, Ctx) || - hasObviousSideEffects(ThenR, Ctx) || - hasObviousSideEffects(ElseR, Ctx) || - hasObviousSideEffects(ThenL, Ctx)) + if (hasObviousSideEffects(Cond, Ctx) || hasObviousSideEffects(ThenR, Ctx) || + hasObviousSideEffects(ElseR, Ctx) || hasObviousSideEffects(ThenL, Ctx)) return; SourceRange SR = IfA->getSourceRange(); diff --git a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp index 527040a4fadd2..3792395e53117 100644 --- a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp @@ -14,6 +14,7 @@ #include "AvoidSetjmpLongjmpCheck.h" #include "AvoidVariadicFunctionsCheck.h" #include "ConcatNestedNamespacesCheck.h" +#include "ConditionalToIfCheck.h" #include "DeprecatedHeadersCheck.h" #include "DeprecatedIosBaseAliasesCheck.h" #include "LoopConvertCheck.h" @@ -54,7 +55,6 @@ #include "UseTransparentFunctorsCheck.h" #include "UseUncaughtExceptionsCheck.h" #include "UseUsingCheck.h" -#include "ConditionalToIfCheck.h" using namespace clang::ast_matchers; @@ -135,7 +135,8 @@ class ModernizeModule : public ClangTidyModule { CheckFactories.registerCheck<UseUncaughtExceptionsCheck>( "modernize-use-uncaught-exceptions"); CheckFactories.registerCheck<UseUsingCheck>("modernize-use-using"); - CheckFactories.registerCheck<ConditionalToIfCheck>("modernize-conditional-to-if"); + CheckFactories.registerCheck<ConditionalToIfCheck>( + "modernize-conditional-to-if"); } }; >From da4e0b6308894bf4d843f8f88b7e5148adf67cee Mon Sep 17 00:00:00 2001 From: Dheeraj <[email protected]> Date: Fri, 7 Nov 2025 23:46:53 +0530 Subject: [PATCH 4/7] followed the suggested changes --- .../clang-tidy/modernize/CMakeLists.txt | 1 - .../modernize/ConditionalToIfCheck.cpp | 324 ------------------ .../modernize/ConditionalToIfCheck.h | 39 --- .../modernize/ModernizeTidyModule.cpp | 3 - .../clang-tidy/readability/CMakeLists.txt | 1 + .../readability/ConditionalToIfCheck.cpp | 85 +++++ .../readability/ConditionalToIfCheck.h | 35 ++ .../readability/ReadabilityTidyModule.cpp | 3 + 8 files changed, 124 insertions(+), 367 deletions(-) delete mode 100644 clang-tools-extra/clang-tidy/modernize/ConditionalToIfCheck.cpp delete mode 100644 clang-tools-extra/clang-tidy/modernize/ConditionalToIfCheck.h create mode 100644 clang-tools-extra/clang-tidy/readability/ConditionalToIfCheck.cpp create mode 100644 clang-tools-extra/clang-tidy/readability/ConditionalToIfCheck.h diff --git a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt index 608a81a7f200c..882f2dc9fb4d8 100644 --- a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt @@ -53,7 +53,6 @@ add_clang_library(clangTidyModernizeModule STATIC UseTransparentFunctorsCheck.cpp UseUncaughtExceptionsCheck.cpp UseUsingCheck.cpp - ConditionalToIfCheck.cpp LINK_LIBS clangTidy diff --git a/clang-tools-extra/clang-tidy/modernize/ConditionalToIfCheck.cpp b/clang-tools-extra/clang-tidy/modernize/ConditionalToIfCheck.cpp deleted file mode 100644 index 64de9b1e70af2..0000000000000 --- a/clang-tools-extra/clang-tidy/modernize/ConditionalToIfCheck.cpp +++ /dev/null @@ -1,324 +0,0 @@ -#include "ConditionalToIfCheck.h" - -#include "clang/AST/ASTContext.h" -#include "clang/AST/Expr.h" -#include "clang/ASTMatchers/ASTMatchFinder.h" -#include "clang/Basic/SourceManager.h" -#include "clang/Lex/Lexer.h" -#include "llvm/ADT/StringSwitch.h" - -using namespace clang; -using namespace clang::ast_matchers; - -namespace clang::tidy::modernize { - -namespace { - -bool isInMacro(const SourceRange &R, const MatchFinder::MatchResult &Res) { - return R.getBegin().isMacroID() || R.getEnd().isMacroID(); -} - -CharSourceRange tokenRange(const SourceRange &R, - const MatchFinder::MatchResult &Res) { - return CharSourceRange::getTokenRange(R); -} - -std::string getTokenText(const SourceRange &R, - const MatchFinder::MatchResult &Res) { - return Lexer::getSourceText(tokenRange(R, Res), *Res.SourceManager, - Res.Context->getLangOpts()) - .str(); -} - -const Expr *strip(const Expr *E) { return E ? E->IgnoreParenImpCasts() : E; } - -// Find the statement that directly contains E (best-effort). -const Stmt *enclosingStmt(const Expr *E, const MatchFinder::MatchResult &Res) { - if (!E) - return nullptr; - const Stmt *Cur = E; - while (true) { - auto Parents = Res.Context->getParents(*Cur); - if (Parents.empty()) - return Cur; - if (const Stmt *S = Parents[0].get<Stmt>()) { - Cur = S; - // Stop when Cur itself is a statement that could be replaced wholesale. - if (isa<ExprWithCleanups>(Cur) || isa<ReturnStmt>(Cur) || - isa<CompoundStmt>(Cur) || isa<IfStmt>(Cur) || isa<DeclStmt>(Cur) || - isa<Expr>(Cur)) - continue; - return Cur; - } else { - return Cur; - } - } -} - -} // namespace - -ConditionalToIfCheck::ConditionalToIfCheck(StringRef Name, - ClangTidyContext *Ctx) - : ClangTidyCheck(Name, Ctx) { - StringRef V = Options.get("PreferredForm", "if"); - // Use StringSwitch for broad LLVM version compatibility. - PreferredForm = llvm::StringSwitch<Preferred>(V) - .CaseLower("conditional", Preferred::Conditional) - .Default(Preferred::If); -} - -void ConditionalToIfCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { - Options.store(Opts, "PreferredForm", - PreferredForm == Preferred::If ? "if" : "conditional"); -} - -void ConditionalToIfCheck::registerMatchers(MatchFinder *Finder) { - // Only match code written in the main file to avoid noisy headers. - auto InMain = isExpansionInMainFile(); - - if (PreferredForm == Preferred::If) { - // 1) x = cond ? a : b; - Finder->addMatcher( - binaryOperator(InMain, isAssignmentOperator(), - hasRHS(conditionalOperator().bind("condop")), - hasLHS(expr().bind("assignLHS"))) - .bind("assign"), - this); - - // 2) return cond ? a : b; - Finder->addMatcher( - returnStmt(InMain, hasReturnValue(conditionalOperator().bind("retop"))) - .bind("ret"), - this); - } else { - // Preferred::Conditional - - // 3a) if (cond) x = A; else x = B; - // (Match a simple assignment in each branch; allow it to be wrapped in - // an ExprWithCleanups/ExprStmt or a single-statement compound.) - auto AssignThen = binaryOperator(isAssignmentOperator()).bind("thenA"); - auto AssignElse = binaryOperator(isAssignmentOperator()).bind("elseA"); - - Finder->addMatcher( - ifStmt(InMain, - hasThen(anyOf(hasDescendant(AssignThen), - compoundStmt(statementCountIs(1), - hasAnySubstatement( - hasDescendant(AssignThen))))), - hasElse(anyOf(hasDescendant(AssignElse), - compoundStmt(statementCountIs(1), - hasAnySubstatement( - hasDescendant(AssignElse)))))) - .bind("ifAssign"), - this); - - // 3b) if (cond) return A; else return B; - auto RetThen = - returnStmt(hasReturnValue(expr().bind("thenR"))).bind("thenRet"); - auto RetElse = - returnStmt(hasReturnValue(expr().bind("elseR"))).bind("elseRet"); - - Finder->addMatcher( - ifStmt( - InMain, - hasThen(anyOf(RetThen, compoundStmt(statementCountIs(1), - hasAnySubstatement(RetThen)))), - hasElse(anyOf(RetElse, compoundStmt(statementCountIs(1), - hasAnySubstatement(RetElse))))) - .bind("ifReturn"), - this); - } -} - -bool ConditionalToIfCheck::locationsAreOK(const SourceRange &R, - const MatchFinder::MatchResult &Rst) { - if (R.isInvalid()) - return false; - if (isInMacro(R, Rst)) - return false; - if (!Rst.SourceManager->isWrittenInMainFile(R.getBegin())) - return false; - return true; -} - -std::string ConditionalToIfCheck::getText(const SourceRange &R, - const MatchFinder::MatchResult &Rst) { - return getTokenText(R, Rst); -} - -bool ConditionalToIfCheck::hasObviousSideEffects(const Expr *E, - ASTContext &Ctx) { - if (!E) - return true; - E = strip(E); - - // Very conservative: rely on Clang's side-effect query. - if (E->HasSideEffects(Ctx)) - return true; - - // Additional heuristics for common side-effect nodes. - if (isa<CallExpr>(E) || isa<CXXConstructExpr>(E) || - isa<CXXOperatorCallExpr>(E)) - return true; - - if (const auto *U = dyn_cast<UnaryOperator>(E)) { - if (U->isIncrementDecrementOp() || U->getOpcode() == UO_AddrOf || - U->getOpcode() == UO_Deref) - return true; - } - if (const auto *B = dyn_cast<BinaryOperator>(E)) { - if (B->isAssignmentOp() || B->getOpcode() == BO_Comma) - return true; - } - return false; -} - -void ConditionalToIfCheck::check(const MatchFinder::MatchResult &Res) { - ASTContext &Ctx = *Res.Context; - - if (PreferredForm == Preferred::If) { - // Handle: return cond ? a : b; - if (const auto *Ret = Res.Nodes.getNodeAs<ReturnStmt>("ret")) { - const auto *CO = Res.Nodes.getNodeAs<ConditionalOperator>("retop"); - if (!CO) - return; - - const Expr *Cond = strip(CO->getCond()); - const Expr *TrueE = strip(CO->getTrueExpr()); - const Expr *FalseE = strip(CO->getFalseExpr()); - - if (hasObviousSideEffects(Cond, Ctx) || - hasObviousSideEffects(TrueE, Ctx) || - hasObviousSideEffects(FalseE, Ctx)) - return; - - SourceRange SR = Ret->getSourceRange(); - if (!locationsAreOK(SR, Res)) - return; - - std::string CondS = getText(Cond->getSourceRange(), Res); - std::string TS = getText(TrueE->getSourceRange(), Res); - std::string FS = getText(FalseE->getSourceRange(), Res); - - std::string Replacement = "if (" + CondS + ") {\n return " + TS + - ";\n} else {\n return " + FS + ";\n}"; - diag(Ret->getBeginLoc(), "replace simple conditional return with if/else") - << FixItHint::CreateReplacement(CharSourceRange::getCharRange(SR), - Replacement); - return; - } - - // Handle: x = cond ? a : b; - if (const auto *Assign = Res.Nodes.getNodeAs<BinaryOperator>("assign")) { - const auto *CO = Res.Nodes.getNodeAs<ConditionalOperator>("condop"); - const auto *LHS = Res.Nodes.getNodeAs<Expr>("assignLHS"); - if (!CO || !LHS) - return; - - const Expr *Cond = strip(CO->getCond()); - const Expr *TrueE = strip(CO->getTrueExpr()); - const Expr *FalseE = strip(CO->getFalseExpr()); - - if (hasObviousSideEffects(Cond, Ctx) || - hasObviousSideEffects(TrueE, Ctx) || - hasObviousSideEffects(FalseE, Ctx) || hasObviousSideEffects(LHS, Ctx)) - return; - - const Stmt *Carrier = enclosingStmt(Assign, Res); - if (!Carrier) - Carrier = Assign; - - SourceRange SR = Carrier->getSourceRange(); - if (!locationsAreOK(SR, Res)) - return; - - std::string LHSS = getText(LHS->getSourceRange(), Res); - std::string CondS = getText(Cond->getSourceRange(), Res); - std::string TS = getText(TrueE->getSourceRange(), Res); - std::string FS = getText(FalseE->getSourceRange(), Res); - - std::string Replacement = "if (" + CondS + ") {\n " + LHSS + " = " + TS + - ";\n} else {\n " + LHSS + " = " + FS + ";\n}"; - diag(Carrier->getBeginLoc(), - "replace simple conditional assignment with if/else") - << FixItHint::CreateReplacement(CharSourceRange::getCharRange(SR), - Replacement); - return; - } - return; - } - - // PreferredForm == Conditional - - // if (cond) return A; else return B; - if (const auto *IfR = Res.Nodes.getNodeAs<IfStmt>("ifReturn")) { - const Expr *Cond = strip(IfR->getCond()); - const Expr *ThenR = strip(Res.Nodes.getNodeAs<Expr>("thenR")); - const Expr *ElseR = strip(Res.Nodes.getNodeAs<Expr>("elseR")); - if (!Cond || !ThenR || !ElseR) - return; - - if (hasObviousSideEffects(Cond, Ctx) || hasObviousSideEffects(ThenR, Ctx) || - hasObviousSideEffects(ElseR, Ctx)) - return; - - SourceRange SR = IfR->getSourceRange(); - if (!locationsAreOK(SR, Res)) - return; - - std::string CondS = getText(Cond->getSourceRange(), Res); - std::string TS = getText(ThenR->getSourceRange(), Res); - std::string FS = getText(ElseR->getSourceRange(), Res); - - std::string Replacement = - "return (" + CondS + ") ? " + TS + " : " + FS + ";"; - diag(IfR->getBeginLoc(), "replace simple if/else with conditional return") - << FixItHint::CreateReplacement(CharSourceRange::getCharRange(SR), - Replacement); - return; - } - - // if (cond) x = A; else x = B; - if (const auto *IfA = Res.Nodes.getNodeAs<IfStmt>("ifAssign")) { - const Expr *Cond = strip(IfA->getCond()); - const auto *ThenA = Res.Nodes.getNodeAs<BinaryOperator>("thenA"); - const auto *ElseA = Res.Nodes.getNodeAs<BinaryOperator>("elseA"); - if (!Cond || !ThenA || !ElseA) - return; - - const Expr *ThenL = strip(ThenA->getLHS()); - const Expr *ElseL = strip(ElseA->getLHS()); - const Expr *ThenR = strip(ThenA->getRHS()); - const Expr *ElseR = strip(ElseA->getRHS()); - if (!ThenL || !ElseL || !ThenR || !ElseR) - return; - - // LHS must be textually identical (safe & simple). - std::string LThen = getText(ThenL->getSourceRange(), Res); - std::string LElse = getText(ElseL->getSourceRange(), Res); - if (LThen != LElse) - return; - - if (hasObviousSideEffects(Cond, Ctx) || hasObviousSideEffects(ThenR, Ctx) || - hasObviousSideEffects(ElseR, Ctx) || hasObviousSideEffects(ThenL, Ctx)) - return; - - SourceRange SR = IfA->getSourceRange(); - if (!locationsAreOK(SR, Res)) - return; - - std::string CondS = getText(Cond->getSourceRange(), Res); - std::string TS = getText(ThenR->getSourceRange(), Res); - std::string FS = getText(ElseR->getSourceRange(), Res); - - std::string Replacement = - LThen + " = (" + CondS + ") ? " + TS + " : " + FS + ";"; - diag(IfA->getBeginLoc(), - "replace simple if/else assignment with conditional expression") - << FixItHint::CreateReplacement(CharSourceRange::getCharRange(SR), - Replacement); - return; - } -} - -} // namespace clang::tidy::modernize diff --git a/clang-tools-extra/clang-tidy/modernize/ConditionalToIfCheck.h b/clang-tools-extra/clang-tidy/modernize/ConditionalToIfCheck.h deleted file mode 100644 index 95d159b580d37..0000000000000 --- a/clang-tools-extra/clang-tidy/modernize/ConditionalToIfCheck.h +++ /dev/null @@ -1,39 +0,0 @@ -#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_CONDITIONALTOIFCHECK_H -#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_CONDITIONALTOIFCHECK_H - -#include "../ClangTidyCheck.h" - -namespace clang::tidy::modernize { - -/// Convert between simple conditional (?:) expressions and equivalent if/else -/// statements in safe, syntactically simple cases. -/// -/// Direction is controlled by the option: -/// - modernize-conditional-to-if.PreferredForm: "if" | "conditional" -class ConditionalToIfCheck : public ClangTidyCheck { -public: - ConditionalToIfCheck(llvm::StringRef Name, ClangTidyContext *Context); - - void storeOptions(ClangTidyOptions::OptionMap &Opts) override; - void registerMatchers(ast_matchers::MatchFinder *Finder) override; - void check(const ast_matchers::MatchFinder::MatchResult &Result) override; - -private: - enum class Preferred { If, Conditional }; - Preferred PreferredForm; - - // Conservative side-effect check (needs ASTContext). - static bool hasObviousSideEffects(const Expr *E, ASTContext &Ctx); - - // Fetch exact source text for a range. - static std::string getText(const SourceRange &R, - const ast_matchers::MatchFinder::MatchResult &Rst); - - // Ensure locations are in main file and not in macros. - static bool locationsAreOK(const SourceRange &R, - const ast_matchers::MatchFinder::MatchResult &Rst); -}; - -} // namespace clang::tidy::modernize - -#endif diff --git a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp index 3792395e53117..360e2b8434d0c 100644 --- a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp @@ -14,7 +14,6 @@ #include "AvoidSetjmpLongjmpCheck.h" #include "AvoidVariadicFunctionsCheck.h" #include "ConcatNestedNamespacesCheck.h" -#include "ConditionalToIfCheck.h" #include "DeprecatedHeadersCheck.h" #include "DeprecatedIosBaseAliasesCheck.h" #include "LoopConvertCheck.h" @@ -135,8 +134,6 @@ class ModernizeModule : public ClangTidyModule { CheckFactories.registerCheck<UseUncaughtExceptionsCheck>( "modernize-use-uncaught-exceptions"); CheckFactories.registerCheck<UseUsingCheck>("modernize-use-using"); - CheckFactories.registerCheck<ConditionalToIfCheck>( - "modernize-conditional-to-if"); } }; diff --git a/clang-tools-extra/clang-tidy/readability/CMakeLists.txt b/clang-tools-extra/clang-tidy/readability/CMakeLists.txt index 91e9354d454d2..d52c96d212ba6 100644 --- a/clang-tools-extra/clang-tidy/readability/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/readability/CMakeLists.txt @@ -62,6 +62,7 @@ add_clang_library(clangTidyReadabilityModule STATIC UseAnyOfAllOfCheck.cpp UseConcisePreprocessorDirectivesCheck.cpp UseStdMinMaxCheck.cpp + ConditionalToIfCheck.cpp LINK_LIBS clangTidy diff --git a/clang-tools-extra/clang-tidy/readability/ConditionalToIfCheck.cpp b/clang-tools-extra/clang-tidy/readability/ConditionalToIfCheck.cpp new file mode 100644 index 0000000000000..191e18e202a27 --- /dev/null +++ b/clang-tools-extra/clang-tidy/readability/ConditionalToIfCheck.cpp @@ -0,0 +1,85 @@ +//===--- ConditionalToIfCheck.cpp - 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 +// +//===----------------------------------------------------------------------===// +// +// 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 + std::string CondStr = Lexer::getSourceText( + CharSourceRange::getTokenRange(Cond->getSourceRange()), SM, + Result.Context->getLangOpts()).str(); + + std::string TrueStr = Lexer::getSourceText( + CharSourceRange::getTokenRange(TrueExpr->getSourceRange()), SM, + Result.Context->getLangOpts()).str(); + + std::string FalseStr = Lexer::getSourceText( + CharSourceRange::getTokenRange(FalseExpr->getSourceRange()), SM, + Result.Context->getLangOpts()).str(); + + // Construct the replacement code + std::string Replacement = + "{ if (" + CondStr + ") " + TrueStr + "; else " + FalseStr + "; }"; + + Diag << FixItHint::CreateReplacement(Ternary->getSourceRange(), Replacement); +} + +} // namespace clang::tidy::readability + diff --git a/clang-tools-extra/clang-tidy/readability/ConditionalToIfCheck.h b/clang-tools-extra/clang-tidy/readability/ConditionalToIfCheck.h new file mode 100644 index 0000000000000..2c35b8d1a2cbb --- /dev/null +++ b/clang-tools-extra/clang-tidy/readability/ConditionalToIfCheck.h @@ -0,0 +1,35 @@ +//===--- ConditionalToIfCheck.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 +// +//===----------------------------------------------------------------------===// +// +// 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 + diff --git a/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp b/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp index 569302e6065f2..cfe69c399efc9 100644 --- a/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp @@ -65,6 +65,7 @@ #include "UseAnyOfAllOfCheck.h" #include "UseConcisePreprocessorDirectivesCheck.h" #include "UseStdMinMaxCheck.h" +#include "ConditionalToIfCheck.h" namespace clang::tidy { namespace readability { @@ -184,6 +185,8 @@ class ReadabilityModule : public ClangTidyModule { "readability-use-concise-preprocessor-directives"); CheckFactories.registerCheck<UseStdMinMaxCheck>( "readability-use-std-min-max"); + CheckFactories.registerCheck<ConditionalToIfCheck>( + "readability-conditional-to-if"); } }; >From de20ffd288d57981af13ca65a1dee8b1148e1308 Mon Sep 17 00:00:00 2001 From: Dheeraj <[email protected]> Date: Fri, 7 Nov 2025 23:59:24 +0530 Subject: [PATCH 5/7] changes for alphabetical order --- .../clang-tidy/readability/CMakeLists.txt | 2 +- .../readability/ConditionalToIfCheck.cpp | 41 ++++++++++--------- .../readability/ConditionalToIfCheck.h | 4 +- .../readability/ReadabilityTidyModule.cpp | 6 +-- 4 files changed, 28 insertions(+), 25 deletions(-) diff --git a/clang-tools-extra/clang-tidy/readability/CMakeLists.txt b/clang-tools-extra/clang-tidy/readability/CMakeLists.txt index d52c96d212ba6..8b0f291f5afc9 100644 --- a/clang-tools-extra/clang-tidy/readability/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/readability/CMakeLists.txt @@ -10,6 +10,7 @@ add_clang_library(clangTidyReadabilityModule STATIC AvoidReturnWithVoidValueCheck.cpp AvoidUnconditionalPreprocessorIfCheck.cpp BracesAroundStatementsCheck.cpp + ConditionalToIfCheck.cpp ConstReturnTypeCheck.cpp ContainerContainsCheck.cpp ContainerDataPointerCheck.cpp @@ -62,7 +63,6 @@ add_clang_library(clangTidyReadabilityModule STATIC UseAnyOfAllOfCheck.cpp UseConcisePreprocessorDirectivesCheck.cpp UseStdMinMaxCheck.cpp - ConditionalToIfCheck.cpp LINK_LIBS clangTidy diff --git a/clang-tools-extra/clang-tidy/readability/ConditionalToIfCheck.cpp b/clang-tools-extra/clang-tidy/readability/ConditionalToIfCheck.cpp index 191e18e202a27..d7715c4d25f99 100644 --- a/clang-tools-extra/clang-tidy/readability/ConditionalToIfCheck.cpp +++ b/clang-tools-extra/clang-tidy/readability/ConditionalToIfCheck.cpp @@ -1,4 +1,5 @@ -//===--- ConditionalToIfCheck.cpp - clang-tidy -------------------*- C++ -*-===// +//===--- ConditionalToIfCheck.cpp - 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. @@ -26,7 +27,7 @@ #include "ConditionalToIfCheck.h" #include "clang/AST/ASTContext.h" #include "clang/ASTMatchers/ASTMatchFinder.h" -#include "clang/Lex/Lexer.h" // <-- ADD THIS INCLUDE +#include "clang/Lex/Lexer.h" // <-- ADD THIS INCLUDE #include "clang/Rewrite/Core/Rewriter.h" using namespace clang::ast_matchers; @@ -36,11 +37,10 @@ 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"), + conditionalOperator(hasTrueExpression(expr().bind("trueExpr")), + hasFalseExpression(expr().bind("falseExpr")), + hasCondition(expr().bind("condExpr"))) + .bind("ternary"), this); } @@ -58,21 +58,25 @@ void ConditionalToIfCheck::check(const MatchFinder::MatchResult &Result) { const SourceManager &SM = *Result.SourceManager; - auto Diag = diag(Ternary->getBeginLoc(), - "replace ternary operator with if/else statement for readability"); + auto Diag = + diag(Ternary->getBeginLoc(), + "replace ternary operator with if/else statement for readability"); // Extract source text for condition, true and false expressions - std::string CondStr = Lexer::getSourceText( - CharSourceRange::getTokenRange(Cond->getSourceRange()), SM, - Result.Context->getLangOpts()).str(); + std::string CondStr = Lexer::getSourceText(CharSourceRange::getTokenRange( + Cond->getSourceRange()), + SM, Result.Context->getLangOpts()) + .str(); - std::string TrueStr = Lexer::getSourceText( - CharSourceRange::getTokenRange(TrueExpr->getSourceRange()), SM, - Result.Context->getLangOpts()).str(); + std::string TrueStr = Lexer::getSourceText(CharSourceRange::getTokenRange( + TrueExpr->getSourceRange()), + SM, Result.Context->getLangOpts()) + .str(); - std::string FalseStr = Lexer::getSourceText( - CharSourceRange::getTokenRange(FalseExpr->getSourceRange()), SM, - Result.Context->getLangOpts()).str(); + std::string FalseStr = Lexer::getSourceText(CharSourceRange::getTokenRange( + FalseExpr->getSourceRange()), + SM, Result.Context->getLangOpts()) + .str(); // Construct the replacement code std::string Replacement = @@ -82,4 +86,3 @@ void ConditionalToIfCheck::check(const MatchFinder::MatchResult &Result) { } } // namespace clang::tidy::readability - diff --git a/clang-tools-extra/clang-tidy/readability/ConditionalToIfCheck.h b/clang-tools-extra/clang-tidy/readability/ConditionalToIfCheck.h index 2c35b8d1a2cbb..00510e125c437 100644 --- a/clang-tools-extra/clang-tidy/readability/ConditionalToIfCheck.h +++ b/clang-tools-extra/clang-tidy/readability/ConditionalToIfCheck.h @@ -1,4 +1,5 @@ -//===--- ConditionalToIfCheck.h - clang-tidy ---------------------*- C++ -*-===// +//===--- ConditionalToIfCheck.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. @@ -32,4 +33,3 @@ class ConditionalToIfCheck : public ClangTidyCheck { } // namespace clang::tidy::readability #endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_CONDITIONALTOIFCHECK_H - diff --git a/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp b/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp index cfe69c399efc9..04011227588f5 100644 --- a/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp @@ -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" @@ -65,7 +66,6 @@ #include "UseAnyOfAllOfCheck.h" #include "UseConcisePreprocessorDirectivesCheck.h" #include "UseStdMinMaxCheck.h" -#include "ConditionalToIfCheck.h" namespace clang::tidy { namespace readability { @@ -85,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>( @@ -185,8 +187,6 @@ class ReadabilityModule : public ClangTidyModule { "readability-use-concise-preprocessor-directives"); CheckFactories.registerCheck<UseStdMinMaxCheck>( "readability-use-std-min-max"); - CheckFactories.registerCheck<ConditionalToIfCheck>( - "readability-conditional-to-if"); } }; >From 6c18c8566461e8bc3902b766aaea2a913249bec4 Mon Sep 17 00:00:00 2001 From: Dheeraj <[email protected]> Date: Sat, 8 Nov 2025 00:14:13 +0530 Subject: [PATCH 6/7] apply suggested changes --- .../clang-tidy/readability/ConditionalToIfCheck.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/clang-tools-extra/clang-tidy/readability/ConditionalToIfCheck.cpp b/clang-tools-extra/clang-tidy/readability/ConditionalToIfCheck.cpp index d7715c4d25f99..9fb3ac241d04e 100644 --- a/clang-tools-extra/clang-tidy/readability/ConditionalToIfCheck.cpp +++ b/clang-tools-extra/clang-tidy/readability/ConditionalToIfCheck.cpp @@ -1,5 +1,4 @@ -//===--- ConditionalToIfCheck.cpp - 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. @@ -63,7 +62,7 @@ void ConditionalToIfCheck::check(const MatchFinder::MatchResult &Result) { "replace ternary operator with if/else statement for readability"); // Extract source text for condition, true and false expressions - std::string CondStr = Lexer::getSourceText(CharSourceRange::getTokenRange( + const std::string CondStr = Lexer::getSourceText(CharSourceRange::getTokenRange( Cond->getSourceRange()), SM, Result.Context->getLangOpts()) .str(); >From 9dae802767a7e1fe7ff78508bb3558272814101d Mon Sep 17 00:00:00 2001 From: Dheeraj <[email protected]> Date: Sat, 8 Nov 2025 00:26:27 +0530 Subject: [PATCH 7/7] apply more suggested changes --- .../clang-tidy/readability/ConditionalToIfCheck.cpp | 6 +++--- .../clang-tidy/readability/ConditionalToIfCheck.h | 3 +-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/clang-tools-extra/clang-tidy/readability/ConditionalToIfCheck.cpp b/clang-tools-extra/clang-tidy/readability/ConditionalToIfCheck.cpp index 9fb3ac241d04e..1946f91f03e5c 100644 --- a/clang-tools-extra/clang-tidy/readability/ConditionalToIfCheck.cpp +++ b/clang-tools-extra/clang-tidy/readability/ConditionalToIfCheck.cpp @@ -67,18 +67,18 @@ void ConditionalToIfCheck::check(const MatchFinder::MatchResult &Result) { SM, Result.Context->getLangOpts()) .str(); - std::string TrueStr = Lexer::getSourceText(CharSourceRange::getTokenRange( + const std::string TrueStr = Lexer::getSourceText(CharSourceRange::getTokenRange( TrueExpr->getSourceRange()), SM, Result.Context->getLangOpts()) .str(); - std::string FalseStr = Lexer::getSourceText(CharSourceRange::getTokenRange( + const std::string FalseStr = Lexer::getSourceText(CharSourceRange::getTokenRange( FalseExpr->getSourceRange()), SM, Result.Context->getLangOpts()) .str(); // Construct the replacement code - std::string Replacement = + const std::string Replacement = "{ if (" + CondStr + ") " + TrueStr + "; else " + FalseStr + "; }"; Diag << FixItHint::CreateReplacement(Ternary->getSourceRange(), Replacement); diff --git a/clang-tools-extra/clang-tidy/readability/ConditionalToIfCheck.h b/clang-tools-extra/clang-tidy/readability/ConditionalToIfCheck.h index 00510e125c437..036024cbcaf4b 100644 --- a/clang-tools-extra/clang-tidy/readability/ConditionalToIfCheck.h +++ b/clang-tools-extra/clang-tidy/readability/ConditionalToIfCheck.h @@ -1,5 +1,4 @@ -//===--- ConditionalToIfCheck.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. _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
