https://github.com/unterumarmung updated https://github.com/llvm/llvm-project/pull/228104
>From 75d6cacfdeafbe0a8b102d842f6205382f3312ff Mon Sep 17 00:00:00 2001 From: Daniil Dudkin <[email protected]> Date: Thu, 1 Oct 2026 18:09:51 +0300 Subject: [PATCH 1/3] [clang-tidy] Add modernize-use-std-interpolation check Add a C++20 check that replaces manual midpoint and linear interpolation formulas with `std::midpoint` and `std::lerp`, inserting `<numeric>` and `<cmath>` as needed. Fixes #189490 Assisted by ChatGPT --- .../clang-tidy/modernize/CMakeLists.txt | 2 + .../modernize/ModernizeTidyModule.cpp | 3 + .../modernize/UseStdInterpolationCheck.cpp | 164 +++++ .../modernize/UseStdInterpolationCheck.h | 33 + clang-tools-extra/docs/ReleaseNotes.md | 5 + .../docs/clang-tidy/checks/list.md | 1 + .../checks/modernize/use-std-interpolation.md | 67 +++ .../checkers/Inputs/Headers/std/cmath | 10 + .../checkers/Inputs/Headers/std/numeric | 6 + .../modernize/Inputs/use-std-interpolation.h | 19 + .../use-std-interpolation-header.cpp | 4 + .../modernize/use-std-interpolation.cpp | 569 ++++++++++++++++++ 12 files changed, 883 insertions(+) create mode 100644 clang-tools-extra/clang-tidy/modernize/UseStdInterpolationCheck.cpp create mode 100644 clang-tools-extra/clang-tidy/modernize/UseStdInterpolationCheck.h create mode 100644 clang-tools-extra/docs/clang-tidy/checks/modernize/use-std-interpolation.md create mode 100644 clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/std/cmath create mode 100644 clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/std/numeric create mode 100644 clang-tools-extra/test/clang-tidy/checkers/modernize/Inputs/use-std-interpolation.h create mode 100644 clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-interpolation-header.cpp create mode 100644 clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-interpolation.cpp diff --git a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt index 23aeb393ad016..a7f6af3b3d151 100644 --- a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt @@ -49,6 +49,7 @@ add_clang_library(clangTidyModernizeModule STATIC UseStartsEndsWithCheck.cpp UseStdBitCheck.cpp UseStdFormatCheck.cpp + UseStdInterpolationCheck.cpp UseStdNumbersCheck.cpp UseStdPrintCheck.cpp UseStringViewCheck.cpp @@ -76,4 +77,5 @@ clang_target_link_libraries(clangTidyModernizeModule clangBasic clangLex clangTooling + clangTransformer ) diff --git a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp index 62676469aefe7..56f916ffc0544 100644 --- a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp @@ -49,6 +49,7 @@ #include "UseStartsEndsWithCheck.h" #include "UseStdBitCheck.h" #include "UseStdFormatCheck.h" +#include "UseStdInterpolationCheck.h" #include "UseStdNumbersCheck.h" #include "UseStdPrintCheck.h" #include "UseStringViewCheck.h" @@ -100,6 +101,8 @@ class ModernizeModule : public ClangTidyModule { "modernize-use-starts-ends-with"); CheckFactories.registerCheck<UseStdBitCheck>("modernize-use-std-bit"); CheckFactories.registerCheck<UseStdFormatCheck>("modernize-use-std-format"); + CheckFactories.registerCheck<UseStdInterpolationCheck>( + "modernize-use-std-interpolation"); CheckFactories.registerCheck<UseStdNumbersCheck>( "modernize-use-std-numbers"); CheckFactories.registerCheck<UseStdPrintCheck>("modernize-use-std-print"); diff --git a/clang-tools-extra/clang-tidy/modernize/UseStdInterpolationCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStdInterpolationCheck.cpp new file mode 100644 index 0000000000000..5febc5835b8ff --- /dev/null +++ b/clang-tools-extra/clang-tidy/modernize/UseStdInterpolationCheck.cpp @@ -0,0 +1,164 @@ +//===----------------------------------------------------------------------===// +// +// 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 "UseStdInterpolationCheck.h" +#include "../utils/Matchers.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchers.h" +#include "clang/Tooling/Transformer/RewriteRule.h" +#include "clang/Tooling/Transformer/Stencil.h" + +using namespace clang::ast_matchers; +using namespace clang::transformer; + +namespace clang::tidy::modernize { +namespace { + +AST_MATCHER(Expr, hasSideEffects) { + return Node.HasSideEffects(Finder->getASTContext()); +} + +AST_MATCHER(Expr, isMacroExpanded) { + if (Node.getBeginLoc().isMacroID() || Node.getEndLoc().isMacroID()) + return true; + if (const auto *Operator = dyn_cast<BinaryOperator>(&Node)) + return Operator->getOperatorLoc().isMacroID(); + return false; +} + +AST_MATCHER_P(QualType, hasUnqualifiedCanonicalType, + ast_matchers::internal::Matcher<QualType>, InnerMatcher) { + return InnerMatcher.matches(Node.getCanonicalType().getUnqualifiedType(), + Finder, Builder); +} + +AST_MATCHER(QualType, isInterpolationFloatingType) { + return Node->isSpecificBuiltinType(BuiltinType::Float) || + Node->isSpecificBuiltinType(BuiltinType::Double) || + Node->isSpecificBuiltinType(BuiltinType::LongDouble); +} + +AST_MATCHER(QualType, isInterpolationIntegerType) { + return Node->isSpecificBuiltinType(BuiltinType::Int) || + Node->isSpecificBuiltinType(BuiltinType::UInt) || + Node->isSpecificBuiltinType(BuiltinType::Long) || + Node->isSpecificBuiltinType(BuiltinType::ULong) || + Node->isSpecificBuiltinType(BuiltinType::LongLong) || + Node->isSpecificBuiltinType(BuiltinType::ULongLong); +} + +using BinaryOperatorMatcher = ast_matchers::internal::Matcher<BinaryOperator>; + +struct InterpolationMatchers { + BinaryOperatorMatcher Midpoint; + BinaryOperatorMatcher Lerp; +}; + +} // namespace + +static InterpolationMatchers makeInterpolationMatchers() { + const auto SameType = hasType(qualType(hasUnqualifiedCanonicalType( + qualType(equalsBoundNode("calculationType"))))); + const auto Start = ignoringParenImpCasts(expr(SameType).bind("start")); + const auto End = ignoringParenImpCasts(expr(SameType).bind("end")); + const auto Factor = ignoringParenImpCasts(expr(SameType).bind("factor")); + const auto RepeatedStart = ignoringParenImpCasts( + expr(matchers::isStatementIdenticalToBoundNode("start"))); + const auto RepeatedFactor = ignoringParenImpCasts( + expr(matchers::isStatementIdenticalToBoundNode("factor"))); + const auto Two = ignoringParenImpCasts( + expr(anyOf(integerLiteral(equals(2)), floatLiteral(equals(2.0))))); + const auto Half = ignoringParenImpCasts(floatLiteral(equals(0.5))); + const auto One = ignoringParenImpCasts( + expr(anyOf(integerLiteral(equals(1)), floatLiteral(equals(1.0))))); + + // Midpoints: (a + b) / 2 and a + (b - a) / 2, also using * 0.5. + const auto Sum = ignoringParenImpCasts(binaryOperator( + hasOperatorName("+"), SameType, hasLHS(Start), hasRHS(End))); + const auto Difference = ignoringParenImpCasts(binaryOperator( + hasOperatorName("-"), SameType, hasLHS(End), hasRHS(RepeatedStart))); + const auto HalfSum = binaryOperator( + anyOf(allOf(hasOperatorName("/"), hasLHS(Sum), hasRHS(Two)), + allOf(hasOperatorName("*"), hasOperands(Sum, Half)))); + const auto HalfDifference = ignoringParenImpCasts(binaryOperator( + anyOf(allOf(hasOperatorName("/"), hasLHS(Difference), hasRHS(Two)), + allOf(hasOperatorName("*"), hasOperands(Difference, Half))))); + const auto DifferenceMidpoint = + binaryOperator(hasOperatorName("+"), hasOperands(Start, HalfDifference)); + + // Difference-form interpolation: a + (b - a) * t. + const auto ScaledDifference = ignoringParenImpCasts(binaryOperator( + hasOperatorName("*"), SameType, hasOperands(Difference, Factor))); + const auto DifferenceLerp = binaryOperator( + hasOperatorName("+"), hasOperands(Start, ScaledDifference)); + + // Weighted interpolation: (1 - t) * a + t * b. + const auto Complement = ignoringParenImpCasts(binaryOperator( + hasOperatorName("-"), SameType, hasLHS(One), hasRHS(Factor))); + const auto WeightedStart = ignoringParenImpCasts(binaryOperator( + hasOperatorName("*"), SameType, hasOperands(Start, Complement))); + const auto WeightedEnd = ignoringParenImpCasts(binaryOperator( + hasOperatorName("*"), SameType, hasOperands(End, RepeatedFactor))); + const auto WeightedLerp = binaryOperator( + hasOperatorName("+"), hasOperands(WeightedStart, WeightedEnd)); + + const auto NumericType = hasType(qualType( + hasUnqualifiedCanonicalType(qualType(anyOf(isInterpolationIntegerType(), + isInterpolationFloatingType())) + .bind("calculationType")))); + const auto FloatingType = hasType(qualType(hasUnqualifiedCanonicalType( + qualType(isInterpolationFloatingType()).bind("calculationType")))); + const auto Midpoint = + binaryOperator(NumericType, anyOf(HalfSum, DifferenceMidpoint)); + const auto Lerp = + binaryOperator(FloatingType, anyOf(DifferenceLerp, WeightedLerp)); + return {Midpoint, Lerp}; +} + +static BinaryOperatorMatcher eligibleCalculation() { + return binaryOperator( + unless(isExpansionInSystemHeader()), unless(isInTemplateInstantiation()), + unless(isTypeDependent()), unless(isValueDependent()), + unless(isMacroExpanded()), unless(hasSideEffects()), + unless(hasDescendant(expr(isMacroExpanded()))), + unless(hasDescendant(cxxOperatorCallExpr())), + unless(hasDescendant(cxxMemberCallExpr(callee(cxxConversionDecl())))), + unless(hasAncestor(expr(matchers::hasUnevaluatedContext()))), + unless(hasAncestor(typeLoc()))); +} + +static RewriteRuleWith<std::string> makeInterpolationRule() { + const auto Patterns = makeInterpolationMatchers(); + const auto Eligible = eligibleCalculation(); + const auto Pattern = binaryOperator(anyOf(Patterns.Midpoint, Patterns.Lerp)); + // Only replace innermost calculations to avoid overlapping edits. The + // bindings used to test descendants must not escape into the replacement. + const auto Innermost = + unless(hasDescendant(binaryOperator(Pattern, Eligible))); + + return applyFirst( + {makeRule( + binaryOperator(Patterns.Midpoint, Eligible, Innermost), + {changeTo(cat("std::midpoint(", expression("start"), ", ", + expression("end"), ")")), + addInclude("numeric", IncludeFormat::Angled)}, + cat("use 'std::midpoint' instead of manual midpoint calculation")), + makeRule( + binaryOperator(Patterns.Lerp, Eligible, Innermost), + {changeTo(cat("std::lerp(", expression("start"), ", ", + expression("end"), ", ", expression("factor"), ")")), + addInclude("cmath", IncludeFormat::Angled)}, + cat("use 'std::lerp' instead of manual linear interpolation"))}); +} + +UseStdInterpolationCheck::UseStdInterpolationCheck(StringRef Name, + ClangTidyContext *Context) + : utils::TransformerClangTidyCheck(makeInterpolationRule(), Name, Context) { +} + +} // namespace clang::tidy::modernize diff --git a/clang-tools-extra/clang-tidy/modernize/UseStdInterpolationCheck.h b/clang-tools-extra/clang-tidy/modernize/UseStdInterpolationCheck.h new file mode 100644 index 0000000000000..0177ac9f11872 --- /dev/null +++ b/clang-tools-extra/clang-tidy/modernize/UseStdInterpolationCheck.h @@ -0,0 +1,33 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USESTDINTERPOLATIONCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USESTDINTERPOLATIONCHECK_H + +#include "../utils/TransformerClangTidyCheck.h" + +namespace clang::tidy::modernize { + +/// Replaces manual midpoint and linear interpolation calculations. +/// +/// For the user-facing documentation see: +/// https://clang.llvm.org/extra/clang-tidy/checks/modernize/use-std-interpolation.html +class UseStdInterpolationCheck : public utils::TransformerClangTidyCheck { +public: + UseStdInterpolationCheck(StringRef Name, ClangTidyContext *Context); + bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { + return LangOpts.CPlusPlus20; + } + std::optional<TraversalKind> getCheckTraversalKind() const override { + return TK_IgnoreUnlessSpelledInSource; + } +}; + +} // namespace clang::tidy::modernize + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USESTDINTERPOLATIONCHECK_H diff --git a/clang-tools-extra/docs/ReleaseNotes.md b/clang-tools-extra/docs/ReleaseNotes.md index c2ee1b06c43e0..0bb4ab5ed1f59 100644 --- a/clang-tools-extra/docs/ReleaseNotes.md +++ b/clang-tools-extra/docs/ReleaseNotes.md @@ -134,6 +134,11 @@ infrastructure are described first, followed by tool-specific sections. Detects malformed regex patterns defined in a single string literal. +- New {doc}`modernize-use-std-interpolation + <clang-tidy/checks/modernize/use-std-interpolation>` check. + + Replaces manual midpoint and linear interpolation calculations. + - New {doc}`modernize-use-to-underlying <clang-tidy/checks/modernize/use-to-underlying>` check. diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.md b/clang-tools-extra/docs/clang-tidy/checks/list.md index a74a26e691053..128db420d0c19 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/list.md +++ b/clang-tools-extra/docs/clang-tidy/checks/list.md @@ -334,6 +334,7 @@ readability/* | {doc}`modernize-use-starts-ends-with <modernize/use-starts-ends-with>` | Yes | | {doc}`modernize-use-std-bit <modernize/use-std-bit>` | Yes | | {doc}`modernize-use-std-format <modernize/use-std-format>` | Yes | +| {doc}`modernize-use-std-interpolation <modernize/use-std-interpolation>` | Yes | | {doc}`modernize-use-std-numbers <modernize/use-std-numbers>` | Yes | | {doc}`modernize-use-std-print <modernize/use-std-print>` | Yes | | {doc}`modernize-use-string-view <modernize/use-string-view>` | Yes | diff --git a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-std-interpolation.md b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-std-interpolation.md new file mode 100644 index 0000000000000..f639e64dbaae7 --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-std-interpolation.md @@ -0,0 +1,67 @@ +```{title} clang-tidy - modernize-use-std-interpolation +``` + +# modernize-use-std-interpolation + +Replaces manual midpoint and linear interpolation calculations. + +The check suggests `std::midpoint` from `<numeric>` and `std::lerp` from `<cmath>` +to express the calculation's intent and avoid intermediate overflow. It requires +C++20 or later. + +For example: + +```c++ +int midpoint(int a, int b) { + return (a + b) / 2; +} + +double interpolate(double a, double b, double t) { + return a + (b - a) * t; +} +``` + +becomes: + +```c++ +#include <cmath> +#include <numeric> + +int midpoint(int a, int b) { + return std::midpoint(a, b); +} + +double interpolate(double a, double b, double t) { + return std::lerp(a, b, t); +} +``` + +The following expressions are recognized: + +| Expression | Replacement | +| --- | --- | +| `(a + b) / 2` | `std::midpoint(a, b)` | +| `a + (b - a) / 2` | `std::midpoint(a, b)` | +| `(a + b) * 0.5` | `std::midpoint(a, b)` | +| `a + (b - a) * 0.5` | `std::midpoint(a, b)` | +| `a + (b - a) * t` | `std::lerp(a, b, t)` | +| `(1 - t) * a + t * b` | `std::lerp(a, b, t)` | + +## Changes in numerical behavior + +Replacing integer `(a + b) / 2` can change rounding. Integer division truncates +toward zero, whereas `std::midpoint(a, b)` rounds toward `a`. For example, +`(2 + 1) / 2` yields `1`, but `std::midpoint(2, 1)` yields `2`. +Unsigned difference formulas can also change results when the subtraction wraps. + +Floating-point replacements can change rounding, overflow handling, signed zero, +and results involving infinities or NaNs. These replacements adopt the numerical +behavior of the standard library facilities; they do not promise identical +results to the original arithmetic. + +## Options + +### IncludeStyle + +A string specifying which include ordering convention to use: `llvm` or +`google`. The default is `llvm`. diff --git a/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/std/cmath b/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/std/cmath new file mode 100644 index 0000000000000..974dbdabf9107 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/std/cmath @@ -0,0 +1,10 @@ +#ifndef CLANG_TIDY_TEST_STD_CMATH +#define CLANG_TIDY_TEST_STD_CMATH +namespace std { +constexpr float lerp(float a, float b, float t) { return a + (b - a) * t; } +constexpr double lerp(double a, double b, double t) { return a + (b - a) * t; } +constexpr long double lerp(long double a, long double b, long double t) { + return a + (b - a) * t; +} +} // namespace std +#endif diff --git a/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/std/numeric b/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/std/numeric new file mode 100644 index 0000000000000..0aa4e1b83b16c --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/std/numeric @@ -0,0 +1,6 @@ +#ifndef CLANG_TIDY_TEST_STD_NUMERIC +#define CLANG_TIDY_TEST_STD_NUMERIC +namespace std { +template <class T> constexpr T midpoint(T a, T b) { return a + (b - a) / 2; } +} // namespace std +#endif diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/Inputs/use-std-interpolation.h b/clang-tools-extra/test/clang-tidy/checkers/modernize/Inputs/use-std-interpolation.h new file mode 100644 index 0000000000000..191b73d0dc608 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/Inputs/use-std-interpolation.h @@ -0,0 +1,19 @@ +#ifndef CLANG_TIDY_TEST_USE_STD_INTERPOLATION_H +#define CLANG_TIDY_TEST_USE_STD_INTERPOLATION_H + +#include <type_traits> +// CHECK-FIXES: #include <cmath> +// CHECK-FIXES-NEXT: #include <numeric> +// CHECK-FIXES-NEXT: #include <type_traits> + +inline double header_midpoint(double a, double b) { + return (a + b) / 2; + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use 'std::midpoint' instead of manual midpoint calculation + // CHECK-FIXES: return std::midpoint(a, b); +} +inline double header_interpolation(double a, double b, double t) { + return a + (b - a) * t; + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use 'std::lerp' instead of manual linear interpolation + // CHECK-FIXES: return std::lerp(a, b, t); +} +#endif diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-interpolation-header.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-interpolation-header.cpp new file mode 100644 index 0000000000000..327ec07c2ffba --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-interpolation-header.cpp @@ -0,0 +1,4 @@ +// RUN: %check_clang_tidy -std=c++20-or-later -check-header %S/Inputs/use-std-interpolation.h %s modernize-use-std-interpolation %t -- -header-filter=.* -format-style=llvm + +#include "use-std-interpolation.h" +#include "use-std-interpolation.h" diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-interpolation.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-interpolation.cpp new file mode 100644 index 0000000000000..43e65ac3e2fc7 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-interpolation.cpp @@ -0,0 +1,569 @@ +// RUN: %check_clang_tidy -std=c++20-or-later -check-suffixes=,MISSING %s modernize-use-std-interpolation %t \ +// RUN: -- -format-style="{BasedOnStyle: LLVM, IncludeBlocks: Regroup}" +// RUN: %check_clang_tidy -std=c++20-or-later -check-suffixes=,PRESENT %s modernize-use-std-interpolation %t-present \ +// RUN: -- -- -DINCLUDES_PRESENT +// RUN: clang-tidy %s -checks=-*,modernize-use-std-interpolation -allow-no-checks -- -std=c++11 2>&1 | count 0 +// RUN: clang-tidy %s -checks=-*,modernize-use-std-interpolation -allow-no-checks -- -std=c++14 2>&1 | count 0 +// RUN: clang-tidy %s -checks=-*,modernize-use-std-interpolation -allow-no-checks -- -std=c++17 2>&1 | count 0 +// RUN: clang-tidy %s -checks=-*,modernize-use-std-interpolation -allow-no-checks -- -x c -std=c17 2>&1 | count 0 + +// CHECK-FIXES-MISSING: #include <cmath> +// CHECK-FIXES-MISSING-NEXT: #include <numeric> + +#if __cplusplus >= 202002L + +#ifdef INCLUDES_PRESENT +#include <cmath> +#include <numeric> +#endif +// CHECK-FIXES-PRESENT-NOT: #include +// CHECK-FIXES-PRESENT: #ifdef INCLUDES_PRESENT +// CHECK-FIXES-PRESENT-NEXT: #include <cmath> +// CHECK-FIXES-PRESENT-NEXT: #include <numeric> +// CHECK-FIXES-PRESENT-NEXT: #endif +// CHECK-FIXES-PRESENT-NOT: #include + +void int_calculations(int a, int b) { + auto sum = (a + b) / 2; + // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: use 'std::midpoint' instead of manual midpoint calculation + // CHECK-FIXES: auto sum = std::midpoint(a, b); + + auto reversed_sum = (b + a) / 2; + // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: use 'std::midpoint' instead of manual midpoint calculation + // CHECK-FIXES: auto reversed_sum = std::midpoint(b, a); + + auto difference = a + (b - a) / 2; + // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: use 'std::midpoint' instead of manual midpoint calculation + // CHECK-FIXES: auto difference = std::midpoint(a, b); + + auto reversed_addition = (b - a) / 2 + a; + // CHECK-MESSAGES: :[[@LINE-1]]:28: warning: use 'std::midpoint' instead of manual midpoint calculation + // CHECK-FIXES: auto reversed_addition = std::midpoint(a, b); + + auto parenthesized = ((a) + ((b))) / (2); + // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: use 'std::midpoint' instead of manual midpoint calculation + // CHECK-FIXES: auto parenthesized = std::midpoint(a, b); + + auto parenthesized_difference = (a) + (((b) - (a)) / (2)); + // CHECK-MESSAGES: :[[@LINE-1]]:35: warning: use 'std::midpoint' instead of manual midpoint calculation + // CHECK-FIXES: auto parenthesized_difference = std::midpoint(a, b); + + auto signed_boundaries = (-2147483647 - 1 + 2147483647) / 2; + // CHECK-MESSAGES: :[[@LINE-1]]:28: warning: use 'std::midpoint' instead of manual midpoint calculation + // CHECK-FIXES: auto signed_boundaries = std::midpoint((-2147483647 - 1), 2147483647); + + auto odd_sum = (2 + 1) / 2; + // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: use 'std::midpoint' instead of manual midpoint calculation + // CHECK-FIXES: auto odd_sum = std::midpoint(2, 1); +} + +void unsigned_calculations(unsigned a, unsigned b) { + auto difference = a + (b - a) / 2U; + // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: use 'std::midpoint' instead of manual midpoint calculation + // CHECK-FIXES: auto difference = std::midpoint(a, b); + + auto sum_unsigned = (a + b) / 2; + // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: use 'std::midpoint' instead of manual midpoint calculation + // CHECK-FIXES: auto sum_unsigned = std::midpoint(a, b); + + auto boundaries = (0U + 4294967295U) / 2; + // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: use 'std::midpoint' instead of manual midpoint calculation + // CHECK-FIXES: auto boundaries = std::midpoint(0U, 4294967295U); +} + +void long_calculations(long a, long b) { + auto sum_long = (a + b) / 2; + // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: use 'std::midpoint' instead of manual midpoint calculation + // CHECK-FIXES: auto sum_long = std::midpoint(a, b); +} + +void unsigned_long_calculations(unsigned long a, unsigned long b) { + auto sum_unsigned_long = (a + b) / 2; + // CHECK-MESSAGES: :[[@LINE-1]]:28: warning: use 'std::midpoint' instead of manual midpoint calculation + // CHECK-FIXES: auto sum_unsigned_long = std::midpoint(a, b); +} + +void long_long_calculations(long long a, long long b) { + auto sum_long_long = (a + b) / 2; + // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: use 'std::midpoint' instead of manual midpoint calculation + // CHECK-FIXES: auto sum_long_long = std::midpoint(a, b); +} + +void unsigned_long_long_calculations(unsigned long long a, unsigned long long b) { + auto sum_unsigned_long_long = (a + b) / 2; + // CHECK-MESSAGES: :[[@LINE-1]]:33: warning: use 'std::midpoint' instead of manual midpoint calculation + // CHECK-FIXES: auto sum_unsigned_long_long = std::midpoint(a, b); +} + +void float_calculations(float a, float b, float t) { + auto sum = (a + b) / 2; + // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: use 'std::midpoint' instead of manual midpoint calculation + // CHECK-FIXES: auto sum = std::midpoint(a, b); + + auto floating_divisor = (a + b) / 2.0f; + // CHECK-MESSAGES: :[[@LINE-1]]:27: warning: use 'std::midpoint' instead of manual midpoint calculation + // CHECK-FIXES: auto floating_divisor = std::midpoint(a, b); + + auto half = (a + b) * 0.5f; + // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: use 'std::midpoint' instead of manual midpoint calculation + // CHECK-FIXES: auto half = std::midpoint(a, b); + + auto half_first = 0.5f * (a + b); + // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: use 'std::midpoint' instead of manual midpoint calculation + // CHECK-FIXES: auto half_first = std::midpoint(a, b); + + auto difference = a + (b - a) / 2; + // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: use 'std::midpoint' instead of manual midpoint calculation + // CHECK-FIXES: auto difference = std::midpoint(a, b); + + auto half_difference = a + (b - a) * 0.5f; + // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: use 'std::midpoint' instead of manual midpoint calculation + // CHECK-FIXES: auto half_difference = std::midpoint(a, b); + + auto half_difference_swapped = 0.5f * (b - a) + a; + // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: use 'std::midpoint' instead of manual midpoint calculation + // CHECK-FIXES: auto half_difference_swapped = std::midpoint(a, b); + + auto lerp_difference = a + (b - a) * t; + // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: use 'std::lerp' instead of manual linear interpolation + // CHECK-FIXES: auto lerp_difference = std::lerp(a, b, t); + + auto lerp_product_swapped = a + t * (b - a); + // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: use 'std::lerp' instead of manual linear interpolation + // CHECK-FIXES: auto lerp_product_swapped = std::lerp(a, b, t); + + auto lerp_addition_swapped = (b - a) * t + a; + // CHECK-MESSAGES: :[[@LINE-1]]:32: warning: use 'std::lerp' instead of manual linear interpolation + // CHECK-FIXES: auto lerp_addition_swapped = std::lerp(a, b, t); + + auto lerp_both_swapped = t * (b - a) + a; + // CHECK-MESSAGES: :[[@LINE-1]]:28: warning: use 'std::lerp' instead of manual linear interpolation + // CHECK-FIXES: auto lerp_both_swapped = std::lerp(a, b, t); + + auto lerp_weighted = (1 - t) * a + t * b; + // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: use 'std::lerp' instead of manual linear interpolation + // CHECK-FIXES: auto lerp_weighted = std::lerp(a, b, t); + + auto lerp_weighted_swapped = b * t + a * (1 - t); + // CHECK-MESSAGES: :[[@LINE-1]]:32: warning: use 'std::lerp' instead of manual linear interpolation + // CHECK-FIXES: auto lerp_weighted_swapped = std::lerp(a, b, t); + + auto lerp_weighted_products = a * (1 - t) + b * t; + // CHECK-MESSAGES: :[[@LINE-1]]:33: warning: use 'std::lerp' instead of manual linear interpolation + // CHECK-FIXES: auto lerp_weighted_products = std::lerp(a, b, t); + + auto lerp_weighted_reverse_sum = t * b + (1 - t) * a; + // CHECK-MESSAGES: :[[@LINE-1]]:36: warning: use 'std::lerp' instead of manual linear interpolation + // CHECK-FIXES: auto lerp_weighted_reverse_sum = std::lerp(a, b, t); +} + +void double_calculations(double a, double b, double t) { + auto sum = (a + b) / 2; + // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: use 'std::midpoint' instead of manual midpoint calculation + // CHECK-FIXES: auto sum = std::midpoint(a, b); + + auto floating_divisor = (a + b) / 2.0; + // CHECK-MESSAGES: :[[@LINE-1]]:27: warning: use 'std::midpoint' instead of manual midpoint calculation + // CHECK-FIXES: auto floating_divisor = std::midpoint(a, b); + + auto half = (a + b) * 0.5; + // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: use 'std::midpoint' instead of manual midpoint calculation + // CHECK-FIXES: auto half = std::midpoint(a, b); + + auto half_first = 0.5 * (a + b); + // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: use 'std::midpoint' instead of manual midpoint calculation + // CHECK-FIXES: auto half_first = std::midpoint(a, b); + + auto difference = a + (b - a) / 2; + // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: use 'std::midpoint' instead of manual midpoint calculation + // CHECK-FIXES: auto difference = std::midpoint(a, b); + + auto half_difference = a + (b - a) * 0.5; + // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: use 'std::midpoint' instead of manual midpoint calculation + // CHECK-FIXES: auto half_difference = std::midpoint(a, b); + + auto half_difference_swapped = 0.5 * (b - a) + a; + // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: use 'std::midpoint' instead of manual midpoint calculation + // CHECK-FIXES: auto half_difference_swapped = std::midpoint(a, b); + + auto lerp_difference = a + (b - a) * t; + // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: use 'std::lerp' instead of manual linear interpolation + // CHECK-FIXES: auto lerp_difference = std::lerp(a, b, t); + + auto lerp_product_swapped = a + t * (b - a); + // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: use 'std::lerp' instead of manual linear interpolation + // CHECK-FIXES: auto lerp_product_swapped = std::lerp(a, b, t); + + auto lerp_addition_swapped = (b - a) * t + a; + // CHECK-MESSAGES: :[[@LINE-1]]:32: warning: use 'std::lerp' instead of manual linear interpolation + // CHECK-FIXES: auto lerp_addition_swapped = std::lerp(a, b, t); + + auto lerp_both_swapped = t * (b - a) + a; + // CHECK-MESSAGES: :[[@LINE-1]]:28: warning: use 'std::lerp' instead of manual linear interpolation + // CHECK-FIXES: auto lerp_both_swapped = std::lerp(a, b, t); + + auto lerp_weighted = (1 - t) * a + t * b; + // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: use 'std::lerp' instead of manual linear interpolation + // CHECK-FIXES: auto lerp_weighted = std::lerp(a, b, t); + + auto lerp_weighted_swapped = b * t + a * (1 - t); + // CHECK-MESSAGES: :[[@LINE-1]]:32: warning: use 'std::lerp' instead of manual linear interpolation + // CHECK-FIXES: auto lerp_weighted_swapped = std::lerp(a, b, t); + + auto lerp_weighted_products = a * (1 - t) + b * t; + // CHECK-MESSAGES: :[[@LINE-1]]:33: warning: use 'std::lerp' instead of manual linear interpolation + // CHECK-FIXES: auto lerp_weighted_products = std::lerp(a, b, t); + + auto lerp_weighted_reverse_sum = t * b + (1 - t) * a; + // CHECK-MESSAGES: :[[@LINE-1]]:36: warning: use 'std::lerp' instead of manual linear interpolation + // CHECK-FIXES: auto lerp_weighted_reverse_sum = std::lerp(a, b, t); + + auto signed_zero = (-0.0 + 0.0) / 2; + // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: use 'std::midpoint' instead of manual midpoint calculation + // CHECK-FIXES: auto signed_zero = std::midpoint((-0.0), 0.0); + + auto subnormal_values = (1e-320 + 2e-320) / 2; + // CHECK-MESSAGES: :[[@LINE-1]]:27: warning: use 'std::midpoint' instead of manual midpoint calculation + // CHECK-FIXES: auto subnormal_values = std::midpoint(1e-320, 2e-320); +} + +void long_double_calculations(long double a, long double b, long double t) { + auto sum = (a + b) / 2; + // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: use 'std::midpoint' instead of manual midpoint calculation + // CHECK-FIXES: auto sum = std::midpoint(a, b); + + auto floating_divisor = (a + b) / 2.0L; + // CHECK-MESSAGES: :[[@LINE-1]]:27: warning: use 'std::midpoint' instead of manual midpoint calculation + // CHECK-FIXES: auto floating_divisor = std::midpoint(a, b); + + auto half = (a + b) * 0.5L; + // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: use 'std::midpoint' instead of manual midpoint calculation + // CHECK-FIXES: auto half = std::midpoint(a, b); + + auto half_first = 0.5L * (a + b); + // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: use 'std::midpoint' instead of manual midpoint calculation + // CHECK-FIXES: auto half_first = std::midpoint(a, b); + + auto difference = a + (b - a) / 2; + // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: use 'std::midpoint' instead of manual midpoint calculation + // CHECK-FIXES: auto difference = std::midpoint(a, b); + + auto half_difference = a + (b - a) * 0.5L; + // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: use 'std::midpoint' instead of manual midpoint calculation + // CHECK-FIXES: auto half_difference = std::midpoint(a, b); + + auto half_difference_swapped = 0.5L * (b - a) + a; + // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: use 'std::midpoint' instead of manual midpoint calculation + // CHECK-FIXES: auto half_difference_swapped = std::midpoint(a, b); + + auto lerp_difference = a + (b - a) * t; + // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: use 'std::lerp' instead of manual linear interpolation + // CHECK-FIXES: auto lerp_difference = std::lerp(a, b, t); + + auto lerp_product_swapped = a + t * (b - a); + // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: use 'std::lerp' instead of manual linear interpolation + // CHECK-FIXES: auto lerp_product_swapped = std::lerp(a, b, t); + + auto lerp_addition_swapped = (b - a) * t + a; + // CHECK-MESSAGES: :[[@LINE-1]]:32: warning: use 'std::lerp' instead of manual linear interpolation + // CHECK-FIXES: auto lerp_addition_swapped = std::lerp(a, b, t); + + auto lerp_both_swapped = t * (b - a) + a; + // CHECK-MESSAGES: :[[@LINE-1]]:28: warning: use 'std::lerp' instead of manual linear interpolation + // CHECK-FIXES: auto lerp_both_swapped = std::lerp(a, b, t); + + auto lerp_weighted = (1 - t) * a + t * b; + // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: use 'std::lerp' instead of manual linear interpolation + // CHECK-FIXES: auto lerp_weighted = std::lerp(a, b, t); + + auto lerp_weighted_swapped = b * t + a * (1 - t); + // CHECK-MESSAGES: :[[@LINE-1]]:32: warning: use 'std::lerp' instead of manual linear interpolation + // CHECK-FIXES: auto lerp_weighted_swapped = std::lerp(a, b, t); + + auto lerp_weighted_products = a * (1 - t) + b * t; + // CHECK-MESSAGES: :[[@LINE-1]]:33: warning: use 'std::lerp' instead of manual linear interpolation + // CHECK-FIXES: auto lerp_weighted_products = std::lerp(a, b, t); + + auto lerp_weighted_reverse_sum = t * b + (1 - t) * a; + // CHECK-MESSAGES: :[[@LINE-1]]:36: warning: use 'std::lerp' instead of manual linear interpolation + // CHECK-FIXES: auto lerp_weighted_reverse_sum = std::lerp(a, b, t); +} +using Real = double; +typedef int Integer; + +auto aliases(const Real &a, Real &b, const Real &t) { + return a + (b - a) * t; + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use 'std::lerp' instead of manual linear interpolation + // CHECK-FIXES: return std::lerp(a, b, t); +} + +auto typedef_midpoint(const Integer &a, Integer &&b) { + return a + (b - a) / 2; + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use 'std::midpoint' instead of manual midpoint calculation + // CHECK-FIXES: return std::midpoint(a, b); +} + +auto explicit_casts(int a, int b) { + return (static_cast<double>(a) + static_cast<double>(b)) / 2; + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use 'std::midpoint' instead of manual midpoint calculation + // CHECK-FIXES: return std::midpoint(static_cast<double>(a), static_cast<double>(b)); +} + +constexpr int constexpr_midpoint(int a, int b) { + return a + (b - a) / 2; + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use 'std::midpoint' instead of manual midpoint calculation + // CHECK-FIXES: return std::midpoint(a, b); +} + +struct Base { double value; }; +struct Derived : Base {}; + +auto inherited_members(const Base &a, const Derived &b, double t) { + return a.value + (b.value - a.value) * t; + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use 'std::lerp' instead of manual linear interpolation + // CHECK-FIXES: return std::lerp(a.value, b.value, t); +} + +auto array_elements(double *values, double t) { + return values[0] + (values[1] - values[0]) * t; + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use 'std::lerp' instead of manual linear interpolation + // CHECK-FIXES: return std::lerp(values[0], values[1], t); +} + +namespace geometry { +using Scalar = double; + +auto namespaced(Scalar a, Scalar b, Scalar t) { + return a + (b - a) * t; + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use 'std::lerp' instead of manual linear interpolation + // CHECK-FIXES: return std::lerp(a, b, t); +} + +} // namespace geometry + +template <class Tag> + +auto nondependent_template(int a, int b) { + return (a + b) / 2; + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use 'std::midpoint' instead of manual midpoint calculation + // CHECK-FIXES: return std::midpoint(a, b); +} + +template auto nondependent_template<int>(int, int); +template auto nondependent_template<double>(int, int); + +template <class T> auto specialized(T a, T b) { return a + (b - a) / 2; } +template <> +auto specialized<double>(double a, double b) { + return a + (b - a) / 2; + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use 'std::midpoint' instead of manual midpoint calculation + // CHECK-FIXES: return std::midpoint(a, b); +} + +auto captured_lambda(int a, int b) { + return [=] { + return (a + b) / 2; + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use 'std::midpoint' instead of manual midpoint calculation + // CHECK-FIXES: return std::midpoint(a, b); + }; +} + +auto lambda_parameters() { + return [](double a, double b, double t) { + return a + (b - a) * t; + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use 'std::lerp' instead of manual linear interpolation + // CHECK-FIXES: return std::lerp(a, b, t); + }; +} + +auto nested_calculations(int a, int b, int c) { + return ((a + b) / 2 + c) / 2; + // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: use 'std::midpoint' instead of manual midpoint calculation + // CHECK-FIXES: return (std::midpoint(a, b) + c) / 2; +} + +double read_value(); +struct Convertible { operator double() const; }; +struct Number { + Number operator+(Number) const; + Number operator-(Number) const; + Number operator*(double) const; + Number operator/(int) const; +}; +enum Enumeration { First, Second }; + +void unrecognized_formulas(int i, int j, double a, double b, double t) { + (void)((i + j) / 3); + (void)((i - j) / 2); + (void)(i + (j - i) / 3); + (void)((1 - t) * a + (t + 1) * b); + (void)((2 - t) * a + t * b); + (void)(a + (b - t) * t); + (void)((a + b) * 0.25); +} + +void mixed_midpoint_types(int i, int j, unsigned u, double a, float f, float g) { + (void)(i + (j - u) / 2); + (void)((i + u) / 2); + (void)((f + g) / 2.0); + (void)((i + j) / 2.0); + (void)((a + i) / 2); +} + +void promoted_or_unsupported_types(short a, short b, char character, + bool flag, Enumeration value) { + (void)((a + b) / 2); + (void)((character + character) / 2); + (void)((flag + flag) / 2); + (void)((value + value) / 2); +} + +void mixed_interpolation_types(int i, int j, double a, double b, double t, + float f, float g) { + (void)(i + (j - i) * t); + (void)(a + (b - a) * i); + (void)(f + (g - f) * t); + (void)(a + (b - a) * 0.25f); +} + +void overloaded_arithmetic(Number a, Number b, double t) { + (void)((a + b) / 2); + (void)(a + (b - a) * t); +} + +void user_defined_conversion(Convertible a, double b) { + (void)((a + b) / 2); +} + +void side_effects(double a, double b, double t) { + (void)((read_value() + b) / 2); + (void)(read_value() + (b - read_value()) * t); + (void)((1 - read_value()) * a + read_value() * b); + (void)((a++ + b) / 2); + (void)((++a + b) / 2); + (void)(a + (b - a) * (t = 0.5)); +} + +void volatile_reads(volatile double &a, double b, double t) { + (void)((a + b) / 2); + (void)(a + (b - a) * t); + (void)(b + (t - b) * a); +} + +void unevaluated_contexts(double a, double b, double t) { + (void)sizeof((a + b) / 2); + (void)noexcept(a + (b - a) * t); + using Result = decltype((a + b) / 2); + (void)requires { (a + b) / 2; a + (b - a) * t; }; +} + +void named_constants(int a, int b) { + constexpr int Divisor = 2; + (void)((a + b) / Divisor); +} + +template <class T> auto dependent(T a, T b, T t) { + return a + (b - a) * t; +} +template auto dependent(double, double, double); +template auto dependent(float, float, float); +template <class T> auto forwarding(T &&a, T &&b) { + return a + (b - a) / 2; +} +template <class... Ts> auto variadic(Ts... values) { + return (((values + values) / 2) + ...); +} +template <class T> concept Interpolatable = requires(T a, T b, T t) { + a + (b - a) * t; +}; +auto generic_lambda() { + return [](auto a, auto b) { return (a + b) / 2; }; +} + +#define MIDPOINT(a, b) (((a) + (b)) / 2) +#define TWO 2 +#define FIRST a +#define ADD + +#define IDENTITY(value) (value) +void macro_cases(int a, int b) { + (void)MIDPOINT(a, b); + (void)((a + b) / TWO); + (void)((FIRST + b) / 2); + (void)((a ADD b) / 2); + (void)IDENTITY((a + b) / 2); +} + +namespace associated { +struct Number {}; +Number operator+(Number, Number); +Number operator/(Number, int); +void adl(Number a, Number b) { (void)((a + b) / 2); } +} // namespace associated + +int intentionally_truncated(int a, int b) { + return (a + b) / 2; // NOLINT(modernize-use-std-interpolation) +} + +auto comma_operand(int a, int b, int c) { + return ((static_cast<void>(a), b) + c) / 2; + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use 'std::midpoint' instead of manual midpoint calculation + // CHECK-FIXES: return std::midpoint((static_cast<void>(a), b), c); +} + +auto conditional_operand(bool choose, int a, int b, int c) { + return ((choose ? a : b) + c) / 2; + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use 'std::midpoint' instead of manual midpoint calculation + // CHECK-FIXES: return std::midpoint((choose ? a : b), c); +} + +auto unmatched_outer(int i, int j) { + return i + (j + i) / 2; + // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: use 'std::midpoint' instead of manual midpoint calculation + // CHECK-FIXES: return i + std::midpoint(j, i); +} + +template auto forwarding<int &>(int &, int &); +template auto variadic(int, int); + +auto instantiate_generic_lambda() { + return generic_lambda()(1, 2); +} + +// These operations are side-effect-free, so their exclusions must work +// independently of the side-effect guard. +struct PureConvertible { + [[gnu::const]] operator double() const; +}; +struct PureArithmetic { + [[gnu::const]] double operator+(double) const; +}; +void pure_user_defined_operations(PureConvertible converted, + PureArithmetic overloaded, + double a, double b) { + (void)((converted + b) / 2); + (void)((static_cast<double>(converted) + b) / 2); + (void)((overloaded + a + b) / 2); +} + +using geometry::Scalar; +auto using_declaration(Scalar a, Scalar b, Scalar t) { + return a + (b - a) * t; + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use 'std::lerp' instead of manual linear interpolation + // CHECK-FIXES: return std::lerp(a, b, t); +} + +template <Interpolatable T> +auto constrained_concrete(T, int a, int b) { + return (a + b) / 2; + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use 'std::midpoint' instead of manual midpoint calculation + // CHECK-FIXES: return std::midpoint(a, b); +} +template auto constrained_concrete(double, int, int); + +void consume(int); +void consume(short); +void promotion_overload(short a, short b) { + consume((a + b) / 2); +} + +#endif // __cplusplus >= 202002L >From 1ae820ac88cf8f4c4f833943f2c59596aae745f3 Mon Sep 17 00:00:00 2001 From: Daniil Dudkin <[email protected]> Date: Fri, 2 Oct 2026 00:45:02 +0300 Subject: [PATCH 2/3] [clang-tidy] Fix use-std-interpolation matching of long double literals --- .../modernize/UseStdInterpolationCheck.cpp | 10 +++++++--- .../checkers/modernize/use-std-interpolation.cpp | 15 +++++++++++++++ 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/clang-tools-extra/clang-tidy/modernize/UseStdInterpolationCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStdInterpolationCheck.cpp index 5febc5835b8ff..b5fe4c5905486 100644 --- a/clang-tools-extra/clang-tidy/modernize/UseStdInterpolationCheck.cpp +++ b/clang-tools-extra/clang-tidy/modernize/UseStdInterpolationCheck.cpp @@ -23,6 +23,10 @@ AST_MATCHER(Expr, hasSideEffects) { return Node.HasSideEffects(Finder->getASTContext()); } +AST_MATCHER_P(FloatingLiteral, hasExactValue, double, Value) { + return Node.getValue().isExactlyValue(Value); +} + AST_MATCHER(Expr, isMacroExpanded) { if (Node.getBeginLoc().isMacroID() || Node.getEndLoc().isMacroID()) return true; @@ -72,10 +76,10 @@ static InterpolationMatchers makeInterpolationMatchers() { const auto RepeatedFactor = ignoringParenImpCasts( expr(matchers::isStatementIdenticalToBoundNode("factor"))); const auto Two = ignoringParenImpCasts( - expr(anyOf(integerLiteral(equals(2)), floatLiteral(equals(2.0))))); - const auto Half = ignoringParenImpCasts(floatLiteral(equals(0.5))); + expr(anyOf(integerLiteral(equals(2)), floatLiteral(hasExactValue(2.0))))); + const auto Half = ignoringParenImpCasts(floatLiteral(hasExactValue(0.5))); const auto One = ignoringParenImpCasts( - expr(anyOf(integerLiteral(equals(1)), floatLiteral(equals(1.0))))); + expr(anyOf(integerLiteral(equals(1)), floatLiteral(hasExactValue(1.0))))); // Midpoints: (a + b) / 2 and a + (b - a) / 2, also using * 0.5. const auto Sum = ignoringParenImpCasts(binaryOperator( diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-interpolation.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-interpolation.cpp index 43e65ac3e2fc7..a97dcfd91b594 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-interpolation.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-interpolation.cpp @@ -2,6 +2,10 @@ // RUN: -- -format-style="{BasedOnStyle: LLVM, IncludeBlocks: Regroup}" // RUN: %check_clang_tidy -std=c++20-or-later -check-suffixes=,PRESENT %s modernize-use-std-interpolation %t-present \ // RUN: -- -- -DINCLUDES_PRESENT +// RUN: %check_clang_tidy -std=c++20-or-later -check-suffixes=,MISSING %s modernize-use-std-interpolation %t-x86 \ +// RUN: -- -format-style="{BasedOnStyle: LLVM, IncludeBlocks: Regroup}" -- -target x86_64-unknown-linux-gnu +// RUN: %check_clang_tidy -std=c++20-or-later -check-suffixes=,MISSING %s modernize-use-std-interpolation %t-aarch64 \ +// RUN: -- -format-style="{BasedOnStyle: LLVM, IncludeBlocks: Regroup}" -- -target aarch64-unknown-linux-gnu // RUN: clang-tidy %s -checks=-*,modernize-use-std-interpolation -allow-no-checks -- -std=c++11 2>&1 | count 0 // RUN: clang-tidy %s -checks=-*,modernize-use-std-interpolation -allow-no-checks -- -std=c++14 2>&1 | count 0 // RUN: clang-tidy %s -checks=-*,modernize-use-std-interpolation -allow-no-checks -- -std=c++17 2>&1 | count 0 @@ -287,7 +291,18 @@ void long_double_calculations(long double a, long double b, long double t) { auto lerp_weighted_reverse_sum = t * b + (1 - t) * a; // CHECK-MESSAGES: :[[@LINE-1]]:36: warning: use 'std::lerp' instead of manual linear interpolation // CHECK-FIXES: auto lerp_weighted_reverse_sum = std::lerp(a, b, t); + + auto floating_complement = (1.0L - t) * a + t * b; + // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: use 'std::lerp' instead of manual linear interpolation + // CHECK-FIXES: auto floating_complement = std::lerp(a, b, t); +} + +void long_double_near_misses(long double a, long double b, long double t) { + (void)((a + b) / 2.01L); + (void)((a + b) * 0.51L); + (void)((1.01L - t) * a + t * b); } + using Real = double; typedef int Integer; >From 81fcbd9375c04d28629ad1f5fcd6a7e205b253f0 Mon Sep 17 00:00:00 2001 From: Daniil Dudkin <[email protected]> Date: Fri, 2 Oct 2026 00:47:36 +0300 Subject: [PATCH 3/3] [clang-tidy] Avoid use-std-interpolation false positives for additive chains --- .../modernize/UseStdInterpolationCheck.cpp | 7 ++- .../checks/modernize/use-std-interpolation.md | 4 ++ .../modernize/use-std-interpolation.cpp | 63 ++++++++++++++++++- 3 files changed, 72 insertions(+), 2 deletions(-) diff --git a/clang-tools-extra/clang-tidy/modernize/UseStdInterpolationCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStdInterpolationCheck.cpp index b5fe4c5905486..041c65960200e 100644 --- a/clang-tools-extra/clang-tidy/modernize/UseStdInterpolationCheck.cpp +++ b/clang-tools-extra/clang-tidy/modernize/UseStdInterpolationCheck.cpp @@ -82,8 +82,13 @@ static InterpolationMatchers makeInterpolationMatchers() { expr(anyOf(integerLiteral(equals(1)), floatLiteral(hasExactValue(1.0))))); // Midpoints: (a + b) / 2 and a + (b - a) / 2, also using * 0.5. + // Exclude ungrouped additive chains, which may express rounded averages + // rather than an intended two-endpoint midpoint. + const auto GroupedEndpoint = + unless(ignoringImpCasts(binaryOperator(hasAnyOperatorName("+", "-")))); const auto Sum = ignoringParenImpCasts(binaryOperator( - hasOperatorName("+"), SameType, hasLHS(Start), hasRHS(End))); + hasOperatorName("+"), SameType, hasLHS(expr(GroupedEndpoint, Start)), + hasRHS(expr(GroupedEndpoint, End)))); const auto Difference = ignoringParenImpCasts(binaryOperator( hasOperatorName("-"), SameType, hasLHS(End), hasRHS(RepeatedStart))); const auto HalfSum = binaryOperator( diff --git a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-std-interpolation.md b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-std-interpolation.md index f639e64dbaae7..09ae89554d1d3 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-std-interpolation.md +++ b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-std-interpolation.md @@ -47,6 +47,10 @@ The following expressions are recognized: | `a + (b - a) * t` | `std::lerp(a, b, t)` | | `(1 - t) * a + t * b` | `std::lerp(a, b, t)` | +Sum midpoint formulas must have two endpoints. Ungrouped addition or subtraction +chains such as `(a + b + 1) / 2` are excluded. Parenthesized endpoints such as +`((a + b) + c) / 2` are supported. + ## Changes in numerical behavior Replacing integer `(a + b) / 2` can change rounding. Integer division truncates diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-interpolation.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-interpolation.cpp index a97dcfd91b594..08cd9e4a0aa0b 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-interpolation.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-interpolation.cpp @@ -52,7 +52,7 @@ void int_calculations(int a, int b) { // CHECK-MESSAGES: :[[@LINE-1]]:35: warning: use 'std::midpoint' instead of manual midpoint calculation // CHECK-FIXES: auto parenthesized_difference = std::midpoint(a, b); - auto signed_boundaries = (-2147483647 - 1 + 2147483647) / 2; + auto signed_boundaries = ((-2147483647 - 1) + 2147483647) / 2; // CHECK-MESSAGES: :[[@LINE-1]]:28: warning: use 'std::midpoint' instead of manual midpoint calculation // CHECK-FIXES: auto signed_boundaries = std::midpoint((-2147483647 - 1), 2147483647); @@ -397,6 +397,34 @@ auto nested_calculations(int a, int b, int c) { // CHECK-FIXES: return (std::midpoint(a, b) + c) / 2; } +void grouped_endpoints(int a, int b, int c) { + auto left_sum = ((a + b) + c) / 2; + // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: use 'std::midpoint' instead of manual midpoint calculation + // CHECK-FIXES: auto left_sum = std::midpoint((a + b), c); + + auto right_sum = (a + (b + c)) / 2; + // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: use 'std::midpoint' instead of manual midpoint calculation + // CHECK-FIXES: auto right_sum = std::midpoint(a, (b + c)); + + auto left_difference = ((a - b) + c) / 2; + // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: use 'std::midpoint' instead of manual midpoint calculation + // CHECK-FIXES: auto left_difference = std::midpoint((a - b), c); + + auto right_difference = (a + (b - c)) / 2; + // CHECK-MESSAGES: :[[@LINE-1]]:27: warning: use 'std::midpoint' instead of manual midpoint calculation + // CHECK-FIXES: auto right_difference = std::midpoint(a, (b - c)); +} + +void floating_grouped_endpoints(double a, double b, double c) { + auto sum = ((a + b) + c) * 0.5; + // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: use 'std::midpoint' instead of manual midpoint calculation + // CHECK-FIXES: auto sum = std::midpoint((a + b), c); + + auto reversed_product = 0.5 * (a + (b - c)); + // CHECK-MESSAGES: :[[@LINE-1]]:27: warning: use 'std::midpoint' instead of manual midpoint calculation + // CHECK-FIXES: auto reversed_product = std::midpoint(a, (b - c)); +} + double read_value(); struct Convertible { operator double() const; }; struct Number { @@ -417,6 +445,39 @@ void unrecognized_formulas(int i, int j, double a, double b, double t) { (void)((a + b) * 0.25); } +void additive_chains(int foldLen, int sinceLast, int other) { + (void)((foldLen + sinceLast + 1) / 2); + (void)((1 + foldLen + sinceLast) / 2); + (void)((foldLen + sinceLast + other) / 2); + (void)((foldLen - sinceLast + 1) / 2); + (void)((foldLen + sinceLast - other + 1) / 2); + (void)((((foldLen + sinceLast + 1))) / 2); +} + +void unsigned_additive_chains(unsigned a, unsigned b) { + (void)((a + b + 1U) / 2U); + (void)((a - b + 1U) / 2U); +} + +void floating_additive_chains(double a, double b, double c) { + (void)((a + b + 1.0) / 2.0); + (void)((a + b + c) * 0.5); + (void)(0.5 * (a + b + c)); + (void)((a - b + c) * 0.5); +} + +void long_double_additive_chains(long double a, long double b) { + (void)((a + b + 1.0L) / 2.0L); + (void)((a + b + 1.0L) * 0.5L); +} + +template <class T> int template_additive_chain(int a, int b) { + return (a + b + 1) / 2; +} +template int template_additive_chain<void>(int, int); + +auto additive_chain_lambda = [](int a, int b) { return (a + b + 1) / 2; }; + void mixed_midpoint_types(int i, int j, unsigned u, double a, float f, float g) { (void)(i + (j - u) / 2); (void)((i + u) / 2); _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
