-
Notifications
You must be signed in to change notification settings - Fork 13.6k
Add clang-tidy check to suggest replacement of conditional statement with std::min/std::max #77816
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
Merged
Merged
Changes from 53 commits
Commits
Show all changes
54 commits
Select commit
Hold shift + click to select a range
1883d98
Add readability check to suggest replacement of conditional statement…
11happy 89be4ba
Formatted the code
11happy e1b65a9
small fix
11happy 042ddc3
Revert "small fix"
11happy c17b298
Revert "Formatted the code"
11happy 9aad658
Revert "Add readability check to suggest replacement of conditional s…
11happy 2fc0938
Added Suggested Changes
11happy 1139ac4
Formatted the Code
11happy f73e9f0
Added Release Notes
11happy faf3312
corrected file name
11happy 5ed9896
corrected title length in docs
11happy 19c4b63
Suggested Changes
11happy 8167787
body indentation
11happy 3a28399
added intialisation,constexpr tests
11happy 2ecd08a
small fixes
11happy 00d4080
Addded Suggested Changes
11happy 6db0820
formatted the code
11happy 5073650
Added Include Check
11happy c728a39
Formatted the code
11happy b8202ae
Added tests for macro & if with brackets
11happy 3b57307
Fixed merge Conflict
11happy 5e31665
Added suggested changes
11happy a2cfcdc
Formatted the code
11happy f5a0f12
Converted to Lambdas,placed at sorted location
11happy e239576
Single Lambda
11happy b868d5b
CamelCase,const auto*
11happy 0797e0c
fixes
11happy c6028b0
few nits
11happy 6acf645
formatted the code
11happy 0eb4aca
extra checks
11happy d5b8dc2
ensure if has no else if
11happy a92160c
correct style issue
11happy 5eafb59
correct docs
11happy 26bfbdc
correct docs
11happy 57bccd6
static functions
11happy 61fd66a
small fix
11happy a841ebc
style issues
11happy 7d2c085
corrrect style issue
11happy 399c4c5
Merge branch 'SecondPR' of https://github.com/11happy/llvm-project in…
11happy abec9b5
correct functional bugs
11happy 6a0e495
pass dependent types
11happy 274b55f
filter implicit cast expr
11happy b7ca014
resolved merge conflict
11happy 8bf8235
Add mising type, optimise
11happy d8df8c8
removed whitespace
11happy 8a5d585
remove unsed initialisation
11happy 85fb67d
small change
11happy 1214547
simplify
11happy 6aef713
small fix
11happy f03b2ff
non template alias
11happy 5d504a7
simplify pointer dereference
11happy 19cc1f3
remove extra parentheses
11happy a316744
use spefici type instead of auto
11happy f0ef998
use auto for cast kind
11happy File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
189 changes: 189 additions & 0 deletions
189
clang-tools-extra/clang-tidy/readability/UseStdMinMaxCheck.cpp
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,189 @@ | ||
//===--- UseStdMinMaxCheck.cpp - clang-tidy -------------------------------===// | ||
// | ||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||
// See https://llvm.org/LICENSE.txt for license information. | ||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
#include "UseStdMinMaxCheck.h" | ||
#include "../utils/ASTUtils.h" | ||
#include "clang/AST/ASTContext.h" | ||
#include "clang/ASTMatchers/ASTMatchFinder.h" | ||
#include "clang/Lex/Preprocessor.h" | ||
|
||
using namespace clang::ast_matchers; | ||
|
||
namespace clang::tidy::readability { | ||
|
||
namespace { | ||
|
||
// Ignore if statements that are inside macros. | ||
AST_MATCHER(IfStmt, isIfInMacro) { | ||
return Node.getIfLoc().isMacroID() || Node.getEndLoc().isMacroID(); | ||
} | ||
|
||
} // namespace | ||
|
||
static const llvm::StringRef AlgorithmHeader("<algorithm>"); | ||
|
||
static bool minCondition(const BinaryOperator::Opcode Op, const Expr *CondLhs, | ||
const Expr *CondRhs, const Expr *AssignLhs, | ||
const Expr *AssignRhs, const ASTContext &Context) { | ||
if ((Op == BO_LT || Op == BO_LE) && | ||
(tidy::utils::areStatementsIdentical(CondLhs, AssignRhs, Context) && | ||
tidy::utils::areStatementsIdentical(CondRhs, AssignLhs, Context))) | ||
return true; | ||
|
||
if ((Op == BO_GT || Op == BO_GE) && | ||
(tidy::utils::areStatementsIdentical(CondLhs, AssignLhs, Context) && | ||
tidy::utils::areStatementsIdentical(CondRhs, AssignRhs, Context))) | ||
return true; | ||
|
||
return false; | ||
} | ||
|
||
static bool maxCondition(const BinaryOperator::Opcode Op, const Expr *CondLhs, | ||
const Expr *CondRhs, const Expr *AssignLhs, | ||
const Expr *AssignRhs, const ASTContext &Context) { | ||
if ((Op == BO_LT || Op == BO_LE) && | ||
(tidy::utils::areStatementsIdentical(CondLhs, AssignLhs, Context) && | ||
tidy::utils::areStatementsIdentical(CondRhs, AssignRhs, Context))) | ||
return true; | ||
|
||
if ((Op == BO_GT || Op == BO_GE) && | ||
(tidy::utils::areStatementsIdentical(CondLhs, AssignRhs, Context) && | ||
tidy::utils::areStatementsIdentical(CondRhs, AssignLhs, Context))) | ||
return true; | ||
|
||
return false; | ||
} | ||
|
||
QualType getNonTemplateAlias(QualType QT) { | ||
while (true) { | ||
// cast to a TypedefType | ||
if (const TypedefType *TT = dyn_cast<TypedefType>(QT)) { | ||
// check if the typedef is a template and if it is dependent | ||
if (!TT->getDecl()->getDescribedTemplate() && | ||
!TT->getDecl()->getDeclContext()->isDependentContext()) | ||
return QT; | ||
QT = TT->getDecl()->getUnderlyingType(); | ||
} | ||
// cast to elaborated type | ||
else if (const ElaboratedType *ET = dyn_cast<ElaboratedType>(QT)) { | ||
QT = ET->getNamedType(); | ||
} else { | ||
break; | ||
} | ||
} | ||
return QT; | ||
} | ||
|
||
static std::string createReplacement(const Expr *CondLhs, const Expr *CondRhs, | ||
const Expr *AssignLhs, | ||
const SourceManager &Source, | ||
const LangOptions &LO, | ||
StringRef FunctionName, | ||
const BinaryOperator *BO) { | ||
const llvm::StringRef CondLhsStr = Lexer::getSourceText( | ||
Source.getExpansionRange(CondLhs->getSourceRange()), Source, LO); | ||
const llvm::StringRef CondRhsStr = Lexer::getSourceText( | ||
Source.getExpansionRange(CondRhs->getSourceRange()), Source, LO); | ||
const llvm::StringRef AssignLhsStr = Lexer::getSourceText( | ||
Source.getExpansionRange(AssignLhs->getSourceRange()), Source, LO); | ||
|
||
clang::QualType GlobalImplicitCastType; | ||
clang::QualType LhsType = CondLhs->getType() | ||
.getCanonicalType() | ||
.getNonReferenceType() | ||
.getUnqualifiedType(); | ||
clang::QualType RhsType = CondRhs->getType() | ||
.getCanonicalType() | ||
.getNonReferenceType() | ||
.getUnqualifiedType(); | ||
if (LhsType != RhsType) { | ||
GlobalImplicitCastType = getNonTemplateAlias(BO->getLHS()->getType()); | ||
} | ||
|
||
return (AssignLhsStr + " = " + FunctionName + | ||
(!GlobalImplicitCastType.isNull() | ||
? "<" + GlobalImplicitCastType.getAsString() + ">(" | ||
: "(") + | ||
CondLhsStr + ", " + CondRhsStr + ");") | ||
.str(); | ||
} | ||
|
||
UseStdMinMaxCheck::UseStdMinMaxCheck(StringRef Name, ClangTidyContext *Context) | ||
: ClangTidyCheck(Name, Context), | ||
IncludeInserter(Options.getLocalOrGlobal("IncludeStyle", | ||
utils::IncludeSorter::IS_LLVM), | ||
areDiagsSelfContained()) {} | ||
|
||
void UseStdMinMaxCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { | ||
Options.store(Opts, "IncludeStyle", IncludeInserter.getStyle()); | ||
11happy marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
void UseStdMinMaxCheck::registerMatchers(MatchFinder *Finder) { | ||
auto AssignOperator = | ||
binaryOperator(hasOperatorName("="), | ||
hasLHS(expr(unless(isTypeDependent())).bind("AssignLhs")), | ||
hasRHS(expr(unless(isTypeDependent())).bind("AssignRhs"))); | ||
auto BinaryOperator = | ||
binaryOperator(hasAnyOperatorName("<", ">", "<=", ">="), | ||
hasLHS(expr(unless(isTypeDependent())).bind("CondLhs")), | ||
hasRHS(expr(unless(isTypeDependent())).bind("CondRhs"))) | ||
.bind("binaryOp"); | ||
Finder->addMatcher( | ||
ifStmt(stmt().bind("if"), unless(isIfInMacro()), | ||
unless(hasElse(stmt())), // Ensure `if` has no `else` | ||
hasCondition(BinaryOperator), | ||
hasThen( | ||
anyOf(stmt(AssignOperator), | ||
compoundStmt(statementCountIs(1), has(AssignOperator)))), | ||
hasParent(stmt(unless(ifStmt(hasElse( | ||
equalsBoundNode("if"))))))), // Ensure `if` has no `else if` | ||
this); | ||
} | ||
|
||
void UseStdMinMaxCheck::registerPPCallbacks(const SourceManager &SM, | ||
Preprocessor *PP, | ||
Preprocessor *ModuleExpanderPP) { | ||
IncludeInserter.registerPreprocessor(PP); | ||
} | ||
|
||
void UseStdMinMaxCheck::check(const MatchFinder::MatchResult &Result) { | ||
const clang::IfStmt *If = Result.Nodes.getNodeAs<IfStmt>("if"); | ||
11happy marked this conversation as resolved.
Show resolved
Hide resolved
|
||
const clang::LangOptions &LO = Result.Context->getLangOpts(); | ||
const clang::Expr *CondLhs = Result.Nodes.getNodeAs<Expr>("CondLhs"); | ||
const clang::Expr *CondRhs = Result.Nodes.getNodeAs<Expr>("CondRhs"); | ||
const clang::Expr *AssignLhs = Result.Nodes.getNodeAs<Expr>("AssignLhs"); | ||
const clang::Expr *AssignRhs = Result.Nodes.getNodeAs<Expr>("AssignRhs"); | ||
const clang::BinaryOperator *BinaryOp = | ||
Result.Nodes.getNodeAs<BinaryOperator>("binaryOp"); | ||
const clang::BinaryOperatorKind BinaryOpcode = BinaryOp->getOpcode(); | ||
const SourceLocation IfLocation = If->getIfLoc(); | ||
const SourceLocation ThenLocation = If->getEndLoc(); | ||
11happy marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
auto ReplaceAndDiagnose = [&](const llvm::StringRef FunctionName) { | ||
const SourceManager &Source = *Result.SourceManager; | ||
diag(IfLocation, "use `%0` instead of `%1`") | ||
<< FunctionName << BinaryOp->getOpcodeStr() | ||
<< FixItHint::CreateReplacement( | ||
SourceRange(IfLocation, Lexer::getLocForEndOfToken( | ||
ThenLocation, 0, Source, LO)), | ||
createReplacement(CondLhs, CondRhs, AssignLhs, Source, LO, | ||
FunctionName, BinaryOp)) | ||
<< IncludeInserter.createIncludeInsertion( | ||
Source.getFileID(If->getBeginLoc()), AlgorithmHeader); | ||
}; | ||
|
||
if (minCondition(BinaryOpcode, CondLhs, CondRhs, AssignLhs, AssignRhs, | ||
(*Result.Context))) { | ||
ReplaceAndDiagnose("std::min"); | ||
} else if (maxCondition(BinaryOpcode, CondLhs, CondRhs, AssignLhs, AssignRhs, | ||
(*Result.Context))) { | ||
ReplaceAndDiagnose("std::max"); | ||
} | ||
} | ||
|
||
} // namespace clang::tidy::readability |
42 changes: 42 additions & 0 deletions
42
clang-tools-extra/clang-tidy/readability/UseStdMinMaxCheck.h
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
//===--- UseStdMinMaxCheck.h - clang-tidy -----------------------*- C++ -*-===// | ||
// | ||
// 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_READABILITY_USESTDMINMAXCHECK_H | ||
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_USESTDMINMAXCHECK_H | ||
|
||
#include "../ClangTidyCheck.h" | ||
#include "../utils/IncludeInserter.h" | ||
|
||
namespace clang::tidy::readability { | ||
11happy marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
/// Replaces certain conditional statements with equivalent calls to | ||
/// ``std::min`` or ``std::max``. | ||
/// For the user-facing documentation see: | ||
/// http://clang.llvm.org/extra/clang-tidy/checks/readability/UseStdMinMax.html | ||
class UseStdMinMaxCheck : public ClangTidyCheck { | ||
public: | ||
UseStdMinMaxCheck(StringRef Name, ClangTidyContext *Context); | ||
bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { | ||
return LangOpts.CPlusPlus; | ||
} | ||
void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP, | ||
Preprocessor *ModuleExpanderPP) override; | ||
void storeOptions(ClangTidyOptions::OptionMap &Opts) override; | ||
void registerMatchers(ast_matchers::MatchFinder *Finder) override; | ||
void check(const ast_matchers::MatchFinder::MatchResult &Result) override; | ||
std::optional<TraversalKind> getCheckTraversalKind() const override { | ||
return TK_IgnoreUnlessSpelledInSource; | ||
} | ||
|
||
private: | ||
utils::IncludeInserter IncludeInserter; | ||
}; | ||
|
||
} // namespace clang::tidy::readability | ||
|
||
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_USESTDMINMAXCHECK_H |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
29 changes: 29 additions & 0 deletions
29
clang-tools-extra/docs/clang-tidy/checks/readability/use-std-min-max.rst
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
.. title:: clang-tidy - readability-use-std-min-max | ||
|
||
readability-use-std-min-max | ||
=========================== | ||
|
||
Replaces certain conditional statements with equivalent calls to | ||
``std::min`` or ``std::max``. | ||
Note: This may impact performance in critical code due to potential | ||
additional stores compared to the original if statement. | ||
|
||
Before: | ||
|
||
.. code-block:: c++ | ||
|
||
void foo() { | ||
int a = 2, b = 3; | ||
if (a < b) | ||
a = b; | ||
} | ||
|
||
11happy marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
After: | ||
|
||
.. code-block:: c++ | ||
|
||
void foo() { | ||
int a = 2, b = 3; | ||
a = std::max(a, b); | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.