================
@@ -0,0 +1,646 @@
+//===--- 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/DeclTemplate.h"
+#include "clang/AST/Stmt.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Basic/Diagnostic.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 <cassert>
+#include <optional>
+#include <string>
+#include <vector>
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::readability {
+
+static constexpr llvm::StringLiteral AllowUserDefinedBoolConversionStr =
+    "AllowUserDefinedBoolConversion";
+static constexpr llvm::StringLiteral MergeableIfDiag =
+    "nested if statements can be merged";
+static constexpr llvm::StringLiteral NestedIfNote =
+    "nested if statement to merge is here";
+
+namespace {
+
+using IfChain = llvm::SmallVector<const IfStmt *>;
+
+enum class ChainHandling {
+  None,
+  WarnOnly,
+  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 contextual user-defined conversion to `bool`.
+static bool containsUserDefinedBoolConversion(const Expr *Expression) {
+  assert(Expression);
+
+  if (const auto *Cast = dyn_cast<ImplicitCastExpr>(Expression);
+      Cast && Cast->getCastKind() == CK_UserDefinedConversion)
+    return true;
+
+  return llvm::any_of(Expression->children(), [](const Stmt *Child) {
+    const auto *ChildExpr = dyn_cast_or_null<Expr>(Child);
+    return ChildExpr && containsUserDefinedBoolConversion(ChildExpr);
+  });
+}
+
+static bool conditionNeedsBoolCast(const Expr *Condition) {
+  assert(Condition);
+
+  if (!containsUserDefinedBoolConversion(Condition))
+    return false;
+
+  const Expr *const Unwrapped =
+      Condition->IgnoreImplicitAsWritten()->IgnoreParens();
+  if (!Unwrapped)
+    return true;
+
+  const QualType ConditionType = Unwrapped->getType();
+  return ConditionType.isNull() || !ConditionType->isScalarType();
+}
+
+static bool
+isConditionExpressionMergeable(const Expr *Condition,
+                               bool AllowUserDefinedBoolConversion) {
+  assert(Condition);
+
+  if (Condition->isTypeDependent())
+    return false;
+
+  if (containsUserDefinedBoolConversion(Condition))
+    return AllowUserDefinedBoolConversion;
+
+  const Expr *const Unwrapped = Condition->IgnoreParenImpCasts();
+  if (!Unwrapped)
+    return false;
+
+  const QualType ConditionType = Unwrapped->getType();
+  return !ConditionType.isNull() && ConditionType->isScalarType();
+}
+
+static std::optional<CharSourceRange>
+getIfConditionRange(const IfStmt *If, const SourceManager &SM,
+                    const LangOptions &LangOpts) {
+  assert(If);
+
+  const SourceLocation ConditionBegin =
+      Lexer::getLocForEndOfToken(If->getLParenLoc(), 0, SM, LangOpts);
+  if (ConditionBegin.isInvalid() || If->getRParenLoc().isInvalid())
+    return std::nullopt;
+
+  const CharSourceRange ConditionRange =
+      CharSourceRange::getCharRange(ConditionBegin, If->getRParenLoc());
+  const CharSourceRange FileRange =
+      Lexer::makeFileCharRange(ConditionRange, SM, LangOpts);
+  if (FileRange.isInvalid())
+    return std::nullopt;
+
+  return FileRange;
+}
+
+static std::optional<std::string>
+getIfConditionText(const IfStmt *If, const SourceManager &SM,
+                   const LangOptions &LangOpts) {
+  const std::optional<CharSourceRange> ConditionRange =
+      getIfConditionRange(If, SM, LangOpts);
+  if (!ConditionRange)
+    return std::nullopt;
+
+  bool Invalid = false;
+  const StringRef ConditionText =
+      Lexer::getSourceText(*ConditionRange, SM, LangOpts, &Invalid);
+  return Invalid || ConditionText.empty()
+             ? std::nullopt
+             : std::optional<std::string>(ConditionText.str());
+}
+
+static bool isLocationInCharRange(SourceLocation Loc, CharSourceRange Range,
+                                  const SourceManager &SM) {
+  return Loc.isValid() && Range.isValid() &&
+         !SM.isBeforeInTranslationUnit(Loc, Range.getBegin()) &&
+         SM.isBeforeInTranslationUnit(Loc, Range.getEnd());
+}
+
+// Keep fix-its only when comments in removed nested headers stay inside the
+// preserved condition text. Other comment placements keep the diagnostic but
+// suppress the rewrite.
+static bool hasOnlyPayloadCommentsInNestedHeader(const IfStmt *Nested,
+                                                 const SourceManager &SM,
+                                                 const LangOptions &LangOpts) {
+  assert(Nested);
+
+  const CharSourceRange HeaderRange = CharSourceRange::getCharRange(
+      Nested->getBeginLoc(), Nested->getThen()->getBeginLoc());
+  const CharSourceRange HeaderFileRange =
+      Lexer::makeFileCharRange(HeaderRange, SM, LangOpts);
----------------
vbvictor wrote:

Ditto

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