================
@@ -0,0 +1,784 @@
+//===--- RedundantNestedIfCheck.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 "RedundantNestedIfCheck.h"
+#include "../utils/LexerUtils.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Stmt.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Tooling/FixIt.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallVector.h"
+#include <optional>
+#include <string>
+#include <vector>
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy {
+template <>
+struct OptionEnumMapping<
+    readability::RedundantNestedIfCheck::UserDefinedBoolConversionMode> {
+  static llvm::ArrayRef<std::pair<
+      readability::RedundantNestedIfCheck::UserDefinedBoolConversionMode,
+      StringRef>>
+  getEnumMapping() {
+    using Mode =
+        readability::RedundantNestedIfCheck::UserDefinedBoolConversionMode;
+    static constexpr std::pair<Mode, StringRef> Mapping[] = {
+        {Mode::None, "None"},
+        {Mode::WarnOnly, "WarnOnly"},
+        {Mode::WarnAndFix, "WarnAndFix"},
+    };
+    return {Mapping};
+  }
+};
+} // namespace clang::tidy
+
+namespace clang::tidy::readability {
+
+static constexpr llvm::StringLiteral WarnOnDependentConstexprIfStr =
+    "WarnOnDependentConstexprIf";
+static constexpr llvm::StringLiteral UserDefinedBoolConversionModeStr =
+    "UserDefinedBoolConversionMode";
+
+namespace {
+enum class ChainHandling {
+  None,
+  WarnOnly,
+  WarnOnlyDependentConstexpr,
+  WarnAndFix,
+};
+
+enum class CombinedConditionBuildStatus {
+  Success,
+  UnsupportedCommentPlacement,
+  Failure,
+};
+
+struct CombinedConditionBuildResult {
+  CombinedConditionBuildStatus Status = CombinedConditionBuildStatus::Failure;
+  std::string Text;
+};
+} // namespace
+
+// Conjoining conditions with `&&` can change behavior when a condition relies
+// on user-defined bool conversion. Keep the check conservative and reject such
+// conditions for automatic merging.
+static bool containsUserDefinedBoolConversion(const Expr *ExprNode) {
+  if (!ExprNode)
+    return false;
+
+  if (const auto *Cast = dyn_cast<ImplicitCastExpr>(ExprNode);
+      Cast && Cast->getCastKind() == CK_UserDefinedConversion)
+    return true;
+
+  return llvm::any_of(ExprNode->children(), [](const Stmt *Child) {
+    const auto *ChildExpr = dyn_cast_or_null<Expr>(Child);
+    return ChildExpr && containsUserDefinedBoolConversion(ChildExpr);
+  });
+}
+
+static bool isConditionExpressionSafeToConjoin(
+    const Expr *Cond, RedundantNestedIfCheck::UserDefinedBoolConversionMode
+                          UserBoolConversionMode) {
+  if (!Cond || Cond->isTypeDependent())
+    return false;
+  const bool HasUserDefinedBoolConversion =
+      containsUserDefinedBoolConversion(Cond);
+  if (UserBoolConversionMode !=
+          RedundantNestedIfCheck::UserDefinedBoolConversionMode::WarnAndFix &&
+      HasUserDefinedBoolConversion) {
+    return false;
+  }
+  const Expr *Unwrapped = Cond->IgnoreParenImpCasts();
+  if (!Unwrapped)
+    return false;
+  const QualType CondType = Unwrapped->getType();
+  if (CondType.isNull())
+    return false;
+  if (CondType->isScalarType())
+    return true;
+  return UserBoolConversionMode ==
+         RedundantNestedIfCheck::UserDefinedBoolConversionMode::WarnAndFix;
+}
+
+static std::optional<CharSourceRange>
+getConditionPayloadRange(const IfStmt *If, const SourceManager &SM,
+                         const LangOptions &LangOpts) {
+  if (!If)
+    return std::nullopt;
+  const SourceLocation PayloadBegin =
+      Lexer::getLocForEndOfToken(If->getLParenLoc(), 0, SM, LangOpts);
+  if (PayloadBegin.isInvalid() || If->getRParenLoc().isInvalid())
+    return std::nullopt;
+
+  const CharSourceRange PayloadRange =
+      CharSourceRange::getCharRange(PayloadBegin, If->getRParenLoc());
+  const CharSourceRange FileRange =
+      Lexer::makeFileCharRange(PayloadRange, SM, LangOpts);
+  if (FileRange.isInvalid())
+    return std::nullopt;
+  return FileRange;
+}
+
+static std::optional<std::string>
+getConditionPayloadText(const IfStmt *If, const SourceManager &SM,
+                        const LangOptions &LangOpts) {
+  const std::optional<CharSourceRange> PayloadRange =
+      getConditionPayloadRange(If, SM, LangOpts);
+  if (!PayloadRange)
+    return std::nullopt;
+
+  bool Invalid = false;
+  const StringRef PayloadText =
+      Lexer::getSourceText(*PayloadRange, SM, LangOpts, &Invalid);
+  if (Invalid || PayloadText.empty())
+    return std::nullopt;
+  return PayloadText.str();
+}
+
+static std::vector<utils::lexer::CommentToken>
+getCommentTokensInRange(CharSourceRange Range, const SourceManager &SM,
----------------
vbvictor wrote:

Can we place it in lexer utils?
IIRC, such function was needed in some other check?

https://github.com/llvm/llvm-project/pull/181558
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to