[clang-tools-extra] [clang-tidy] add check to suggest replacement of nested std::min or std::max with initializer lists (PR #85572)
Piotr Zegar via cfe-commits
cfe-commits at lists.llvm.org
Sun Mar 31 10:45:11 PDT 2024
================
@@ -0,0 +1,252 @@
+//===--- MinMaxUseInitializerListCheck.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 "MinMaxUseInitializerListCheck.h"
+#include "../utils/ASTUtils.h"
+#include "../utils/LexerUtils.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang;
+
+namespace {
+
+struct FindArgsResult {
+ const Expr *First;
+ const Expr *Last;
+ const Expr *Compare;
+ std::vector<const clang::Expr *> Args;
+};
+
+} // anonymous namespace
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::modernize {
+
+static FindArgsResult findArgs(const CallExpr *Call) {
+ FindArgsResult Result;
+ Result.First = nullptr;
+ Result.Last = nullptr;
+ Result.Compare = nullptr;
+
+ if (Call->getNumArgs() == 3) {
+ auto ArgIterator = Call->arguments().begin();
+ std::advance(ArgIterator, 2);
+ Result.Compare = *ArgIterator;
+ } else {
+ auto ArgIterator = Call->arguments().begin();
+
+ if (const auto *InitListExpr =
+ dyn_cast<CXXStdInitializerListExpr>(*ArgIterator)) {
+ if (const auto *InitList = dyn_cast<clang::InitListExpr>(
+ InitListExpr->getSubExpr()->IgnoreImplicit())) {
+ Result.Args.insert(Result.Args.begin(), InitList->inits().begin(),
+ InitList->inits().end());
+
+ Result.First = *ArgIterator;
+ Result.Last = *ArgIterator;
+
+ std::advance(ArgIterator, 1);
+ if (ArgIterator != Call->arguments().end()) {
+ Result.Compare = *ArgIterator;
+ }
+ return Result;
+ }
+ }
+ }
+
+ for (const Expr *Arg : Call->arguments()) {
+ if (!Result.First)
+ Result.First = Arg;
+
+ if (Arg == Result.Compare)
+ continue;
+
+ Result.Args.push_back(Arg);
+ Result.Last = Arg;
+ }
+
+ return Result;
+}
+
+static std::vector<FixItHint>
+generateReplacement(const MatchFinder::MatchResult &Match,
+ const CallExpr *TopCall, const FindArgsResult &Result) {
+ std::vector<FixItHint> FixItHints;
+
+ const QualType ResultType = TopCall->getDirectCallee()
+ ->getReturnType()
+ .getNonReferenceType()
+ .getUnqualifiedType()
+ .getCanonicalType();
+ const bool IsInitializerList = Result.First == Result.Last;
+
+ if (!IsInitializerList)
+ FixItHints.push_back(
+ FixItHint::CreateInsertion(Result.First->getBeginLoc(), "{"));
+
+ for (const Expr *Arg : Result.Args) {
+ if (const auto *InnerCall =
+ dyn_cast<CallExpr>(Arg->IgnoreParenImpCasts())) {
+ const FindArgsResult InnerResult = findArgs(InnerCall);
+ const std::vector<FixItHint> InnerReplacements =
+ generateReplacement(Match, InnerCall, InnerResult);
+ if (InnerCall->getDirectCallee()->getQualifiedNameAsString() ==
+ TopCall->getDirectCallee()->getQualifiedNameAsString() &&
+ ((!Result.Compare && !InnerResult.Compare) ||
+ utils::areStatementsIdentical(Result.Compare, InnerResult.Compare,
+ *Match.Context))) {
+
+ FixItHints.push_back(
+ FixItHint::CreateRemoval(CharSourceRange::getTokenRange(
+ InnerCall->getCallee()->getSourceRange())));
+
+ const auto LParen = utils::lexer::findNextTokenSkippingComments(
+ InnerCall->getCallee()->getEndLoc(), *Match.SourceManager,
+ Match.Context->getLangOpts());
+ if (LParen && LParen->getKind() == tok::l_paren)
+ FixItHints.push_back(
+ FixItHint::CreateRemoval(SourceRange(LParen->getLocation())));
+
+ FixItHints.push_back(
+ FixItHint::CreateRemoval(SourceRange(InnerCall->getRParenLoc())));
+
+ if (InnerResult.First == InnerResult.Last) {
+ FixItHints.insert(FixItHints.end(), InnerReplacements.begin(),
+ InnerReplacements.end());
+
+ FixItHints.push_back(
+ FixItHint::CreateRemoval(CharSourceRange::getTokenRange(
+ InnerResult.First->getBeginLoc())));
+ FixItHints.push_back(FixItHint::CreateRemoval(
+ CharSourceRange::getTokenRange(InnerResult.First->getEndLoc())));
+ } else
+ FixItHints.insert(FixItHints.end(), InnerReplacements.begin() + 1,
+ InnerReplacements.end() - 1);
----------------
PiotrZSL wrote:
never use -1 on .end, it may work on std::vector, but thats not safe, just use std::advance on begin, and verify that we have at least 1 element.
https://github.com/llvm/llvm-project/pull/85572
More information about the cfe-commits
mailing list