================ @@ -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
