Skip to content

[clang-tidy] use correct template type in std::min and std::max when operand is integer literal for readability-use-std-min-max #122296

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Conversation

HerrCai0907
Copy link
Contributor

@HerrCai0907 HerrCai0907 commented Jan 9, 2025

When comparing with integer literal, integer promote will happen to promote type which has less bit width than int to int or unsigned int.
It will let auto-fix provide correct but out of expected fix.

e.g.

short a;
if ( a > 10 )
  a = 10;

will be

short a;
if ( (int)a > 10 )
  a = (short)10;

which will be fixed as

short a;
a = std::max<int>(a, 10);

but actually it can be

short a;
a = std::max<short>(a, 10);

Fixed: #121676

Copy link
Contributor Author

HerrCai0907 commented Jan 9, 2025

@HerrCai0907 HerrCai0907 marked this pull request as ready for review January 9, 2025 15:21
@llvmbot
Copy link
Member

llvmbot commented Jan 9, 2025

@llvm/pr-subscribers-clang-tools-extra

@llvm/pr-subscribers-clang-tidy

Author: Congcong Cai (HerrCai0907)

Changes

When comparing with integer literal, integer promote will happen to promote type which has less bit width than int to int or unsigned int.
It will let auto-fix provide correct but out of expected fix.

e.g.

short a;
if ( a &gt; 10 )
  a = 10;

will be

short a;
if ( (int)a &gt; 10 )
  a = (short)10;

which will be fixed as

short a;
a = std::max&lt;int&gt;(a, 10);

but actually it can be

short a;
a = std::max&lt;short&gt;(a, 10);

Full diff: https://github.com/llvm/llvm-project/pull/122296.diff

3 Files Affected:

  • (modified) clang-tools-extra/clang-tidy/readability/UseStdMinMaxCheck.cpp (+23-12)
  • (modified) clang-tools-extra/docs/ReleaseNotes.rst (+4)
  • (modified) clang-tools-extra/test/clang-tidy/checkers/readability/use-std-min-max.cpp (+21)
diff --git a/clang-tools-extra/clang-tidy/readability/UseStdMinMaxCheck.cpp b/clang-tools-extra/clang-tidy/readability/UseStdMinMaxCheck.cpp
index 179173502a8d01..6f6b8a853a91e0 100644
--- a/clang-tools-extra/clang-tidy/readability/UseStdMinMaxCheck.cpp
+++ b/clang-tools-extra/clang-tidy/readability/UseStdMinMaxCheck.cpp
@@ -79,6 +79,27 @@ static QualType getNonTemplateAlias(QualType QT) {
   return QT;
 }
 
+static QualType getReplacementCastType(const Expr *CondLhs, const Expr *CondRhs,
+                                       QualType ComparedType) {
+  QualType LhsType = CondLhs->getType();
+  QualType RhsType = CondRhs->getType();
+  QualType LhsCanonicalType =
+      LhsType.getCanonicalType().getNonReferenceType().getUnqualifiedType();
+  QualType RhsCanonicalType =
+      RhsType.getCanonicalType().getNonReferenceType().getUnqualifiedType();
+  QualType GlobalImplicitCastType;
+  if (LhsCanonicalType != RhsCanonicalType) {
+    if (llvm::isa<IntegerLiteral>(CondRhs)) {
+      GlobalImplicitCastType = getNonTemplateAlias(LhsType);
+    } else if (llvm::isa<IntegerLiteral>(CondLhs)) {
+      GlobalImplicitCastType = getNonTemplateAlias(RhsType);
+    } else {
+      GlobalImplicitCastType = getNonTemplateAlias(ComparedType);
+    }
+  }
+  return GlobalImplicitCastType;
+}
+
 static std::string createReplacement(const Expr *CondLhs, const Expr *CondRhs,
                                      const Expr *AssignLhs,
                                      const SourceManager &Source,
@@ -92,18 +113,8 @@ static std::string createReplacement(const Expr *CondLhs, const Expr *CondRhs,
   const llvm::StringRef AssignLhsStr = Lexer::getSourceText(
       Source.getExpansionRange(AssignLhs->getSourceRange()), Source, LO);
 
-  QualType GlobalImplicitCastType;
-  QualType LhsType = CondLhs->getType()
-                         .getCanonicalType()
-                         .getNonReferenceType()
-                         .getUnqualifiedType();
-  QualType RhsType = CondRhs->getType()
-                         .getCanonicalType()
-                         .getNonReferenceType()
-                         .getUnqualifiedType();
-  if (LhsType != RhsType) {
-    GlobalImplicitCastType = getNonTemplateAlias(BO->getLHS()->getType());
-  }
+  QualType GlobalImplicitCastType =
+      getReplacementCastType(CondLhs, CondRhs, BO->getLHS()->getType());
 
   return (AssignLhsStr + " = " + FunctionName +
           (!GlobalImplicitCastType.isNull()
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 94e15639c4a92e..fd523da8dc5a1b 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -364,6 +364,10 @@ Changes in existing checks
   <clang-tidy/checks/readability/identifier-naming>` check to
   validate ``namespace`` aliases.
 
+- Improved :doc:`readability-use-std-min-max
+  <clang-tidy/checks/readability/use-std-min-max>` check to use correct template
+  type in ``std::min`` and ``std::max`` when operand is integer literal.
+
 Removed checks
 ^^^^^^^^^^^^^^
 
diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/use-std-min-max.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/use-std-min-max.cpp
index 9c0e2eabda348d..35ade8a7c6d37e 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/readability/use-std-min-max.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/readability/use-std-min-max.cpp
@@ -252,3 +252,24 @@ void testVectorSizeType() {
   if (value < v.size())
     value = v.size();
 }
+
+namespace gh121676 {
+
+void useLeft() {
+  using U16 = unsigned short;
+  U16 I = 0;
+  // CHECK-MESSAGES: :[[@LINE+2]]:3: warning: use `std::max` instead of `<` [readability-use-std-min-max]
+  // CHECK-FIXES: I = std::max<U16>(I, 16U);
+  if (I < 16U)
+    I = 16U;
+}
+void useRight() {
+  using U16 = unsigned short;
+  U16 I = 0;
+  // CHECK-MESSAGES: :[[@LINE+2]]:3: warning: use `std::min` instead of `<` [readability-use-std-min-max]
+  // CHECK-FIXES: I = std::min<U16>(16U, I);
+  if (16U < I)
+    I = 16U;
+}
+
+} // namespace gh121676

@HerrCai0907 HerrCai0907 force-pushed the users/ccc01-09-_clang-tidy_nfc_clean_readability-use-std-min-max branch from e123790 to 2a09266 Compare January 9, 2025 15:28
@HerrCai0907 HerrCai0907 force-pushed the users/ccc01-09-_clang-tidy_use_correct_template_type_in_std_min_and_std_max_when_operand_is_integer_literal_for_readability-use-std-min-max branch from 7958e40 to 0f90ae9 Compare January 9, 2025 15:29
Base automatically changed from users/ccc01-09-_clang-tidy_nfc_clean_readability-use-std-min-max to main January 9, 2025 23:35
…`` when operand is integer literal for readability-use-std-min-max
@HerrCai0907 HerrCai0907 force-pushed the users/ccc01-09-_clang-tidy_use_correct_template_type_in_std_min_and_std_max_when_operand_is_integer_literal_for_readability-use-std-min-max branch from 0f90ae9 to 2973b21 Compare January 9, 2025 23:37
Copy link
Member

@PiotrZSL PiotrZSL left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM.

@HerrCai0907 HerrCai0907 merged commit 32bcd41 into main Jan 11, 2025
9 checks passed
@HerrCai0907 HerrCai0907 deleted the users/ccc01-09-_clang-tidy_use_correct_template_type_in_std_min_and_std_max_when_operand_is_integer_literal_for_readability-use-std-min-max branch January 11, 2025 10:48
BaiXilin pushed a commit to BaiXilin/llvm-fix-vnni-instr-types that referenced this pull request Jan 12, 2025
…`` when operand is integer literal for readability-use-std-min-max (llvm#122296)

When comparing with integer literal, integer promote will happen to
promote type which has less bit width than int to int or unsigned int.
It will let auto-fix provide correct but out of expected fix.

e.g.
```c++
short a;
if ( a > 10 )
  a = 10;
```
will be
```c++
short a;
if ( (int)a > 10 )
  a = (short)10;
```

which will be fixed as
```c++
short a;
a = std::max<int>(a, 10);
```

but actually it can be
```c++
short a;
a = std::max<short>(a, 10);
```

Fixed: llvm#121676
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[clang-tidy] readability-use-std-min-max changes the type of the resulting expression
3 participants