[clang-tools-extra] [clang-tidy] Add `modernize-use-std-interpolation` check (PR #228104)
Daniil Dudkin via cfe-commits
cfe-commits at lists.llvm.org
Thu Oct 1 14:53:49 PDT 2026
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 <unterumarmung at yandex.ru>
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 <unterumarmung at yandex.ru>
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 9b77e90e0c6ce13e06d9da6aeacee6244d2b9338 Mon Sep 17 00:00:00 2001
From: Daniil Dudkin <unterumarmung at yandex.ru>
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..b30e3b39e73b4 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.
+ // Preserve parentheses around endpoints so that an ungrouped chain such as
+ // (a + b + 1) / 2 is not treated as the midpoint of a + b and 1.
+ 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);
More information about the cfe-commits
mailing list