-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[clang-tidy] Handle C++ structured bindings in performance-for-range-copy
#77105
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
Conversation
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
@llvm/pr-subscribers-clang @llvm/pr-subscribers-clang-tidy Author: Clement Courbet (legrosbuffle) ChangesRight now we are not triggering on:
Full diff: https://github.com/llvm/llvm-project/pull/77105.diff 5 Files Affected:
diff --git a/clang-tools-extra/clang-tidy/performance/ForRangeCopyCheck.cpp b/clang-tools-extra/clang-tidy/performance/ForRangeCopyCheck.cpp
index 5bfa6fb0d02d5c..655e480ffa62cb 100644
--- a/clang-tools-extra/clang-tidy/performance/ForRangeCopyCheck.cpp
+++ b/clang-tools-extra/clang-tidy/performance/ForRangeCopyCheck.cpp
@@ -97,6 +97,15 @@ bool ForRangeCopyCheck::handleConstValueCopy(const VarDecl &LoopVar,
return true;
}
+static bool isReferenced(const VarDecl &LoopVar, const Stmt &Stmt,
+ ASTContext &Context) {
+ const auto IsLoopVar = varDecl(equalsNode(&LoopVar));
+ return !match(stmt(hasDescendant(declRefExpr(to(valueDecl(anyOf(
+ IsLoopVar, bindingDecl(forDecomposition(IsLoopVar)))))))),
+ Stmt, Context)
+ .empty();
+}
+
bool ForRangeCopyCheck::handleCopyIsOnlyConstReferenced(
const VarDecl &LoopVar, const CXXForRangeStmt &ForRange,
ASTContext &Context) {
@@ -113,9 +122,7 @@ bool ForRangeCopyCheck::handleCopyIsOnlyConstReferenced(
// compiler warning which can't be suppressed.
// Since this case is very rare, it is safe to ignore it.
if (!ExprMutationAnalyzer(*ForRange.getBody(), Context).isMutated(&LoopVar) &&
- !utils::decl_ref_expr::allDeclRefExprs(LoopVar, *ForRange.getBody(),
- Context)
- .empty()) {
+ isReferenced(LoopVar, *ForRange.getBody(), Context)) {
auto Diag = diag(
LoopVar.getLocation(),
"loop variable is copied but only used as const reference; consider "
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 4d25e2ebe85f5f..4491d239dc7888 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -419,6 +419,10 @@ Changes in existing checks
<clang-tidy/checks/performance/faster-string-find>` check to properly escape
single quotes.
+- Improved :doc:`performance-for-range-copy
+ <clang-tidy/checks/performance/for-range-copy>` check to handle cases where
+ the loop variable is a structured binding.
+
- Improved :doc:`performance-noexcept-move-constructor
<clang-tidy/checks/performance/noexcept-move-constructor>` to better handle
conditional noexcept expressions, eliminating false-positives.
diff --git a/clang-tools-extra/test/clang-tidy/checkers/performance/for-range-copy.cpp b/clang-tools-extra/test/clang-tidy/checkers/performance/for-range-copy.cpp
index 1a2eedc9e65c53..e8c967dacfb7d5 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/performance/for-range-copy.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/performance/for-range-copy.cpp
@@ -47,6 +47,11 @@ struct S {
S &operator=(const S &);
};
+struct Point {
+ ~Point() {}
+ int x, y;
+};
+
struct Convertible {
operator S() const {
return S();
@@ -87,6 +92,10 @@ void instantiated() {
// CHECK-MESSAGES: [[@LINE-1]]:16: warning: the loop variable's type is {{.*}}
// CHECK-FIXES: {{^}} for (const S& S2 : View<Iterator<S>>()) {}
+ for (const auto [X, Y] : View<Iterator<Point>>()) {}
+ // CHECK-MESSAGES: [[@LINE-1]]:19: warning: the loop variable's type is
+ // CHECK-FIXES: {{^}} for (const auto& [X, Y] : View<Iterator<Point>>()) {}
+
for (const T T2 : View<Iterator<T>>()) {}
// CHECK-MESSAGES: [[@LINE-1]]:16: warning: the loop variable's type is {{.*}}
// CHECK-FIXES: {{^}} for (const T& T2 : View<Iterator<T>>()) {}
@@ -123,11 +132,6 @@ struct Mutable {
~Mutable() {}
};
-struct Point {
- ~Point() {}
- int x, y;
-};
-
Mutable& operator<<(Mutable &Out, bool B) {
Out.setBool(B);
return Out;
@@ -144,6 +148,7 @@ void useByValue(Mutable M);
void useByConstValue(const Mutable M);
void mutate(Mutable *M);
void mutate(Mutable &M);
+void mutate(int &);
void onceConstOnceMutated(const Mutable &M1, Mutable &M2);
void negativeVariableIsMutated() {
@@ -234,6 +239,22 @@ void positiveOnlyAccessedFieldAsConst() {
}
}
+void positiveOnlyAccessedFieldAsConstBinding() {
+ for (auto [X, Y] : View<Iterator<Point>>()) {
+ // CHECK-MESSAGES: [[@LINE-1]]:13: warning: loop variable is copied but
+ // CHECK-FIXES: for (const auto& [X, Y] : View<Iterator<Point>>()) {
+ use(X);
+ use(Y);
+ }
+}
+
+void negativeOnlyAccessedFieldAsConstBinding() {
+ for (auto [X, Y] : View<Iterator<Point>>()) {
+ use(X);
+ mutate(Y);
+ }
+}
+
void positiveOnlyUsedInCopyConstructor() {
for (auto A : View<Iterator<Mutable>>()) {
// CHECK-MESSAGES: [[@LINE-1]]:13: warning: loop variable is copied but only used as const reference; consider making it a const reference [performance-for-range-copy]
diff --git a/clang/lib/Analysis/ExprMutationAnalyzer.cpp b/clang/lib/Analysis/ExprMutationAnalyzer.cpp
index 624a643cc60e4b..29f05b25bdec2b 100644
--- a/clang/lib/Analysis/ExprMutationAnalyzer.cpp
+++ b/clang/lib/Analysis/ExprMutationAnalyzer.cpp
@@ -204,9 +204,16 @@ const Stmt *ExprMutationAnalyzer::findMutationMemoized(
const Stmt *ExprMutationAnalyzer::tryEachDeclRef(const Decl *Dec,
MutationFinder Finder) {
- const auto Refs =
- match(findAll(declRefExpr(to(equalsNode(Dec))).bind(NodeID<Expr>::value)),
- Stm, Context);
+ const auto Refs = match(
+ findAll(
+ declRefExpr(to(
+ // `Dec` or a binding if `Dec` is a decomposition.
+ anyOf(equalsNode(Dec),
+ bindingDecl(forDecomposition(equalsNode(Dec))))
+ //
+ ))
+ .bind(NodeID<Expr>::value)),
+ Stm, Context);
for (const auto &RefNodes : Refs) {
const auto *E = RefNodes.getNodeAs<Expr>(NodeID<Expr>::value);
if ((this->*Finder)(E))
diff --git a/clang/unittests/Analysis/ExprMutationAnalyzerTest.cpp b/clang/unittests/Analysis/ExprMutationAnalyzerTest.cpp
index f1a1f857a0ee5b..a94f857720b035 100644
--- a/clang/unittests/Analysis/ExprMutationAnalyzerTest.cpp
+++ b/clang/unittests/Analysis/ExprMutationAnalyzerTest.cpp
@@ -592,6 +592,26 @@ TEST(ExprMutationAnalyzerTest, ByConstRRefArgument) {
ElementsAre("static_cast<A &&>(x)"));
}
+// Section: bindings.
+
+TEST(ExprMutationAnalyzerTest, BindingModifies) {
+ const auto AST =
+ buildASTFromCode("struct Point { int x; int y; };"
+ "void mod(int&);"
+ "void f(Point p) { auto& [x, y] = p; mod(x); }");
+ const auto Results =
+ match(withEnclosingCompound(declRefTo("p")), AST->getASTContext());
+ EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x", "mod(x)"));
+}
+
+TEST(ExprMutationAnalyzerTest, BindingDoesNotModify) {
+ const auto AST = buildASTFromCode("struct Point { int x; int y; };"
+ "void f(Point p) { auto& [x, y] = p; x; }");
+ const auto Results =
+ match(withEnclosingCompound(declRefTo("p")), AST->getASTContext());
+ EXPECT_FALSE(isMutated(Results, AST.get()));
+}
+
// section: explicit std::move and std::forward testing
TEST(ExprMutationAnalyzerTest, Move) {
|
fberger
approved these changes
Jan 8, 2024
clang-tools-extra/test/clang-tidy/checkers/performance/for-range-copy.cpp
Outdated
Show resolved
Hide resolved
clang-tools-extra/test/clang-tidy/checkers/performance/for-range-copy.cpp
Outdated
Show resolved
Hide resolved
Right now we are not triggering on: ``` for (auto [x, y] : container) { // const-only access } ```
d5b83c7
to
7420ce9
Compare
justinfargnoli
pushed a commit
to justinfargnoli/llvm-project
that referenced
this pull request
Jan 28, 2024
…-copy` (llvm#77105) Right now we are not triggering on: ``` for (auto [x, y] : container) { // const-only access } ```
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Labels
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.
Right now we are not triggering on: