[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
Mon Apr 22 23:00:13 PDT 2024
================
@@ -0,0 +1,274 @@
+//===--- 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;
+ SmallVector<const clang::Expr *, 2> 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;
+
+ // check if the function has initializer list argument
+ if (Call->getNumArgs() < 3) {
+ auto ArgIterator = Call->arguments().begin();
+
+ const auto *InitListExpr =
+ dyn_cast<CXXStdInitializerListExpr>(*ArgIterator);
+ const auto *InitList =
+ InitListExpr != nullptr
+ ? dyn_cast<clang::InitListExpr>(
+ InitListExpr->getSubExpr()->IgnoreImplicit())
+ : nullptr;
+
+ if (InitList) {
+ Result.Args.append(InitList->inits().begin(), InitList->inits().end());
+ Result.First = *ArgIterator;
+ Result.Last = *ArgIterator;
+
+ // check if there is a comparison argument
+ std::advance(ArgIterator, 1);
+ if (ArgIterator != Call->arguments().end())
+ Result.Compare = *ArgIterator;
+
+ return Result;
+ }
+ } else {
+ // if it has 3 arguments then the last will be the comparison
+ Result.Compare = *(std::next(Call->arguments().begin(), 2));
+ }
+
+ if (Result.Compare)
+ Result.Args = SmallVector<const Expr *>(llvm::drop_end(Call->arguments()));
+ else
+ Result.Args = SmallVector<const Expr *>(Call->arguments());
+
+ Result.First = Result.Args.front();
+ Result.Last = Result.Args.back();
+
+ return Result;
+}
+
+static SmallVector<FixItHint>
+generateReplacements(const MatchFinder::MatchResult &Match,
+ const CallExpr *TopCall, const FindArgsResult &Result,
+ const bool IgnoreNonTrivialTypes,
+ const unsigned long IgnoreTrivialTypesOfSizeAbove) {
+ SmallVector<FixItHint> FixItHints;
+
+ const QualType ResultType = TopCall->getDirectCallee()
+ ->getReturnType()
+ .getNonReferenceType()
+ .getUnqualifiedType()
+ .getCanonicalType();
+
+ // check if the type is trivial
+ const bool isResultTypeTrivial = Match.Context->getBaseElementType(ResultType)
+ .isTrivialType(*Match.Context);
+ const SourceManager &SourceMngr = *Match.SourceManager;
+ const LangOptions &LanguageOpts = Match.Context->getLangOpts();
+
+ if ((!isResultTypeTrivial && IgnoreNonTrivialTypes))
+ return FixItHints;
+
+ if (isResultTypeTrivial &&
+ // size in bits divided by 8 to get bytes
+ Match.Context->getTypeSize(ResultType) / 8 >
+ IgnoreTrivialTypesOfSizeAbove)
+ return FixItHints;
+
+ for (const Expr *Arg : Result.Args) {
+ const auto *InnerCall = dyn_cast<CallExpr>(Arg->IgnoreParenImpCasts());
+
+ // If the argument is not a nested call
+ if (!InnerCall) {
+ // check if typecast is required
+ const QualType ArgType = Arg->IgnoreParenImpCasts()
+ ->getType()
+ .getUnqualifiedType()
+ .getCanonicalType();
+
+ if (ArgType == ResultType)
+ continue;
+
+ const StringRef ArgText = Lexer::getSourceText(
+ CharSourceRange::getTokenRange(Arg->getSourceRange()), SourceMngr,
+ LanguageOpts);
+
+ Twine Replacement = llvm::Twine("static_cast<")
+ .concat(ResultType.getAsString(LanguageOpts))
+ .concat(">(")
+ .concat(ArgText)
+ .concat(")");
----------------
PiotrZSL wrote:
And this is reported by address sanitizer:
```
==335149==ERROR: AddressSanitizer: stack-use-after-scope on address 0x725ae7653890 at pc 0x56da3506075a bp 0x7ffccd4af960 sp 0x7ffccd4af958
READ of size 8 at 0x725ae7653890 thread T0
#0 0x56da35060759 in print /fpwork/llvm-project/llvm/lib/Support/Twine.cpp:165:21
#1 0x56da35060759 in llvm::Twine::printOneChild(llvm::raw_ostream&, llvm::Twine::Child, llvm::Twine::NodeKind) const /fpwork/llvm-project/llvm/lib/Support/Twine.cpp:65:16
#2 0x56da3505f618 in print /fpwork/llvm-project/llvm/lib/Support/Twine.cpp:165:3
#3 0x56da3505f618 in toVector /fpwork/llvm-project/llvm/lib/Support/Twine.cpp:34:3
#4 0x56da3505f618 in llvm::Twine::toStringRef(llvm::SmallVectorImpl<char>&) const /fpwork/llvm-project/llvm/include/llvm/ADT/Twine.h:495:7
#5 0x56da3505f190 in llvm::Twine::str[abi:cxx11]() const /fpwork/llvm-project/llvm/lib/Support/Twine.cpp:29:10
```
https://github.com/llvm/llvm-project/pull/85572
More information about the cfe-commits
mailing list