Skip to content

Commit 1883d98

Browse files
committed
Add readability check to suggest replacement of conditional statement with std::min/std::max
Signed-off-by: 11happy <[email protected]>
1 parent dc97457 commit 1883d98

File tree

8 files changed

+183
-0
lines changed

8 files changed

+183
-0
lines changed

clang-tools-extra/clang-tidy/readability/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ add_clang_library(clangTidyReadabilityModule
88
AvoidReturnWithVoidValueCheck.cpp
99
AvoidUnconditionalPreprocessorIfCheck.cpp
1010
BracesAroundStatementsCheck.cpp
11+
ConditionaltostdminmaxCheck.cpp
1112
ConstReturnTypeCheck.cpp
1213
ContainerContainsCheck.cpp
1314
ContainerDataPointerCheck.cpp
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
//===--- ConditionaltostdminmaxCheck.cpp - clang-tidy ---------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "ConditionaltostdminmaxCheck.h"
10+
#include "clang/AST/ASTContext.h"
11+
#include "clang/ASTMatchers/ASTMatchFinder.h"
12+
13+
using namespace clang::ast_matchers;
14+
15+
namespace clang::tidy::readability {
16+
17+
void ConditionaltostdminmaxCheck::registerMatchers(MatchFinder *Finder) {
18+
Finder->addMatcher(
19+
ifStmt(
20+
has(
21+
binaryOperator(
22+
anyOf(hasOperatorName("<"),hasOperatorName(">")),
23+
hasLHS(ignoringImpCasts(declRefExpr().bind("lhsVar1"))),
24+
hasRHS(ignoringImpCasts(declRefExpr().bind("rhsVar1")))
25+
)
26+
)
27+
,
28+
hasThen(
29+
stmt(
30+
binaryOperator(
31+
hasOperatorName("="),
32+
hasLHS(ignoringImpCasts(declRefExpr().bind("lhsVar2"))),
33+
hasRHS(ignoringImpCasts(declRefExpr().bind("rhsVar2")))
34+
)
35+
)
36+
)
37+
).bind("ifStmt"),this);
38+
39+
40+
41+
}
42+
43+
void ConditionaltostdminmaxCheck::check(const MatchFinder::MatchResult &Result) {
44+
const DeclRefExpr *lhsVar1 = Result.Nodes.getNodeAs<DeclRefExpr>("lhsVar1");
45+
const DeclRefExpr *rhsVar1 = Result.Nodes.getNodeAs<DeclRefExpr>("rhsVar1");
46+
const DeclRefExpr *lhsVar2 = Result.Nodes.getNodeAs<DeclRefExpr>("lhsVar2");
47+
const DeclRefExpr *rhsVar2 = Result.Nodes.getNodeAs<DeclRefExpr>("rhsVar2");
48+
const IfStmt *ifStmt = Result.Nodes.getNodeAs<IfStmt>("ifStmt");
49+
50+
if (!lhsVar1 || !rhsVar1 || !lhsVar2 || !rhsVar2 || !ifStmt)
51+
return;
52+
53+
const BinaryOperator *binaryOp = dyn_cast<BinaryOperator>(ifStmt->getCond());
54+
if (!binaryOp)
55+
return;
56+
57+
SourceLocation ifLocation = ifStmt->getIfLoc();
58+
SourceLocation thenLocation = ifStmt->getEndLoc();
59+
60+
if(binaryOp->getOpcode() == BO_LT){
61+
if(lhsVar1->getDecl() == lhsVar2->getDecl() && rhsVar1->getDecl() == rhsVar2->getDecl()){
62+
diag(ifStmt->getIfLoc(), "use std::max instead of <")<<FixItHint::CreateReplacement(SourceRange(ifLocation, thenLocation),
63+
lhsVar2->getNameInfo().getAsString() + " = std::max(" + lhsVar1->getNameInfo().getAsString() + ", " +
64+
rhsVar1->getNameInfo().getAsString() + ")");
65+
}
66+
else if(lhsVar1->getDecl() == rhsVar2->getDecl() && rhsVar1->getDecl() == lhsVar2->getDecl()){
67+
diag(ifStmt->getIfLoc(), "use std::min instead of <")<<FixItHint::CreateReplacement(SourceRange(ifLocation, thenLocation),
68+
lhsVar2->getNameInfo().getAsString() + " = std::min(" + lhsVar1->getNameInfo().getAsString() + ", " +
69+
rhsVar1->getNameInfo().getAsString() + ")");
70+
}
71+
}
72+
else if(binaryOp->getOpcode() == BO_GT){
73+
if(lhsVar1->getDecl() == lhsVar2->getDecl() && rhsVar1->getDecl() == rhsVar2->getDecl()){
74+
diag(ifStmt->getIfLoc(), "use std::min instead of >")<<FixItHint::CreateReplacement(SourceRange(ifLocation, thenLocation),
75+
lhsVar2->getNameInfo().getAsString() + " = std::min(" + lhsVar1->getNameInfo().getAsString() + ", " +
76+
rhsVar1->getNameInfo().getAsString() + ")");
77+
}
78+
else if(lhsVar1->getDecl() == rhsVar2->getDecl() && rhsVar1->getDecl() == lhsVar2->getDecl()){
79+
diag(ifStmt->getIfLoc(), "use std::max instead of >")<<FixItHint::CreateReplacement(SourceRange(ifLocation, thenLocation),
80+
lhsVar2->getNameInfo().getAsString() + " = std::max(" + lhsVar1->getNameInfo().getAsString() + ", " +
81+
rhsVar1->getNameInfo().getAsString() + ")");
82+
}
83+
}
84+
}
85+
86+
} // namespace clang::tidy::readability
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
//===--- ConditionaltostdminmaxCheck.h - clang-tidy -------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_CONDITIONALTOSTDMINMAXCHECK_H
10+
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_CONDITIONALTOSTDMINMAXCHECK_H
11+
12+
#include "../ClangTidyCheck.h"
13+
14+
namespace clang::tidy::readability {
15+
16+
/// FIXME: replaces certain conditional statements with equivalent std::min or std::max expressions, improving readability and promoting the use of standard library functions."
17+
///
18+
/// For the user-facing documentation see:
19+
/// http://clang.llvm.org/extra/clang-tidy/checks/readability/ConditionalToStdMinMax.html
20+
class ConditionaltostdminmaxCheck : public ClangTidyCheck {
21+
public:
22+
ConditionaltostdminmaxCheck(StringRef Name, ClangTidyContext *Context)
23+
: ClangTidyCheck(Name, Context) {}
24+
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
25+
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
26+
};
27+
28+
} // namespace clang::tidy::readability
29+
30+
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_CONDITIONALTOSTDMINMAXCHECK_H

clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "AvoidReturnWithVoidValueCheck.h"
1414
#include "AvoidUnconditionalPreprocessorIfCheck.h"
1515
#include "BracesAroundStatementsCheck.h"
16+
#include "ConditionaltostdminmaxCheck.h"
1617
#include "ConstReturnTypeCheck.h"
1718
#include "ContainerContainsCheck.h"
1819
#include "ContainerDataPointerCheck.h"
@@ -62,6 +63,8 @@ namespace readability {
6263
class ReadabilityModule : public ClangTidyModule {
6364
public:
6465
void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
66+
CheckFactories.registerCheck<ConditionaltostdminmaxCheck>(
67+
"readability-ConditionalToStdMinMax");
6568
CheckFactories.registerCheck<AvoidConstParamsInDecls>(
6669
"readability-avoid-const-params-in-decls");
6770
CheckFactories.registerCheck<AvoidReturnWithVoidValueCheck>(

clang-tools-extra/docs/ReleaseNotes.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,12 @@ New checks
224224
Recommends the smallest possible underlying type for an ``enum`` or ``enum``
225225
class based on the range of its enumerators.
226226

227+
- New :doc:`readability-ConditionalToStdMinMax
228+
<clang-tidy/checks/readability/ConditionalToStdMinMax>` check.
229+
230+
Replaces certain conditional statements with equivalent std::min or std::max expressions,
231+
improving readability and promoting the use of standard library functions.
232+
227233
- New :doc:`readability-reference-to-constructed-temporary
228234
<clang-tidy/checks/readability/reference-to-constructed-temporary>` check.
229235

clang-tools-extra/docs/clang-tidy/checks/list.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,7 @@ Clang-Tidy Checks
336336
:doc:`portability-restrict-system-includes <portability/restrict-system-includes>`, "Yes"
337337
:doc:`portability-simd-intrinsics <portability/simd-intrinsics>`,
338338
:doc:`portability-std-allocator-const <portability/std-allocator-const>`,
339+
:doc:`readability-ConditionalToStdMinMax <readability/ConditionalToStdMinMax>`, "Yes"
339340
:doc:`readability-avoid-const-params-in-decls <readability/avoid-const-params-in-decls>`, "Yes"
340341
:doc:`readability-avoid-return-with-void-value <readability/avoid-return-with-void-value>`,
341342
:doc:`readability-avoid-unconditional-preprocessor-if <readability/avoid-unconditional-preprocessor-if>`,
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
.. title:: clang-tidy - readability-ConditionalToStdMinMax
2+
3+
readability-ConditionalToStdMinMax
4+
==================================
5+
6+
Replaces certain conditional statements with equivalent std::min or std::max expressions,
7+
improving readability and promoting the use of standard library functions.
8+
9+
Examples:
10+
11+
Before:
12+
13+
.. code-block:: c++
14+
15+
void foo(){
16+
int a,b;
17+
if(a < b)
18+
a = b;
19+
}
20+
21+
22+
After:
23+
24+
.. code-block:: c++
25+
26+
int main(){
27+
a = std::max(a, b);
28+
29+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// RUN: %check_clang_tidy %s readability-ConditionalToStdMinMax %t
2+
3+
void foo() {
4+
int value1,value2;
5+
6+
// CHECK-MESSAGES: :[[@LINE+1]]:3: warning: use std::max instead of < [readability-ConditionalToStdMinMax]
7+
if (value1 < value2)
8+
value1 = value2; // CHECK-FIXES: value1 = std::max(value1, value2);
9+
10+
// CHECK-MESSAGES: :[[@LINE+1]]:3: warning: use std::min instead of < [readability-ConditionalToStdMinMax]
11+
if (value1 < value2)
12+
value2 = value1; // CHECK-FIXES: value2 = std::min(value1, value2);
13+
14+
// CHECK-MESSAGES: :[[@LINE+1]]:3: warning: use std::min instead of > [readability-ConditionalToStdMinMax]
15+
if (value2 > value1)
16+
value2 = value1; // CHECK-FIXES: value2 = std::min(value2, value1);
17+
18+
// CHECK-MESSAGES: :[[@LINE+1]]:3: warning: use std::max instead of > [readability-ConditionalToStdMinMax]
19+
if (value2 > value1)
20+
value1 = value2; // CHECK-FIXES: value1 = std::max(value2, value1);
21+
22+
// No suggestion needed here
23+
if (value1 == value2)
24+
value1 = value2;
25+
26+
27+
}

0 commit comments

Comments
 (0)