[clang-tools-extra] [clang-tidy] Add performance-redundant-lookup check (PR #125420)

2025-02-15 Thread Balazs Benics via cfe-commits

steakhal wrote:

> @steakhal: Please fix documentation and Release Notes wording that I 
> mentioned already.

Thanks, fixed!
I'm actually more curious about the semantic requirements, and about what 
directions should I explore more.

https://github.com/llvm/llvm-project/pull/125420
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [clang-tidy] Add performance-redundant-lookup check (PR #125420)

2025-02-15 Thread Balazs Benics via cfe-commits


@@ -91,6 +91,12 @@ Improvements to clang-tidy
 New checks
 ^^
 
+- New :doc:`performance-redundant-lookup
+  ` check.
+
+  This check warns about potential redundant container lookup operations within

steakhal wrote:

Dropped in c6a681c955fef8b933374a05a157f48ba4ead360

https://github.com/llvm/llvm-project/pull/125420
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [clang-tidy] Add performance-redundant-lookup check (PR #125420)

2025-02-15 Thread via cfe-commits

EugeneZelenko wrote:

@steakhal: Please fix documentation and Release Notes wording that I mentioned 
already.

https://github.com/llvm/llvm-project/pull/125420
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [clang-tidy] Add performance-redundant-lookup check (PR #125420)

2025-02-15 Thread Balazs Benics via cfe-commits

steakhal wrote:

> Thanks for the insight. Take your time.

So, I spent a few hours experimenting and it's really difficult to do, only 
using `ExprSequence`.
Just to recap, my idea was to match the assignments and inc/dec unary operator 
calls to detect if the container or the key object is mutated.
Then partition the lookup events if any of the lookups were "separated" by such 
a mutation.
Then check if the remaining group has at least 2 lookups, if so, report them.

It got fairly complicated and still buggy. I definitely think it's achievable, 
it's just too much that I could invest into.

So, let's say, I'd stick with the current version of [this 
branch](https://github.com/steakhal/llvm-project/tree/bb/tidy-add-redundant-lookup-check),
 what would be left to continue the review? @firewave 

https://github.com/llvm/llvm-project/pull/125420
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [clang-tidy] Add performance-redundant-lookup check (PR #125420)

2025-02-11 Thread Balazs Benics via cfe-commits

https://github.com/steakhal closed 
https://github.com/llvm/llvm-project/pull/125420
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [clang-tidy] Add performance-redundant-lookup check (PR #125420)

2025-02-10 Thread Oliver Stöneberg via cfe-commits

firewave wrote:

I think we should agree on an initial implementation to be landed and open 
separate tickets and not a single meta one for additional patterns to detect.

https://github.com/llvm/llvm-project/pull/125420
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [clang-tidy] Add performance-redundant-lookup check (PR #125420)

2025-02-09 Thread Balazs Benics via cfe-commits

steakhal wrote:

I think this case would be caught by the static analyzer based implementation.

There the loop would be unrolled about 3-4 times, so the checker should get to 
see the redundant lookups.

I'll check if my assessment is correct.

But I decided that the complexity does not worth the CSA based solution 
compared to the AST based.

Both has their ups and downs.

https://github.com/llvm/llvm-project/pull/125420
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [clang-tidy] Add performance-redundant-lookup check (PR #125420)

2025-02-09 Thread Kazu Hirata via cfe-commits

kazutakahirata wrote:

@steakhal, I don't meant to delay the landing of this PR, but I just want to 
bring up the following.  I'm wondering if your checker can catch repeated 
lookups within a loop like so:

```
for (int A : Vec)
  Map[Key].push_back(A);
```

in a function that does not reference `Map` anywhere else.  That is, 
statically, there is only one statement doing map lookups, but dynamically, the 
same lookup is repeated over and over.  Thanks!


https://github.com/llvm/llvm-project/pull/125420
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [clang-tidy] Add performance-redundant-lookup check (PR #125420)

2025-02-08 Thread Balazs Benics via cfe-commits

steakhal wrote:

I revisited the check using `ExprSequence`.
It became more actionable from what I can tell, but exposes weaknesses of 
`ExprSequence` too, which I'll later demonstrate.

My approach with my prototype using `ExprSequence` was:
 1) Collect lookups just like before.
 2) Take the cross product of the lookups, and build a graph using the 
`inSequence` relationship among the lookups.
 3) Take the nodes that has no parents, aka. roots of the `inSequence` graph, 
because I'll report a separate "bug" for the reachable lookups from each Root.
 4) Pick the last lookup of the group as the diagnostic location, and add notes 
for the rest.
 
 This got fairly involved, and likely scales really poorly with the number of 
lookups using the same key and container.
 The results are mixed, and I think it's caused by `ExprSequence`, and 
hallucinates `inSequence` relationship for Stmt pairs where it actually 
shouldn't.
 
Here are a couple of examples:
![image](https://github.com/user-attachments/assets/78f6a662-4b54-4145-ad73-8221ad48218e)
![image](https://github.com/user-attachments/assets/a53cd473-8601-4339-9548-dd351e9b1b7b)
![image](https://github.com/user-attachments/assets/f723cc13-40b7-4e2b-a060-377d5b2c7f44)


So, if `ExprSequence` would honor `return` statements and if blocks, then it 
would be a viable direction to move forward.

https://github.com/llvm/llvm-project/pull/125420
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [clang-tidy] Add performance-redundant-lookup check (PR #125420)

2025-02-04 Thread Balazs Benics via cfe-commits

steakhal wrote:

> I ran it on the Cppcheck codebase and there were several cases I would 
> consider false positives as well as some which are really awkward to fix (in 
> some cases leading to potentially unnecessary lookups). I will provide some 
> examples in the next few days.

I ran it on cppcheck, and I had 88 reports in total.
See the results at 
[this](https://codechecker-demo.eastus.cloudapp.azure.com/Default/reports?review-status=Confirmed%20bug&review-status=False%20positive&detection-status=New&detection-status=Reopened&detection-status=Unresolved&run=steakhal-redundant-lookups-in-cppcheck&is-unique=off&diff-type=New)
 CodeChecker instance. Apply filters to look for only TPs or FPs. [Thanks for 
Ericsson hosting the instance btw]
I looked each of the reports and classified them as "true positive" if I 
(likely) could refactor the code and would make sense doing so. FP otherwise if 
I wouldn't refactor the code.
This gave me 56 TPs and 32 FPs.

Note that sometimes the looked ugly, and had a lot of nesting. It may be 
possible to refactor but I'd decide not touching it. This is one form of a FP.
The FP TP classification is of course arbitrary, and you may disagree with what 
I choose.
These TPs are the cases where I think the code could be and should be 
refactored, nothing more.

After seeing the results on cppcheck, I think it would make sense to use 
`ExprSequence` to ensure that the lookups are not on mutually exclusive paths 
for instance.
It would also match better with the category of the check "performance".
On the down side, it would make us no longer diagnose code like this:
```c++
if (cond)
  map[key] = foo();
else
  map[key] = bar();
```
I've seen code like this part of the TP set.

BTW I've not seen any misclassification of "map" or "set" objects due to the 
relaxed regex.

https://github.com/llvm/llvm-project/pull/125420
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [clang-tidy] Add performance-redundant-lookup check (PR #125420)

2025-02-03 Thread Oliver Stöneberg via cfe-commits

firewave wrote:

I ran it on the Cppcheck codebase and there were several cases I would consider 
false positives as well as some which are really awkward to fix (in some cases 
leading to potentially unnecessary lookups). I will provide some examples in 
the next few days.

https://github.com/llvm/llvm-project/pull/125420
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [clang-tidy] Add performance-redundant-lookup check (PR #125420)

2025-02-03 Thread Balazs Benics via cfe-commits

steakhal wrote:

> Performance looks good. Everything under 5% is acceptable.
> 
> The top three entries should probably get tickets so they get looked into. 
> The amount seems too much for what it actually provides.

Yea, my idea was to give a bit of context of how it compares to the resr of the 
checks. Good to know its acceptable! Thanks.

https://github.com/llvm/llvm-project/pull/125420
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [clang-tidy] Add performance-redundant-lookup check (PR #125420)

2025-02-03 Thread Oliver Stöneberg via cfe-commits

firewave wrote:

Performance looks good. Everything under 5% is acceptable.

The top three entries should probably get tickets so they get looked into. The 
amount seems too much for what it actually provides.

https://github.com/llvm/llvm-project/pull/125420
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [clang-tidy] Add performance-redundant-lookup check (PR #125420)

2025-02-03 Thread via cfe-commits


@@ -91,6 +91,12 @@ Improvements to clang-tidy
 New checks
 ^^
 
+- New :doc:`performance-redundant-lookup
+  ` check.
+
+  This check warns about potential redundant container lookup operations within

EugeneZelenko wrote:

`This check` should be omitted.

https://github.com/llvm/llvm-project/pull/125420
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [clang-tidy] Add performance-redundant-lookup check (PR #125420)

2025-02-03 Thread Balazs Benics via cfe-commits


@@ -0,0 +1,147 @@
+.. title:: clang-tidy - performance-redundant-lookup
+
+performance-redundant-lookup
+
+
+This check warns about potential redundant container lookup operations within

steakhal wrote:

Fixed in d9d3984a056192b8ef61f385090d90c55774123d

https://github.com/llvm/llvm-project/pull/125420
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [clang-tidy] Add performance-redundant-lookup check (PR #125420)

2025-02-03 Thread Balazs Benics via cfe-commits

https://github.com/steakhal updated 
https://github.com/llvm/llvm-project/pull/125420

>From 78390b2af01fcd5acfbd2a313852962c873e5611 Mon Sep 17 00:00:00 2001
From: Balazs Benics 
Date: Sun, 2 Feb 2025 17:35:39 +0100
Subject: [PATCH 1/8] [clang-tidy] Add performance-redundant-lookup check

Finds redundant lookups to set|map containers.
For example: `map.count(key) && map[key] < threshold`
---
 .../clang-tidy/performance/CMakeLists.txt |   1 +
 .../performance/PerformanceTidyModule.cpp |   3 +
 .../performance/RedundantLookupCheck.cpp  | 159 ++
 .../performance/RedundantLookupCheck.h|  46 +
 clang-tools-extra/docs/ReleaseNotes.rst   |   5 +
 .../docs/clang-tidy/checks/list.rst   |   1 +
 .../checks/performance/redundant-lookup.rst   | 147 
 .../checkers/performance/redundant-lookup.cpp | 142 
 8 files changed, 504 insertions(+)
 create mode 100644 
clang-tools-extra/clang-tidy/performance/RedundantLookupCheck.cpp
 create mode 100644 
clang-tools-extra/clang-tidy/performance/RedundantLookupCheck.h
 create mode 100644 
clang-tools-extra/docs/clang-tidy/checks/performance/redundant-lookup.rst
 create mode 100644 
clang-tools-extra/test/clang-tidy/checkers/performance/redundant-lookup.cpp

diff --git a/clang-tools-extra/clang-tidy/performance/CMakeLists.txt 
b/clang-tools-extra/clang-tidy/performance/CMakeLists.txt
index c6e547c5089fb0..6f907852c9fd1c 100644
--- a/clang-tools-extra/clang-tidy/performance/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/performance/CMakeLists.txt
@@ -21,6 +21,7 @@ add_clang_library(clangTidyPerformanceModule STATIC
   NoexceptMoveConstructorCheck.cpp
   NoexceptSwapCheck.cpp
   PerformanceTidyModule.cpp
+  RedundantLookupCheck.cpp
   TriviallyDestructibleCheck.cpp
   TypePromotionInMathFnCheck.cpp
   UnnecessaryCopyInitialization.cpp
diff --git a/clang-tools-extra/clang-tidy/performance/PerformanceTidyModule.cpp 
b/clang-tools-extra/clang-tidy/performance/PerformanceTidyModule.cpp
index 9e0fa6f88b36a0..a613114e6b83bd 100644
--- a/clang-tools-extra/clang-tidy/performance/PerformanceTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/performance/PerformanceTidyModule.cpp
@@ -24,6 +24,7 @@
 #include "NoexceptDestructorCheck.h"
 #include "NoexceptMoveConstructorCheck.h"
 #include "NoexceptSwapCheck.h"
+#include "RedundantLookupCheck.h"
 #include "TriviallyDestructibleCheck.h"
 #include "TypePromotionInMathFnCheck.h"
 #include "UnnecessaryCopyInitialization.h"
@@ -62,6 +63,8 @@ class PerformanceModule : public ClangTidyModule {
 "performance-noexcept-move-constructor");
 CheckFactories.registerCheck(
 "performance-noexcept-swap");
+CheckFactories.registerCheck(
+"performance-redundant-lookup");
 CheckFactories.registerCheck(
 "performance-trivially-destructible");
 CheckFactories.registerCheck(
diff --git a/clang-tools-extra/clang-tidy/performance/RedundantLookupCheck.cpp 
b/clang-tools-extra/clang-tidy/performance/RedundantLookupCheck.cpp
new file mode 100644
index 00..fefb28682c5b8a
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/performance/RedundantLookupCheck.cpp
@@ -0,0 +1,159 @@
+//===--- RedundantLookupCheck.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 "RedundantLookupCheck.h"
+#include "../utils/OptionsUtils.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Regex.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::performance {
+
+static constexpr auto DefaultContainerNameRegex = "set|map";
+
+static const llvm::StringRef DefaultLookupMethodNames =
+llvm::StringLiteral( //
+"at;"
+"contains;"
+"count;"
+"find_as;"
+"find;"
+// These are tricky, as they take the "key" at different places.
+// They sometimes bundle up the key and the value together in a pair.
+//   "emplace;"
+//   "insert_or_assign;"
+//   "insert;"
+//   "try_emplace;"
+)
+.drop_back(); // Drops the last semicolon.
+
+RedundantLookupCheck::RedundantLookupCheck(StringRef Name,
+   ClangTidyContext *Context)
+: ClangTidyCheck(Name, Context),
+  ContainerNameRegex(
+  Options.get("ContainerNameRegex", DefaultContainerNameRegex)),
+  LookupMethodNames(utils::options::parseStringList(
+  Options.get("LookupMethodNames", DefaultLookupMethodNames))) {}
+
+void RedundantLookupCheck::storeOptions(ClangTidyOption

[clang-tools-extra] [clang-tidy] Add performance-redundant-lookup check (PR #125420)

2025-02-03 Thread Balazs Benics via cfe-commits

steakhal wrote:

> > Could you please run this with `--enable-check-profile` to see how heavy it 
> > is?
> 
> I plan to re-run it on clang soon, and share the results.


I've picked a heavy TU of clang for the test: `clang/lib/Sema/SemaExpr.cpp`
```
===-===
  clang-tidy checks profiling
===-===
  Total Execution Time: 12.6394 seconds (12.6125 wall clock)

   ---User Time---   --System Time--   --User+System--   ---Wall Time---  --- 
Name ---
   1.3723 ( 19.8%)   1.3141 ( 23.0%)   2.6865 ( 21.3%)   2.6692 ( 21.2%)  
misc-unused-using-decls
   0.7283 ( 10.5%)   0.6586 ( 11.5%)   1.3869 ( 11.0%)   1.3875 ( 11.0%)  
llvm-prefer-isa-or-dyn-cast-in-conditionals
   0.9165 ( 13.2%)   0.2823 (  4.9%)   1.1987 (  9.5%)   1.1982 (  9.5%)  
llvm-qualified-auto
   0.5424 (  7.8%)   0.4717 (  8.3%)   1.0141 (  8.0%)   1.0058 (  8.0%)  
misc-unconventional-assign-operator
   0.4127 (  6.0%)   0.3865 (  6.8%)   0.7991 (  6.3%)   0.7982 (  6.3%)  
misc-misleading-identifier
   0.4212 (  6.1%)   0.3571 (  6.2%)   0.7783 (  6.2%)   0.7810 (  6.2%)  
misc-confusable-identifiers
   0.4033 (  5.8%)   0.3750 (  6.6%)   0.7784 (  6.2%)   0.7775 (  6.2%)  
misc-definitions-in-headers
   0.3977 (  5.7%)   0.3700 (  6.5%)   0.7677 (  6.1%)   0.7658 (  6.1%)  
misc-non-copyable-objects
   0.2984 (  4.3%)   0.2572 (  4.5%)   0.5556 (  4.4%)   0.5532 (  4.4%)  
misc-redundant-expression
   0.2539 (  3.7%)   0.2164 (  3.8%)   0.4703 (  3.7%)   0.4691 (  3.7%)  
performance-redundant-lookup
   0.2140 (  3.1%)   0.1966 (  3.4%)   0.4107 (  3.2%)   0.4092 (  3.2%)  
misc-misplaced-const
   0.2021 (  2.9%)   0.1826 (  3.2%)   0.3846 (  3.0%)   0.3911 (  3.1%)  
misc-use-internal-linkage
   0.1293 (  1.9%)   0.1195 (  2.1%)   0.2488 (  2.0%)   0.2495 (  2.0%)  
misc-new-delete-overloads
   0.1141 (  1.6%)   0.1046 (  1.8%)   0.2187 (  1.7%)   0.2189 (  1.7%)  
llvm-prefer-register-over-unsigned
   0.1209 (  1.7%)   0.0911 (  1.6%)   0.2120 (  1.7%)   0.2112 (  1.7%)  
misc-static-assert
   0.1081 (  1.6%)   0.0989 (  1.7%)   0.2070 (  1.6%)   0.2093 (  1.7%)  
llvm-twine-local
   0.0673 (  1.0%)   0.0670 (  1.2%)   0.1343 (  1.1%)   0.1325 (  1.1%)  
misc-unused-alias-decls
   0.0693 (  1.0%)   0.0633 (  1.1%)   0.1326 (  1.0%)   0.1309 (  1.0%)  
misc-uniqueptr-reset-release
   0.0699 (  1.0%)   0.0567 (  1.0%)   0.1266 (  1.0%)   0.1261 (  1.0%)  
llvm-else-after-return
   0.0736 (  1.1%)   0.0358 (  0.6%)   0.1094 (  0.9%)   0.1093 (  0.9%)  
llvm-namespace-comment
   0.0100 (  0.1%)   0.0091 (  0.2%)   0.0191 (  0.2%)   0.0190 (  0.2%)  
misc-misleading-bidirectional
   0. (  0.0%)   0. (  0.0%)   0. (  0.0%)   0. (  0.0%)  
misc-throw-by-value-catch-by-reference
   6.9252 (100.0%)   5.7142 (100.0%)  12.6394 (100.0%)  12.6125 (100.0%)  Total
```
   
The check `performance-redundant-lookup` had a single hit at line 5353 for that 
TU, which was a TP:
```c++
  if (Param->hasUnparsedDefaultArg()) {
assert(!RewrittenInit && "Should not have a rewritten init expression yet");
// If we've already cleared out the location for the default argument,
// that means we're parsing it right now.
if (!UnparsedDefaultArgLocs.count(Param)) { // <-- 
first lookup
  Diag(Param->getBeginLoc(), diag::err_recursive_default_argument) << FD;
  Diag(CallLoc, diag::note_recursive_default_argument_used_here);
  Param->setInvalidDecl();
  return true;
}

Diag(CallLoc, diag::err_use_of_default_argument_to_function_declared_later)
<< FD << cast(FD->getDeclContext());
Diag(UnparsedDefaultArgLocs[Param], // <- second 
lookup
 diag::note_default_argument_declared_here);
return true;
  }
```

https://github.com/llvm/llvm-project/pull/125420
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [clang-tidy] Add performance-redundant-lookup check (PR #125420)

2025-02-03 Thread via cfe-commits


@@ -0,0 +1,147 @@
+.. title:: clang-tidy - performance-redundant-lookup
+
+performance-redundant-lookup
+
+
+This check warns about potential redundant container lookup operations within

EugeneZelenko wrote:

Please synchronize with statement in Release Notes.

https://github.com/llvm/llvm-project/pull/125420
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [clang-tidy] Add performance-redundant-lookup check (PR #125420)

2025-02-03 Thread Balazs Benics via cfe-commits


@@ -0,0 +1,159 @@
+//===--- RedundantLookupCheck.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 "RedundantLookupCheck.h"
+#include "../utils/OptionsUtils.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Regex.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::performance {
+
+static constexpr auto DefaultContainerNameRegex = "set|map";
+
+static const llvm::StringRef DefaultLookupMethodNames =
+llvm::StringLiteral( //
+"at;"
+"contains;"
+"count;"
+"find_as;"
+"find;"
+// These are tricky, as they take the "key" at different places.
+// They sometimes bundle up the key and the value together in a pair.
+//   "emplace;"
+//   "insert_or_assign;"
+//   "insert;"
+//   "try_emplace;"
+)
+.drop_back(); // Drops the last semicolon.
+
+RedundantLookupCheck::RedundantLookupCheck(StringRef Name,
+   ClangTidyContext *Context)
+: ClangTidyCheck(Name, Context),
+  ContainerNameRegex(
+  Options.get("ContainerNameRegex", DefaultContainerNameRegex)),
+  LookupMethodNames(utils::options::parseStringList(
+  Options.get("LookupMethodNames", DefaultLookupMethodNames))) {}
+
+void RedundantLookupCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "ContainerNameRegex", ContainerNameRegex);
+  Options.store(Opts, "LookupMethodNames",
+utils::options::serializeStringList(LookupMethodNames));
+}
+
+namespace {
+/// Checks if any of the ends of the source range is in a macro expansion.
+AST_MATCHER(Expr, hasMacroSourceRange) {
+  SourceRange R = Node.getSourceRange();
+  return R.getBegin().isMacroID() || R.getEnd().isMacroID();
+}
+} // namespace
+
+static constexpr const char *ObjKey = "obj";
+static constexpr const char *LookupKey = "key";
+static constexpr const char *LookupCallKey = "lookup";
+static constexpr const char *EnclosingFnKey = "fn";
+
+void RedundantLookupCheck::registerMatchers(MatchFinder *Finder) {
+  auto MatchesContainerNameRegex =
+  matchesName(ContainerNameRegex, llvm::Regex::IgnoreCase);
+
+  // Match that the expression is a record type with a name that contains "map"
+  // or "set".
+  auto RecordCalledMapOrSet =
+  expr(ignoringImpCasts(hasType(hasUnqualifiedDesugaredType(recordType(
+   hasDeclaration(namedDecl(MatchesContainerNameRegex)))
+  .bind(ObjKey);
+
+  auto SubscriptCall =
+  cxxOperatorCallExpr(hasOverloadedOperatorName("[]"), argumentCountIs(2),
+  hasArgument(0, RecordCalledMapOrSet),
+  hasArgument(1, expr().bind(LookupKey)));
+
+  auto LookupMethodCalls =
+  cxxMemberCallExpr(on(RecordCalledMapOrSet), argumentCountIs(1),
+hasArgument(0, expr().bind(LookupKey)),
+callee(cxxMethodDecl(hasAnyName(LookupMethodNames;
+
+  // Match any lookup or subscript calls that are not in a macro expansion.
+  auto AnyLookup = callExpr(unless(hasMacroSourceRange()),
+anyOf(SubscriptCall, LookupMethodCalls))
+   .bind(LookupCallKey);
+
+  // We need to collect all lookups in a function to be able to report them in
+  // batches.
+  Finder->addMatcher(
+  functionDecl(hasBody(compoundStmt(forEachDescendant(AnyLookup
+  .bind(EnclosingFnKey),
+  this);
+}
+
+/// Hash the container object expr along with the key used for lookup and the
+/// enclosing function together.
+static unsigned hashLookupEvent(const ASTContext &Ctx,
+const FunctionDecl *EnclosingFn,
+const Expr *LookupKey,
+const Expr *ContainerObject) {
+  llvm::FoldingSetNodeID ID;
+  ID.AddPointer(EnclosingFn);
+
+  LookupKey->Profile(ID, Ctx, /*Canonical=*/true,
+ /*ProfileLambdaExpr=*/true);
+  ContainerObject->Profile(ID, Ctx, /*Canonical=*/true,
+   /*ProfileLambdaExpr=*/true);
+  return ID.ComputeHash();
+}
+
+void RedundantLookupCheck::check(const MatchFinder::MatchResult &Result) {
+  SM = Result.SourceManager;
+
+  const auto *EnclosingFn =
+  Result.Nodes.getNodeAs(EnclosingFnKey);
+  const auto *LookupCall = Result.Nodes.getNodeAs(LookupCallKey);
+  const auto *Key = Result.Nodes.getNodeAs(LookupKey);
+  const auto *ContainerObject = Result.Nodes.getNodeAs(ObjKey);

[clang-tools-extra] [clang-tidy] Add performance-redundant-lookup check (PR #125420)

2025-02-02 Thread Kazu Hirata via cfe-commits

kazutakahirata wrote:

@steakhal I just tried this PR on the LLVM code base (that is, 
`llvm-project/llvm`).  I'm impressed how many real issues it finds.  Thank you 
so much!

https://github.com/llvm/llvm-project/pull/125420
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [clang-tidy] Add performance-redundant-lookup check (PR #125420)

2025-02-02 Thread Piotr Zegar via cfe-commits


@@ -0,0 +1,159 @@
+//===--- RedundantLookupCheck.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 "RedundantLookupCheck.h"
+#include "../utils/OptionsUtils.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Regex.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::performance {
+
+static constexpr auto DefaultContainerNameRegex = "set|map";
+
+static const llvm::StringRef DefaultLookupMethodNames =
+llvm::StringLiteral( //
+"at;"
+"contains;"
+"count;"
+"find_as;"
+"find;"
+// These are tricky, as they take the "key" at different places.
+// They sometimes bundle up the key and the value together in a pair.
+//   "emplace;"
+//   "insert_or_assign;"
+//   "insert;"
+//   "try_emplace;"
+)
+.drop_back(); // Drops the last semicolon.
+
+RedundantLookupCheck::RedundantLookupCheck(StringRef Name,
+   ClangTidyContext *Context)
+: ClangTidyCheck(Name, Context),
+  ContainerNameRegex(
+  Options.get("ContainerNameRegex", DefaultContainerNameRegex)),
+  LookupMethodNames(utils::options::parseStringList(
+  Options.get("LookupMethodNames", DefaultLookupMethodNames))) {}
+
+void RedundantLookupCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "ContainerNameRegex", ContainerNameRegex);
+  Options.store(Opts, "LookupMethodNames",
+utils::options::serializeStringList(LookupMethodNames));
+}
+
+namespace {
+/// Checks if any of the ends of the source range is in a macro expansion.
+AST_MATCHER(Expr, hasMacroSourceRange) {
+  SourceRange R = Node.getSourceRange();
+  return R.getBegin().isMacroID() || R.getEnd().isMacroID();
+}
+} // namespace
+
+static constexpr const char *ObjKey = "obj";
+static constexpr const char *LookupKey = "key";
+static constexpr const char *LookupCallKey = "lookup";
+static constexpr const char *EnclosingFnKey = "fn";
+
+void RedundantLookupCheck::registerMatchers(MatchFinder *Finder) {
+  auto MatchesContainerNameRegex =
+  matchesName(ContainerNameRegex, llvm::Regex::IgnoreCase);
+
+  // Match that the expression is a record type with a name that contains "map"
+  // or "set".
+  auto RecordCalledMapOrSet =
+  expr(ignoringImpCasts(hasType(hasUnqualifiedDesugaredType(recordType(
+   hasDeclaration(namedDecl(MatchesContainerNameRegex)))
+  .bind(ObjKey);
+
+  auto SubscriptCall =
+  cxxOperatorCallExpr(hasOverloadedOperatorName("[]"), argumentCountIs(2),
+  hasArgument(0, RecordCalledMapOrSet),
+  hasArgument(1, expr().bind(LookupKey)));
+
+  auto LookupMethodCalls =
+  cxxMemberCallExpr(on(RecordCalledMapOrSet), argumentCountIs(1),
+hasArgument(0, expr().bind(LookupKey)),
+callee(cxxMethodDecl(hasAnyName(LookupMethodNames;
+
+  // Match any lookup or subscript calls that are not in a macro expansion.
+  auto AnyLookup = callExpr(unless(hasMacroSourceRange()),
+anyOf(SubscriptCall, LookupMethodCalls))
+   .bind(LookupCallKey);
+
+  // We need to collect all lookups in a function to be able to report them in
+  // batches.
+  Finder->addMatcher(
+  functionDecl(hasBody(compoundStmt(forEachDescendant(AnyLookup
+  .bind(EnclosingFnKey),
+  this);
+}
+
+/// Hash the container object expr along with the key used for lookup and the
+/// enclosing function together.
+static unsigned hashLookupEvent(const ASTContext &Ctx,
+const FunctionDecl *EnclosingFn,
+const Expr *LookupKey,
+const Expr *ContainerObject) {
+  llvm::FoldingSetNodeID ID;
+  ID.AddPointer(EnclosingFn);
+
+  LookupKey->Profile(ID, Ctx, /*Canonical=*/true,
+ /*ProfileLambdaExpr=*/true);
+  ContainerObject->Profile(ID, Ctx, /*Canonical=*/true,
+   /*ProfileLambdaExpr=*/true);
+  return ID.ComputeHash();
+}
+
+void RedundantLookupCheck::check(const MatchFinder::MatchResult &Result) {
+  SM = Result.SourceManager;
+
+  const auto *EnclosingFn =
+  Result.Nodes.getNodeAs(EnclosingFnKey);
+  const auto *LookupCall = Result.Nodes.getNodeAs(LookupCallKey);
+  const auto *Key = Result.Nodes.getNodeAs(LookupKey);
+  const auto *ContainerObject = Result.Nodes.getNodeAs(ObjKey);

[clang-tools-extra] [clang-tidy] Add performance-redundant-lookup check (PR #125420)

2025-02-02 Thread Balazs Benics via cfe-commits


@@ -0,0 +1,147 @@
+.. title:: clang-tidy - performance-redundant-lookup
+
+performance-redundant-lookup
+
+
+This check warns about potential redundant container lookup operations within
+a function.
+
+Examples
+
+
+.. code-block:: c++
+
+if (map.count(key) && map[key] < threshold) {
+  // do stuff
+}
+
+In this example, we would check if the key is present in the map,
+and then do a second lookup to actually load the value.
+We could refactor the code into this, to use a single lookup:
+
+.. code-block:: c++
+
+if (auto it = map.find(key); it != map.end() && it->second < threshold) {
+  // do stuff
+}
+
+In this example, we do three lookups while calculating, caching and then
+using the result of some expensive computation:
+
+.. code-block:: c++
+
+if (!cache.contains(key)) {
+cache[key] = computeExpensiveValue();
+}
+use(cache[key]);
+
+We could refactor this code using ``try_emplace`` to fill up the cache entry
+if wasn't present, and just use it if we already computed the value.
+This way we would only have a single unavoidable lookup:
+
+.. code-block:: c++
+
+auto [cacheSlot, inserted] cache.try_emplace(key);
+if (inserted) {
+cacheSlot->second = computeExpensiveValue();
+}
+use(cacheSlot->second);
+
+
+What is a "lookup"?
+---
+
+All container operations that walk the internal structure of the container
+should be considered as "lookups".
+This means that checking if an element is present or inserting an element
+is also considered as a "lookup".
+
+For example, ``contains``, ``count`` but even the ``operator[]``
+should be considered as "lookups".
+
+Technically ``insert``, ``emplace`` or ``try_emplace`` are also lookups,
+even if due to limitations, they are not recognized as such.
+
+Lookups inside macros are ignored, thus not considered as "lookups".
+For example:
+
+.. code-block:: c++
+
+assert(map.count(key) == 0); // Not considered as a "lookup".
+
+Options
+---
+
+.. option:: ContainerNameRegex
+
+   The regular expression matching the type of the container objects.
+   This is matched in a case insensitive manner.
+   Default is ``set|map``.

steakhal wrote:

I share your concern. However, there is more to consider here.

I think, this check should work with user-defined containers out of the box, to 
have a better off the shelf experience.

If they find FPs, they can override this regex, and that's it.

I think the chances as slim to have container-like APIs like I'm matching here. 
For example, exatcly single argument "insert".

I could restrict the matches by requiring more things. For example, `key_type` 
type alias defined in the container, or something like that.
However, for doing that, I'd first ask if this misclassification really happens 
in practice to justify the complexity.

My experience with the FPs is that there are sharp edges with the check, but 
this one is not one of those.

https://github.com/llvm/llvm-project/pull/125420
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [clang-tidy] Add performance-redundant-lookup check (PR #125420)

2025-02-02 Thread Oliver Stöneberg via cfe-commits


@@ -0,0 +1,147 @@
+.. title:: clang-tidy - performance-redundant-lookup
+
+performance-redundant-lookup
+
+
+This check warns about potential redundant container lookup operations within
+a function.
+
+Examples
+
+
+.. code-block:: c++
+
+if (map.count(key) && map[key] < threshold) {
+  // do stuff
+}
+
+In this example, we would check if the key is present in the map,
+and then do a second lookup to actually load the value.
+We could refactor the code into this, to use a single lookup:
+
+.. code-block:: c++
+
+if (auto it = map.find(key); it != map.end() && it->second < threshold) {
+  // do stuff
+}
+
+In this example, we do three lookups while calculating, caching and then
+using the result of some expensive computation:
+
+.. code-block:: c++
+
+if (!cache.contains(key)) {
+cache[key] = computeExpensiveValue();
+}
+use(cache[key]);
+
+We could refactor this code using ``try_emplace`` to fill up the cache entry
+if wasn't present, and just use it if we already computed the value.
+This way we would only have a single unavoidable lookup:
+
+.. code-block:: c++
+
+auto [cacheSlot, inserted] cache.try_emplace(key);
+if (inserted) {
+cacheSlot->second = computeExpensiveValue();
+}
+use(cacheSlot->second);
+
+
+What is a "lookup"?
+---
+
+All container operations that walk the internal structure of the container
+should be considered as "lookups".
+This means that checking if an element is present or inserting an element
+is also considered as a "lookup".
+
+For example, ``contains``, ``count`` but even the ``operator[]``
+should be considered as "lookups".
+
+Technically ``insert``, ``emplace`` or ``try_emplace`` are also lookups,
+even if due to limitations, they are not recognized as such.
+
+Lookups inside macros are ignored, thus not considered as "lookups".
+For example:
+
+.. code-block:: c++
+
+assert(map.count(key) == 0); // Not considered as a "lookup".
+
+Options
+---
+
+.. option:: ContainerNameRegex
+
+   The regular expression matching the type of the container objects.
+   This is matched in a case insensitive manner.
+   Default is ``set|map``.

firewave wrote:

But it would match `class UseTest` or `class Settings`.

> In that case it wouldnt match `llvm::SetVector`.

Maybe such classes should be explicitly be specified.

I am not a fan of including classes which a regular users does not come across 
i.e. `llvm::`. Even including ``boost::` specific stuff feels like a stretch to 
me. But let's not get too off-topic and wait for other people to chime in.

Making it so broad might even affect the performance but let's wait for the 
numbers first. I will build it locally an give it a spin with the next few 
days. (hopefully)

https://github.com/llvm/llvm-project/pull/125420
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [clang-tidy] Add performance-redundant-lookup check (PR #125420)

2025-02-02 Thread Balazs Benics via cfe-commits


@@ -0,0 +1,147 @@
+.. title:: clang-tidy - performance-redundant-lookup
+
+performance-redundant-lookup
+
+
+This check warns about potential redundant container lookup operations within
+a function.
+
+Examples
+
+
+.. code-block:: c++
+
+if (map.count(key) && map[key] < threshold) {
+  // do stuff
+}
+
+In this example, we would check if the key is present in the map,
+and then do a second lookup to actually load the value.
+We could refactor the code into this, to use a single lookup:
+
+.. code-block:: c++
+
+if (auto it = map.find(key); it != map.end() && it->second < threshold) {
+  // do stuff
+}
+
+In this example, we do three lookups while calculating, caching and then
+using the result of some expensive computation:
+
+.. code-block:: c++
+
+if (!cache.contains(key)) {
+cache[key] = computeExpensiveValue();
+}
+use(cache[key]);
+
+We could refactor this code using ``try_emplace`` to fill up the cache entry
+if wasn't present, and just use it if we already computed the value.
+This way we would only have a single unavoidable lookup:
+
+.. code-block:: c++
+
+auto [cacheSlot, inserted] cache.try_emplace(key);
+if (inserted) {
+cacheSlot->second = computeExpensiveValue();
+}
+use(cacheSlot->second);
+
+
+What is a "lookup"?
+---
+
+All container operations that walk the internal structure of the container
+should be considered as "lookups".
+This means that checking if an element is present or inserting an element
+is also considered as a "lookup".
+
+For example, ``contains``, ``count`` but even the ``operator[]``
+should be considered as "lookups".
+
+Technically ``insert``, ``emplace`` or ``try_emplace`` are also lookups,
+even if due to limitations, they are not recognized as such.
+
+Lookups inside macros are ignored, thus not considered as "lookups".
+For example:
+
+.. code-block:: c++
+
+assert(map.count(key) == 0); // Not considered as a "lookup".
+
+Options
+---
+
+.. option:: ContainerNameRegex
+
+   The regular expression matching the type of the container objects.
+   This is matched in a case insensitive manner.
+   Default is ``set|map``.

steakhal wrote:

I intentionally left it matching on anything that has `map` or `set` spelled. 
This is to also match `DenseMap` `DenseSet` of `llvm` for instance.

https://github.com/llvm/llvm-project/pull/125420
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [clang-tidy] Add performance-redundant-lookup check (PR #125420)

2025-02-02 Thread Balazs Benics via cfe-commits


@@ -0,0 +1,147 @@
+.. title:: clang-tidy - performance-redundant-lookup
+
+performance-redundant-lookup
+
+
+This check warns about potential redundant container lookup operations within
+a function.
+
+Examples
+
+
+.. code-block:: c++
+
+if (map.count(key) && map[key] < threshold) {
+  // do stuff
+}
+
+In this example, we would check if the key is present in the map,
+and then do a second lookup to actually load the value.
+We could refactor the code into this, to use a single lookup:
+
+.. code-block:: c++
+
+if (auto it = map.find(key); it != map.end() && it->second < threshold) {
+  // do stuff
+}
+
+In this example, we do three lookups while calculating, caching and then
+using the result of some expensive computation:
+
+.. code-block:: c++
+
+if (!cache.contains(key)) {
+cache[key] = computeExpensiveValue();
+}
+use(cache[key]);
+
+We could refactor this code using ``try_emplace`` to fill up the cache entry
+if wasn't present, and just use it if we already computed the value.
+This way we would only have a single unavoidable lookup:
+
+.. code-block:: c++
+
+auto [cacheSlot, inserted] cache.try_emplace(key);
+if (inserted) {
+cacheSlot->second = computeExpensiveValue();
+}
+use(cacheSlot->second);
+
+
+What is a "lookup"?
+---
+
+All container operations that walk the internal structure of the container
+should be considered as "lookups".
+This means that checking if an element is present or inserting an element
+is also considered as a "lookup".
+
+For example, ``contains``, ``count`` but even the ``operator[]``
+should be considered as "lookups".
+
+Technically ``insert``, ``emplace`` or ``try_emplace`` are also lookups,
+even if due to limitations, they are not recognized as such.
+
+Lookups inside macros are ignored, thus not considered as "lookups".
+For example:
+
+.. code-block:: c++
+
+assert(map.count(key) == 0); // Not considered as a "lookup".
+
+Options
+---
+
+.. option:: ContainerNameRegex
+
+   The regular expression matching the type of the container objects.
+   This is matched in a case insensitive manner.
+   Default is ``set|map``.

steakhal wrote:

In that case it wouldnt match `llvm::SetVector`.

https://github.com/llvm/llvm-project/pull/125420
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [clang-tidy] Add performance-redundant-lookup check (PR #125420)

2025-02-02 Thread Oliver Stöneberg via cfe-commits


@@ -0,0 +1,147 @@
+.. title:: clang-tidy - performance-redundant-lookup
+
+performance-redundant-lookup
+
+
+This check warns about potential redundant container lookup operations within
+a function.
+
+Examples
+
+
+.. code-block:: c++
+
+if (map.count(key) && map[key] < threshold) {
+  // do stuff
+}
+
+In this example, we would check if the key is present in the map,
+and then do a second lookup to actually load the value.
+We could refactor the code into this, to use a single lookup:
+
+.. code-block:: c++
+
+if (auto it = map.find(key); it != map.end() && it->second < threshold) {
+  // do stuff
+}
+
+In this example, we do three lookups while calculating, caching and then
+using the result of some expensive computation:
+
+.. code-block:: c++
+
+if (!cache.contains(key)) {
+cache[key] = computeExpensiveValue();
+}
+use(cache[key]);
+
+We could refactor this code using ``try_emplace`` to fill up the cache entry
+if wasn't present, and just use it if we already computed the value.
+This way we would only have a single unavoidable lookup:
+
+.. code-block:: c++
+
+auto [cacheSlot, inserted] cache.try_emplace(key);
+if (inserted) {
+cacheSlot->second = computeExpensiveValue();
+}
+use(cacheSlot->second);
+
+
+What is a "lookup"?
+---
+
+All container operations that walk the internal structure of the container
+should be considered as "lookups".
+This means that checking if an element is present or inserting an element
+is also considered as a "lookup".
+
+For example, ``contains``, ``count`` but even the ``operator[]``
+should be considered as "lookups".
+
+Technically ``insert``, ``emplace`` or ``try_emplace`` are also lookups,
+even if due to limitations, they are not recognized as such.
+
+Lookups inside macros are ignored, thus not considered as "lookups".
+For example:
+
+.. code-block:: c++
+
+assert(map.count(key) == 0); // Not considered as a "lookup".
+
+Options
+---
+
+.. option:: ContainerNameRegex
+
+   The regular expression matching the type of the container objects.
+   This is matched in a case insensitive manner.
+   Default is ``set|map``.

firewave wrote:

Maybe at least make it `set$|map$` so it only matches at the end of the string 
instead of random occurrences.

https://github.com/llvm/llvm-project/pull/125420
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [clang-tidy] Add performance-redundant-lookup check (PR #125420)

2025-02-02 Thread Balazs Benics via cfe-commits

steakhal wrote:

> Could you please run this with `--enable-check-profile` to see how heavy it 
> is?

I plan to re-run it on clang soon, and share the results.

https://github.com/llvm/llvm-project/pull/125420
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [clang-tidy] Add performance-redundant-lookup check (PR #125420)

2025-02-02 Thread Oliver Stöneberg via cfe-commits

firewave wrote:

Could you please run this with `--enable-check-profile` to see how heavy it is?

https://github.com/llvm/llvm-project/pull/125420
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [clang-tidy] Add performance-redundant-lookup check (PR #125420)

2025-02-02 Thread via cfe-commits


@@ -0,0 +1,147 @@
+.. title:: clang-tidy - performance-redundant-lookup
+
+performance-redundant-lookup
+
+
+This check warns about potential redundant container lookup operations within
+a function.
+
+Examples
+
+
+.. code-block:: c++
+
+if (map.count(key) && map[key] < threshold) {
+  // do stuff
+}
+
+In this example, we would check if the key is present in the map,
+and then do a second lookup to actually load the value.
+We could refactor the code into this, to use a single lookup:
+
+.. code-block:: c++
+
+if (auto it = map.find(key); it != map.end() && it->second < threshold) {
+  // do stuff
+}
+
+In this example, we do three lookups while calculating, caching and then
+using the result of some expensive computation:
+
+.. code-block:: c++
+
+if (!cache.contains(key)) {
+cache[key] = computeExpensiveValue();
+}
+use(cache[key]);
+
+We could refactor this code using ``try_emplace`` to fill up the cache entry
+if wasn't present, and just use it if we already computed the value.
+This way we would only have a single unavoidable lookup:
+
+.. code-block:: c++
+
+auto [cacheSlot, inserted] cache.try_emplace(key);
+if (inserted) {
+cacheSlot->second = computeExpensiveValue();
+}
+use(cacheSlot->second);
+
+
+What is a "lookup"?
+---
+
+All container operations that walk the internal structure of the container
+should be considered as "lookups".
+This means that checking if an element is present or inserting an element
+is also considered as a "lookup".
+
+For example, ``contains``, ``count`` but even the ``operator[]``
+should be considered as "lookups".
+
+Technically ``insert``, ``emplace`` or ``try_emplace`` are also lookups,
+even if due to limitations, they are not recognized as such.
+
+Lookups inside macros are ignored, thus not considered as "lookups".
+For example:
+
+.. code-block:: c++
+
+assert(map.count(key) == 0); // Not considered as a "lookup".
+
+Options
+---
+
+.. option:: ContainerNameRegex
+
+   The regular expression matching the type of the container objects.
+   This is matched in a case insensitive manner.
+   Default is ``set|map``.

EugeneZelenko wrote:

How about `boost`?

https://github.com/llvm/llvm-project/pull/125420
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [clang-tidy] Add performance-redundant-lookup check (PR #125420)

2025-02-02 Thread Oliver Stöneberg via cfe-commits


@@ -0,0 +1,147 @@
+.. title:: clang-tidy - performance-redundant-lookup
+
+performance-redundant-lookup
+
+
+This check warns about potential redundant container lookup operations within
+a function.
+
+Examples
+
+
+.. code-block:: c++
+
+if (map.count(key) && map[key] < threshold) {
+  // do stuff
+}
+
+In this example, we would check if the key is present in the map,
+and then do a second lookup to actually load the value.
+We could refactor the code into this, to use a single lookup:
+
+.. code-block:: c++
+
+if (auto it = map.find(key); it != map.end() && it->second < threshold) {
+  // do stuff
+}
+
+In this example, we do three lookups while calculating, caching and then
+using the result of some expensive computation:
+
+.. code-block:: c++
+
+if (!cache.contains(key)) {
+cache[key] = computeExpensiveValue();
+}
+use(cache[key]);
+
+We could refactor this code using ``try_emplace`` to fill up the cache entry
+if wasn't present, and just use it if we already computed the value.
+This way we would only have a single unavoidable lookup:
+
+.. code-block:: c++
+
+auto [cacheSlot, inserted] cache.try_emplace(key);
+if (inserted) {
+cacheSlot->second = computeExpensiveValue();
+}
+use(cacheSlot->second);
+
+
+What is a "lookup"?
+---
+
+All container operations that walk the internal structure of the container
+should be considered as "lookups".
+This means that checking if an element is present or inserting an element
+is also considered as a "lookup".
+
+For example, ``contains``, ``count`` but even the ``operator[]``
+should be considered as "lookups".
+
+Technically ``insert``, ``emplace`` or ``try_emplace`` are also lookups,
+even if due to limitations, they are not recognized as such.
+
+Lookups inside macros are ignored, thus not considered as "lookups".
+For example:
+
+.. code-block:: c++
+
+assert(map.count(key) == 0); // Not considered as a "lookup".
+
+Options
+---
+
+.. option:: ContainerNameRegex
+
+   The regular expression matching the type of the container objects.
+   This is matched in a case insensitive manner.
+   Default is ``set|map``.

firewave wrote:

That would also match `{flat_|unordered_}{multi}{set|map}` from the standard 
which appear to have similar interfaces which should be fine. But maybe it 
should be limited to the `std` namespace so it does not apply to any 
implementation.

And it would also match an object like `class my_mmap`.

https://github.com/llvm/llvm-project/pull/125420
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [clang-tidy] Add performance-redundant-lookup check (PR #125420)

2025-02-02 Thread via cfe-commits


@@ -0,0 +1,159 @@
+//===--- RedundantLookupCheck.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 "RedundantLookupCheck.h"
+#include "../utils/OptionsUtils.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Regex.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::performance {
+
+static constexpr auto DefaultContainerNameRegex = "set|map";
+
+static const llvm::StringRef DefaultLookupMethodNames =
+llvm::StringLiteral( //
+"at;"
+"contains;"
+"count;"
+"find_as;"
+"find;"
+// These are tricky, as they take the "key" at different places.
+// They sometimes bundle up the key and the value together in a pair.
+//   "emplace;"
+//   "insert_or_assign;"
+//   "insert;"
+//   "try_emplace;"
+)
+.drop_back(); // Drops the last semicolon.
+
+RedundantLookupCheck::RedundantLookupCheck(StringRef Name,
+   ClangTidyContext *Context)
+: ClangTidyCheck(Name, Context),
+  ContainerNameRegex(
+  Options.get("ContainerNameRegex", DefaultContainerNameRegex)),
+  LookupMethodNames(utils::options::parseStringList(
+  Options.get("LookupMethodNames", DefaultLookupMethodNames))) {}
+
+void RedundantLookupCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "ContainerNameRegex", ContainerNameRegex);
+  Options.store(Opts, "LookupMethodNames",
+utils::options::serializeStringList(LookupMethodNames));
+}
+
+namespace {
+/// Checks if any of the ends of the source range is in a macro expansion.
+AST_MATCHER(Expr, hasMacroSourceRange) {
+  SourceRange R = Node.getSourceRange();
+  return R.getBegin().isMacroID() || R.getEnd().isMacroID();
+}
+} // namespace
+
+static constexpr const char *ObjKey = "obj";
+static constexpr const char *LookupKey = "key";
+static constexpr const char *LookupCallKey = "lookup";
+static constexpr const char *EnclosingFnKey = "fn";
+
+void RedundantLookupCheck::registerMatchers(MatchFinder *Finder) {
+  auto MatchesContainerNameRegex =
+  matchesName(ContainerNameRegex, llvm::Regex::IgnoreCase);
+
+  // Match that the expression is a record type with a name that contains "map"
+  // or "set".
+  auto RecordCalledMapOrSet =
+  expr(ignoringImpCasts(hasType(hasUnqualifiedDesugaredType(recordType(
+   hasDeclaration(namedDecl(MatchesContainerNameRegex)))
+  .bind(ObjKey);
+
+  auto SubscriptCall =
+  cxxOperatorCallExpr(hasOverloadedOperatorName("[]"), argumentCountIs(2),
+  hasArgument(0, RecordCalledMapOrSet),
+  hasArgument(1, expr().bind(LookupKey)));
+
+  auto LookupMethodCalls =
+  cxxMemberCallExpr(on(RecordCalledMapOrSet), argumentCountIs(1),
+hasArgument(0, expr().bind(LookupKey)),
+callee(cxxMethodDecl(hasAnyName(LookupMethodNames;
+
+  // Match any lookup or subscript calls that are not in a macro expansion.
+  auto AnyLookup = callExpr(unless(hasMacroSourceRange()),
+anyOf(SubscriptCall, LookupMethodCalls))
+   .bind(LookupCallKey);
+
+  // We need to collect all lookups in a function to be able to report them in
+  // batches.
+  Finder->addMatcher(
+  functionDecl(hasBody(compoundStmt(forEachDescendant(AnyLookup
+  .bind(EnclosingFnKey),
+  this);
+}
+
+/// Hash the container object expr along with the key used for lookup and the
+/// enclosing function together.
+static unsigned hashLookupEvent(const ASTContext &Ctx,
+const FunctionDecl *EnclosingFn,
+const Expr *LookupKey,
+const Expr *ContainerObject) {
+  llvm::FoldingSetNodeID ID;
+  ID.AddPointer(EnclosingFn);
+
+  LookupKey->Profile(ID, Ctx, /*Canonical=*/true,
+ /*ProfileLambdaExpr=*/true);
+  ContainerObject->Profile(ID, Ctx, /*Canonical=*/true,
+   /*ProfileLambdaExpr=*/true);
+  return ID.ComputeHash();
+}
+
+void RedundantLookupCheck::check(const MatchFinder::MatchResult &Result) {
+  SM = Result.SourceManager;
+
+  const auto *EnclosingFn =
+  Result.Nodes.getNodeAs(EnclosingFnKey);
+  const auto *LookupCall = Result.Nodes.getNodeAs(LookupCallKey);
+  const auto *Key = Result.Nodes.getNodeAs(LookupKey);
+  const auto *ContainerObject = Result.Nodes.getNodeAs(ObjKey);

[clang-tools-extra] [clang-tidy] Add performance-redundant-lookup check (PR #125420)

2025-02-02 Thread Balazs Benics via cfe-commits

https://github.com/steakhal updated 
https://github.com/llvm/llvm-project/pull/125420

>From 78390b2af01fcd5acfbd2a313852962c873e5611 Mon Sep 17 00:00:00 2001
From: Balazs Benics 
Date: Sun, 2 Feb 2025 17:35:39 +0100
Subject: [PATCH 1/6] [clang-tidy] Add performance-redundant-lookup check

Finds redundant lookups to set|map containers.
For example: `map.count(key) && map[key] < threshold`
---
 .../clang-tidy/performance/CMakeLists.txt |   1 +
 .../performance/PerformanceTidyModule.cpp |   3 +
 .../performance/RedundantLookupCheck.cpp  | 159 ++
 .../performance/RedundantLookupCheck.h|  46 +
 clang-tools-extra/docs/ReleaseNotes.rst   |   5 +
 .../docs/clang-tidy/checks/list.rst   |   1 +
 .../checks/performance/redundant-lookup.rst   | 147 
 .../checkers/performance/redundant-lookup.cpp | 142 
 8 files changed, 504 insertions(+)
 create mode 100644 
clang-tools-extra/clang-tidy/performance/RedundantLookupCheck.cpp
 create mode 100644 
clang-tools-extra/clang-tidy/performance/RedundantLookupCheck.h
 create mode 100644 
clang-tools-extra/docs/clang-tidy/checks/performance/redundant-lookup.rst
 create mode 100644 
clang-tools-extra/test/clang-tidy/checkers/performance/redundant-lookup.cpp

diff --git a/clang-tools-extra/clang-tidy/performance/CMakeLists.txt 
b/clang-tools-extra/clang-tidy/performance/CMakeLists.txt
index c6e547c5089fb0e..6f907852c9fd1c4 100644
--- a/clang-tools-extra/clang-tidy/performance/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/performance/CMakeLists.txt
@@ -21,6 +21,7 @@ add_clang_library(clangTidyPerformanceModule STATIC
   NoexceptMoveConstructorCheck.cpp
   NoexceptSwapCheck.cpp
   PerformanceTidyModule.cpp
+  RedundantLookupCheck.cpp
   TriviallyDestructibleCheck.cpp
   TypePromotionInMathFnCheck.cpp
   UnnecessaryCopyInitialization.cpp
diff --git a/clang-tools-extra/clang-tidy/performance/PerformanceTidyModule.cpp 
b/clang-tools-extra/clang-tidy/performance/PerformanceTidyModule.cpp
index 9e0fa6f88b36a00..a613114e6b83bd3 100644
--- a/clang-tools-extra/clang-tidy/performance/PerformanceTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/performance/PerformanceTidyModule.cpp
@@ -24,6 +24,7 @@
 #include "NoexceptDestructorCheck.h"
 #include "NoexceptMoveConstructorCheck.h"
 #include "NoexceptSwapCheck.h"
+#include "RedundantLookupCheck.h"
 #include "TriviallyDestructibleCheck.h"
 #include "TypePromotionInMathFnCheck.h"
 #include "UnnecessaryCopyInitialization.h"
@@ -62,6 +63,8 @@ class PerformanceModule : public ClangTidyModule {
 "performance-noexcept-move-constructor");
 CheckFactories.registerCheck(
 "performance-noexcept-swap");
+CheckFactories.registerCheck(
+"performance-redundant-lookup");
 CheckFactories.registerCheck(
 "performance-trivially-destructible");
 CheckFactories.registerCheck(
diff --git a/clang-tools-extra/clang-tidy/performance/RedundantLookupCheck.cpp 
b/clang-tools-extra/clang-tidy/performance/RedundantLookupCheck.cpp
new file mode 100644
index 000..fefb28682c5b8af
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/performance/RedundantLookupCheck.cpp
@@ -0,0 +1,159 @@
+//===--- RedundantLookupCheck.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 "RedundantLookupCheck.h"
+#include "../utils/OptionsUtils.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Regex.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::performance {
+
+static constexpr auto DefaultContainerNameRegex = "set|map";
+
+static const llvm::StringRef DefaultLookupMethodNames =
+llvm::StringLiteral( //
+"at;"
+"contains;"
+"count;"
+"find_as;"
+"find;"
+// These are tricky, as they take the "key" at different places.
+// They sometimes bundle up the key and the value together in a pair.
+//   "emplace;"
+//   "insert_or_assign;"
+//   "insert;"
+//   "try_emplace;"
+)
+.drop_back(); // Drops the last semicolon.
+
+RedundantLookupCheck::RedundantLookupCheck(StringRef Name,
+   ClangTidyContext *Context)
+: ClangTidyCheck(Name, Context),
+  ContainerNameRegex(
+  Options.get("ContainerNameRegex", DefaultContainerNameRegex)),
+  LookupMethodNames(utils::options::parseStringList(
+  Options.get("LookupMethodNames", DefaultLookupMethodNames))) {}
+
+void RedundantLookupCheck::storeOptions(ClangTidy

[clang-tools-extra] [clang-tidy] Add performance-redundant-lookup check (PR #125420)

2025-02-02 Thread Balazs Benics via cfe-commits

https://github.com/steakhal updated 
https://github.com/llvm/llvm-project/pull/125420

>From 78390b2af01fcd5acfbd2a313852962c873e5611 Mon Sep 17 00:00:00 2001
From: Balazs Benics 
Date: Sun, 2 Feb 2025 17:35:39 +0100
Subject: [PATCH 1/5] [clang-tidy] Add performance-redundant-lookup check

Finds redundant lookups to set|map containers.
For example: `map.count(key) && map[key] < threshold`
---
 .../clang-tidy/performance/CMakeLists.txt |   1 +
 .../performance/PerformanceTidyModule.cpp |   3 +
 .../performance/RedundantLookupCheck.cpp  | 159 ++
 .../performance/RedundantLookupCheck.h|  46 +
 clang-tools-extra/docs/ReleaseNotes.rst   |   5 +
 .../docs/clang-tidy/checks/list.rst   |   1 +
 .../checks/performance/redundant-lookup.rst   | 147 
 .../checkers/performance/redundant-lookup.cpp | 142 
 8 files changed, 504 insertions(+)
 create mode 100644 
clang-tools-extra/clang-tidy/performance/RedundantLookupCheck.cpp
 create mode 100644 
clang-tools-extra/clang-tidy/performance/RedundantLookupCheck.h
 create mode 100644 
clang-tools-extra/docs/clang-tidy/checks/performance/redundant-lookup.rst
 create mode 100644 
clang-tools-extra/test/clang-tidy/checkers/performance/redundant-lookup.cpp

diff --git a/clang-tools-extra/clang-tidy/performance/CMakeLists.txt 
b/clang-tools-extra/clang-tidy/performance/CMakeLists.txt
index c6e547c5089fb0e..6f907852c9fd1c4 100644
--- a/clang-tools-extra/clang-tidy/performance/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/performance/CMakeLists.txt
@@ -21,6 +21,7 @@ add_clang_library(clangTidyPerformanceModule STATIC
   NoexceptMoveConstructorCheck.cpp
   NoexceptSwapCheck.cpp
   PerformanceTidyModule.cpp
+  RedundantLookupCheck.cpp
   TriviallyDestructibleCheck.cpp
   TypePromotionInMathFnCheck.cpp
   UnnecessaryCopyInitialization.cpp
diff --git a/clang-tools-extra/clang-tidy/performance/PerformanceTidyModule.cpp 
b/clang-tools-extra/clang-tidy/performance/PerformanceTidyModule.cpp
index 9e0fa6f88b36a00..a613114e6b83bd3 100644
--- a/clang-tools-extra/clang-tidy/performance/PerformanceTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/performance/PerformanceTidyModule.cpp
@@ -24,6 +24,7 @@
 #include "NoexceptDestructorCheck.h"
 #include "NoexceptMoveConstructorCheck.h"
 #include "NoexceptSwapCheck.h"
+#include "RedundantLookupCheck.h"
 #include "TriviallyDestructibleCheck.h"
 #include "TypePromotionInMathFnCheck.h"
 #include "UnnecessaryCopyInitialization.h"
@@ -62,6 +63,8 @@ class PerformanceModule : public ClangTidyModule {
 "performance-noexcept-move-constructor");
 CheckFactories.registerCheck(
 "performance-noexcept-swap");
+CheckFactories.registerCheck(
+"performance-redundant-lookup");
 CheckFactories.registerCheck(
 "performance-trivially-destructible");
 CheckFactories.registerCheck(
diff --git a/clang-tools-extra/clang-tidy/performance/RedundantLookupCheck.cpp 
b/clang-tools-extra/clang-tidy/performance/RedundantLookupCheck.cpp
new file mode 100644
index 000..fefb28682c5b8af
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/performance/RedundantLookupCheck.cpp
@@ -0,0 +1,159 @@
+//===--- RedundantLookupCheck.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 "RedundantLookupCheck.h"
+#include "../utils/OptionsUtils.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Regex.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::performance {
+
+static constexpr auto DefaultContainerNameRegex = "set|map";
+
+static const llvm::StringRef DefaultLookupMethodNames =
+llvm::StringLiteral( //
+"at;"
+"contains;"
+"count;"
+"find_as;"
+"find;"
+// These are tricky, as they take the "key" at different places.
+// They sometimes bundle up the key and the value together in a pair.
+//   "emplace;"
+//   "insert_or_assign;"
+//   "insert;"
+//   "try_emplace;"
+)
+.drop_back(); // Drops the last semicolon.
+
+RedundantLookupCheck::RedundantLookupCheck(StringRef Name,
+   ClangTidyContext *Context)
+: ClangTidyCheck(Name, Context),
+  ContainerNameRegex(
+  Options.get("ContainerNameRegex", DefaultContainerNameRegex)),
+  LookupMethodNames(utils::options::parseStringList(
+  Options.get("LookupMethodNames", DefaultLookupMethodNames))) {}
+
+void RedundantLookupCheck::storeOptions(ClangTidy

[clang-tools-extra] [clang-tidy] Add performance-redundant-lookup check (PR #125420)

2025-02-02 Thread Balazs Benics via cfe-commits


@@ -0,0 +1,147 @@
+.. title:: clang-tidy - performance-redundant-lookup
+
+performance-redundant-lookup
+
+
+This check warns about potential redundant container lookup operations within
+a function.
+
+Examples
+
+
+.. code-block:: c++
+
+if (map.count(key) && map[key] < threshold) {
+  // do stuff
+}
+
+In this example, we would check if the key is present in the map,
+and then do a second lookup to actually load the value.
+We could refactor the code into this, to use a single lookup:
+
+.. code-block:: c++
+
+if (auto it = map.find(key); it != map.end() && it->second < threshold) {
+  // do stuff
+}
+
+In this example, we do three lookups while calculating, caching and then
+using the result of some expensive computation:
+
+.. code-block:: c++
+
+if (!cache.contains(key)) {
+cache[key] = computeExpensiveValue();
+}
+use(cache[key]);
+
+We could refactor this code using ``try_emplace`` to fill up the cache entry
+if wasn't present, and just use it if we already computed the value.
+This way we would only have a single unavoidable lookup:
+
+.. code-block:: c++
+
+auto [cacheSlot, inserted] cache.try_emplace(key);
+if (inserted) {
+cacheSlot->second = computeExpensiveValue();
+}
+use(cacheSlot->second);
+
+
+What is a "lookup"?
+---
+
+All container operations that walk the internal structure of the container
+should be considered as "lookups".
+This means that checking if an element is present or inserting an element
+is also considered as a "lookup".
+
+For example, ``contains``, ``count`` but even the ``operator[]``
+should be considered as "lookups".
+
+Technically ``insert``, ``emplace`` or ``try_emplace`` are also lookups,
+even if due to limitations, they are not recognized as such.
+
+Lookups inside macros are ignored, thus not considered as "lookups".
+For example:
+
+.. code-block:: c++
+
+assert(map.count(key) == 0); // Not considered as a "lookup".
+
+Options
+---
+
+.. option:: ContainerNameRegex
+
+   The regular expression matching the type of the container objects.
+   This is matched in a case insensitive manner.
+   Default is ``set|map``.

steakhal wrote:

> How about unordered_map/unordered_set?

They would match the `set|map` case-insensitive regex.

https://github.com/llvm/llvm-project/pull/125420
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [clang-tidy] Add performance-redundant-lookup check (PR #125420)

2025-02-02 Thread via cfe-commits


@@ -0,0 +1,147 @@
+.. title:: clang-tidy - performance-redundant-lookup
+
+performance-redundant-lookup
+
+
+This check warns about potential redundant container lookup operations within
+a function.
+
+Examples
+
+
+.. code-block:: c++
+
+if (map.count(key) && map[key] < threshold) {
+  // do stuff
+}
+
+In this example, we would check if the key is present in the map,
+and then do a second lookup to actually load the value.
+We could refactor the code into this, to use a single lookup:
+
+.. code-block:: c++
+
+if (auto it = map.find(key); it != map.end() && it->second < threshold) {
+  // do stuff
+}
+
+In this example, we do three lookups while calculating, caching and then
+using the result of some expensive computation:
+
+.. code-block:: c++
+
+if (!cache.contains(key)) {
+cache[key] = computeExpensiveValue();
+}
+use(cache[key]);
+
+We could refactor this code using ``try_emplace`` to fill up the cache entry
+if wasn't present, and just use it if we already computed the value.
+This way we would only have a single unavoidable lookup:
+
+.. code-block:: c++
+
+auto [cacheSlot, inserted] cache.try_emplace(key);
+if (inserted) {
+cacheSlot->second = computeExpensiveValue();
+}
+use(cacheSlot->second);
+
+
+What is a "lookup"?
+---
+
+All container operations that walk the internal structure of the container
+should be considered as "lookups".
+This means that checking if an element is present or inserting an element
+is also considered as a "lookup".
+
+For example, ``contains``, ``count`` but even the ``operator[]``
+should be considered as "lookups".
+
+Technically ``insert``, ``emplace`` or ``try_emplace`` are also lookups,
+even if due to limitations, they are not recognized as such.
+
+Lookups inside macros are ignored, thus not considered as "lookups".
+For example:
+
+.. code-block:: c++
+
+assert(map.count(key) == 0); // Not considered as a "lookup".
+
+Options
+---
+
+.. option:: ContainerNameRegex
+
+   The regular expression matching the type of the container objects.
+   This is matched in a case insensitive manner.
+   Default is ``set|map``.
+
+.. option:: LookupMethodNames
+
+   Member function names to consider as **lookup** operation.
+   These methods must have exactly 1 argument.
+   Default is ``at;contains;count;find_as;find``.
+
+Limitations
+---
+
+ - The "redundant lookups" may span across a large chunk of code.
+   Such reports can be considered as false-positives because it's hard to judge
+   if the container is definitely not mutated between the lookups.
+   It would be hard to split the lookup groups in a stable and meaningful way,
+   and a threshold for proximity would be just an arbitrary limit.
+
+ - The "redundant lookups" may span across different control-flow constructs,
+   making it impossible to refactor.
+   It may be that the code was deliberately structured like it was, thus the
+   report is considered a false-positive.
+   Use your best judgement to see if anything needs to be fixed or not.
+   For example:
+
+   .. code-block:: c++
+
+if (coin())
+map[key] = foo();
+else
+map[key] = bar();
+
+   Could be refactored into:
+
+   .. code-block:: c++
+
+map[key] = coin() ? foo() : bar();
+
+   However, the following code could be considered intentional:
+
+   .. code-block:: c++
+
+// Handle the likely case.
+if (auto it = map.find(key); it != map.end()) {
+return process(*it);
+}
+
+// Commit the dirty items, and check again.
+for (const auto &item : dirtyList) {
+commit(item, map); // Updates the "map".
+}
+
+// Do a final check.
+if (auto it = map.find(key); it != map.end()) {
+return process(*it);
+}
+
+ - The key argument of a lookup may have sideffects. Sideffects are ignored 
when identifying lookups.
+   This can introduce some false-positives. For example:
+
+   .. code-block:: c++
+
+m.contains(rng(++n));
+m.contains(rng(++n)); // FP: This is considered a redundant lookup.
+
+ - Lookup member functions must have exactly 1 argument to match.
+   There are technically lookup functions, such as ``insert`` or 
``try_emplace``,
+   but it would be hard to identify the "key" part of the argument,
+   while leaving the implementation open for user-configuration via the
+   ``LookupMethodNames`` option.

EugeneZelenko wrote:

```suggestion
   `LookupMethodNames` option.
```

https://github.com/llvm/llvm-project/pull/125420
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [clang-tidy] Add performance-redundant-lookup check (PR #125420)

2025-02-02 Thread via cfe-commits


@@ -0,0 +1,147 @@
+.. title:: clang-tidy - performance-redundant-lookup
+
+performance-redundant-lookup
+
+
+This check warns about potential redundant container lookup operations within
+a function.
+
+Examples
+
+
+.. code-block:: c++
+
+if (map.count(key) && map[key] < threshold) {
+  // do stuff
+}
+
+In this example, we would check if the key is present in the map,
+and then do a second lookup to actually load the value.
+We could refactor the code into this, to use a single lookup:
+
+.. code-block:: c++
+
+if (auto it = map.find(key); it != map.end() && it->second < threshold) {
+  // do stuff
+}
+
+In this example, we do three lookups while calculating, caching and then
+using the result of some expensive computation:
+
+.. code-block:: c++
+
+if (!cache.contains(key)) {
+cache[key] = computeExpensiveValue();
+}
+use(cache[key]);
+
+We could refactor this code using ``try_emplace`` to fill up the cache entry
+if wasn't present, and just use it if we already computed the value.
+This way we would only have a single unavoidable lookup:
+
+.. code-block:: c++
+
+auto [cacheSlot, inserted] cache.try_emplace(key);
+if (inserted) {
+cacheSlot->second = computeExpensiveValue();
+}
+use(cacheSlot->second);
+
+
+What is a "lookup"?
+---
+
+All container operations that walk the internal structure of the container
+should be considered as "lookups".
+This means that checking if an element is present or inserting an element
+is also considered as a "lookup".
+
+For example, ``contains``, ``count`` but even the ``operator[]``
+should be considered as "lookups".
+
+Technically ``insert``, ``emplace`` or ``try_emplace`` are also lookups,
+even if due to limitations, they are not recognized as such.
+
+Lookups inside macros are ignored, thus not considered as "lookups".
+For example:
+
+.. code-block:: c++
+
+assert(map.count(key) == 0); // Not considered as a "lookup".
+
+Options
+---
+
+.. option:: ContainerNameRegex
+
+   The regular expression matching the type of the container objects.
+   This is matched in a case insensitive manner.
+   Default is ``set|map``.

EugeneZelenko wrote:

```suggestion
   Default is `set|map`.
```

Please use single back-ticks for option names and values.

How about `unordered_map/unordered_set`?

https://github.com/llvm/llvm-project/pull/125420
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [clang-tidy] Add performance-redundant-lookup check (PR #125420)

2025-02-02 Thread via cfe-commits


@@ -0,0 +1,147 @@
+.. title:: clang-tidy - performance-redundant-lookup
+
+performance-redundant-lookup
+
+
+This check warns about potential redundant container lookup operations within
+a function.
+
+Examples
+
+
+.. code-block:: c++
+
+if (map.count(key) && map[key] < threshold) {
+  // do stuff
+}
+
+In this example, we would check if the key is present in the map,
+and then do a second lookup to actually load the value.
+We could refactor the code into this, to use a single lookup:
+
+.. code-block:: c++
+
+if (auto it = map.find(key); it != map.end() && it->second < threshold) {
+  // do stuff
+}
+
+In this example, we do three lookups while calculating, caching and then
+using the result of some expensive computation:
+
+.. code-block:: c++
+
+if (!cache.contains(key)) {
+cache[key] = computeExpensiveValue();
+}
+use(cache[key]);
+
+We could refactor this code using ``try_emplace`` to fill up the cache entry
+if wasn't present, and just use it if we already computed the value.
+This way we would only have a single unavoidable lookup:
+
+.. code-block:: c++
+
+auto [cacheSlot, inserted] cache.try_emplace(key);
+if (inserted) {
+cacheSlot->second = computeExpensiveValue();
+}
+use(cacheSlot->second);
+
+
+What is a "lookup"?
+---
+
+All container operations that walk the internal structure of the container
+should be considered as "lookups".
+This means that checking if an element is present or inserting an element
+is also considered as a "lookup".
+
+For example, ``contains``, ``count`` but even the ``operator[]``
+should be considered as "lookups".
+
+Technically ``insert``, ``emplace`` or ``try_emplace`` are also lookups,
+even if due to limitations, they are not recognized as such.
+
+Lookups inside macros are ignored, thus not considered as "lookups".
+For example:
+
+.. code-block:: c++
+
+assert(map.count(key) == 0); // Not considered as a "lookup".
+
+Options
+---
+
+.. option:: ContainerNameRegex
+
+   The regular expression matching the type of the container objects.
+   This is matched in a case insensitive manner.
+   Default is ``set|map``.
+
+.. option:: LookupMethodNames
+
+   Member function names to consider as **lookup** operation.
+   These methods must have exactly 1 argument.
+   Default is ``at;contains;count;find_as;find``.

EugeneZelenko wrote:

```suggestion
   Default is `at;contains;count;find_as;find`.
```

https://github.com/llvm/llvm-project/pull/125420
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [clang-tidy] Add performance-redundant-lookup check (PR #125420)

2025-02-02 Thread Balazs Benics via cfe-commits

https://github.com/steakhal updated 
https://github.com/llvm/llvm-project/pull/125420

>From 78390b2af01fcd5acfbd2a313852962c873e5611 Mon Sep 17 00:00:00 2001
From: Balazs Benics 
Date: Sun, 2 Feb 2025 17:35:39 +0100
Subject: [PATCH 1/4] [clang-tidy] Add performance-redundant-lookup check

Finds redundant lookups to set|map containers.
For example: `map.count(key) && map[key] < threshold`
---
 .../clang-tidy/performance/CMakeLists.txt |   1 +
 .../performance/PerformanceTidyModule.cpp |   3 +
 .../performance/RedundantLookupCheck.cpp  | 159 ++
 .../performance/RedundantLookupCheck.h|  46 +
 clang-tools-extra/docs/ReleaseNotes.rst   |   5 +
 .../docs/clang-tidy/checks/list.rst   |   1 +
 .../checks/performance/redundant-lookup.rst   | 147 
 .../checkers/performance/redundant-lookup.cpp | 142 
 8 files changed, 504 insertions(+)
 create mode 100644 
clang-tools-extra/clang-tidy/performance/RedundantLookupCheck.cpp
 create mode 100644 
clang-tools-extra/clang-tidy/performance/RedundantLookupCheck.h
 create mode 100644 
clang-tools-extra/docs/clang-tidy/checks/performance/redundant-lookup.rst
 create mode 100644 
clang-tools-extra/test/clang-tidy/checkers/performance/redundant-lookup.cpp

diff --git a/clang-tools-extra/clang-tidy/performance/CMakeLists.txt 
b/clang-tools-extra/clang-tidy/performance/CMakeLists.txt
index c6e547c5089fb0e..6f907852c9fd1c4 100644
--- a/clang-tools-extra/clang-tidy/performance/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/performance/CMakeLists.txt
@@ -21,6 +21,7 @@ add_clang_library(clangTidyPerformanceModule STATIC
   NoexceptMoveConstructorCheck.cpp
   NoexceptSwapCheck.cpp
   PerformanceTidyModule.cpp
+  RedundantLookupCheck.cpp
   TriviallyDestructibleCheck.cpp
   TypePromotionInMathFnCheck.cpp
   UnnecessaryCopyInitialization.cpp
diff --git a/clang-tools-extra/clang-tidy/performance/PerformanceTidyModule.cpp 
b/clang-tools-extra/clang-tidy/performance/PerformanceTidyModule.cpp
index 9e0fa6f88b36a00..a613114e6b83bd3 100644
--- a/clang-tools-extra/clang-tidy/performance/PerformanceTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/performance/PerformanceTidyModule.cpp
@@ -24,6 +24,7 @@
 #include "NoexceptDestructorCheck.h"
 #include "NoexceptMoveConstructorCheck.h"
 #include "NoexceptSwapCheck.h"
+#include "RedundantLookupCheck.h"
 #include "TriviallyDestructibleCheck.h"
 #include "TypePromotionInMathFnCheck.h"
 #include "UnnecessaryCopyInitialization.h"
@@ -62,6 +63,8 @@ class PerformanceModule : public ClangTidyModule {
 "performance-noexcept-move-constructor");
 CheckFactories.registerCheck(
 "performance-noexcept-swap");
+CheckFactories.registerCheck(
+"performance-redundant-lookup");
 CheckFactories.registerCheck(
 "performance-trivially-destructible");
 CheckFactories.registerCheck(
diff --git a/clang-tools-extra/clang-tidy/performance/RedundantLookupCheck.cpp 
b/clang-tools-extra/clang-tidy/performance/RedundantLookupCheck.cpp
new file mode 100644
index 000..fefb28682c5b8af
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/performance/RedundantLookupCheck.cpp
@@ -0,0 +1,159 @@
+//===--- RedundantLookupCheck.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 "RedundantLookupCheck.h"
+#include "../utils/OptionsUtils.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Regex.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::performance {
+
+static constexpr auto DefaultContainerNameRegex = "set|map";
+
+static const llvm::StringRef DefaultLookupMethodNames =
+llvm::StringLiteral( //
+"at;"
+"contains;"
+"count;"
+"find_as;"
+"find;"
+// These are tricky, as they take the "key" at different places.
+// They sometimes bundle up the key and the value together in a pair.
+//   "emplace;"
+//   "insert_or_assign;"
+//   "insert;"
+//   "try_emplace;"
+)
+.drop_back(); // Drops the last semicolon.
+
+RedundantLookupCheck::RedundantLookupCheck(StringRef Name,
+   ClangTidyContext *Context)
+: ClangTidyCheck(Name, Context),
+  ContainerNameRegex(
+  Options.get("ContainerNameRegex", DefaultContainerNameRegex)),
+  LookupMethodNames(utils::options::parseStringList(
+  Options.get("LookupMethodNames", DefaultLookupMethodNames))) {}
+
+void RedundantLookupCheck::storeOptions(ClangTidy

[clang-tools-extra] [clang-tidy] Add performance-redundant-lookup check (PR #125420)

2025-02-02 Thread via cfe-commits

llvmbot wrote:




@llvm/pr-subscribers-clang-tidy

Author: Balazs Benics (steakhal)


Changes

Finds redundant lookups to set|map containers.
For example: `map.count(key) && map[key] < threshold`

---

I've implemented this check in the Clang Static Analyzer, but it didn't really 
bring much value compared to its cost.
Because of this, I decided to have a second look and implement this in 
clang-tidy.
As in the 
[comment](https://github.com/llvm/llvm-project/issues/123376#issuecomment-2628966580)
 people showed interest, I decided to give a final polish to the check and 
publish it to ask for feedback.

My experience of this checker so far on the `clang` code base was that it 
didn't produce many false-positives, but those are hard to avoid due to what 
patterns we are looking for.
Frequently the findings were actionable and useful, and already fixed a couple 
issue that I found: 0e62c748d440a6d12d190e951c987efe309f40d6, 
9333d8fb0749b1ae2b152a1003d2ecc00027b3d5, 
d0a142eaea03661e8399f2c1733b93d21d55dfee, 
0d21ef4e6c50c7d4d591adf7e6dbd6232e8a99c4, 
65708bad579229cd7f62b8d0eaefda4bb20eb6d8, 
16d4453f2f5d9554ce39507fda0f33ce9066007b.

I've considered adding some fixits, and that may be possible if we focus on 
some really frequent patterns, but I decided not to invest there.
I've considered techniques to limit the scope of what "lookups" should be 
bundled together, for example the lookups that are in close proximity, or 
lookups that are within the same lexical scope, or there are no mutations 
between the lookups; but ultimately I found any of these arbitrary and 
confusing - though I admit, there could be improvements.

Let me know what you think.

---

Patch is 35.66 KiB, truncated to 20.00 KiB below, full version: 
https://github.com/llvm/llvm-project/pull/125420.diff


13 Files Affected:

- (modified) clang-tools-extra/clang-tidy/performance/CMakeLists.txt (+1) 
- (modified) clang-tools-extra/clang-tidy/performance/PerformanceTidyModule.cpp 
(+3) 
- (added) clang-tools-extra/clang-tidy/performance/RedundantLookupCheck.cpp 
(+159) 
- (added) clang-tools-extra/clang-tidy/performance/RedundantLookupCheck.h (+46) 
- (modified) clang-tools-extra/docs/ReleaseNotes.rst (+5) 
- (modified) clang-tools-extra/docs/clang-tidy/checks/list.rst (+1) 
- (added) 
clang-tools-extra/docs/clang-tidy/checks/performance/redundant-lookup.rst 
(+147) 
- (added) clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/functional 
(+21) 
- (added) clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/map (+167) 
- (added) clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/memory 
(+10) 
- (added) clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/set (+145) 
- (added) clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/utility 
(+13) 
- (added) 
clang-tools-extra/test/clang-tidy/checkers/performance/redundant-lookup.cpp 
(+142) 


``diff
diff --git a/clang-tools-extra/clang-tidy/performance/CMakeLists.txt 
b/clang-tools-extra/clang-tidy/performance/CMakeLists.txt
index c6e547c5089fb0e..6f907852c9fd1c4 100644
--- a/clang-tools-extra/clang-tidy/performance/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/performance/CMakeLists.txt
@@ -21,6 +21,7 @@ add_clang_library(clangTidyPerformanceModule STATIC
   NoexceptMoveConstructorCheck.cpp
   NoexceptSwapCheck.cpp
   PerformanceTidyModule.cpp
+  RedundantLookupCheck.cpp
   TriviallyDestructibleCheck.cpp
   TypePromotionInMathFnCheck.cpp
   UnnecessaryCopyInitialization.cpp
diff --git a/clang-tools-extra/clang-tidy/performance/PerformanceTidyModule.cpp 
b/clang-tools-extra/clang-tidy/performance/PerformanceTidyModule.cpp
index 9e0fa6f88b36a00..a613114e6b83bd3 100644
--- a/clang-tools-extra/clang-tidy/performance/PerformanceTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/performance/PerformanceTidyModule.cpp
@@ -24,6 +24,7 @@
 #include "NoexceptDestructorCheck.h"
 #include "NoexceptMoveConstructorCheck.h"
 #include "NoexceptSwapCheck.h"
+#include "RedundantLookupCheck.h"
 #include "TriviallyDestructibleCheck.h"
 #include "TypePromotionInMathFnCheck.h"
 #include "UnnecessaryCopyInitialization.h"
@@ -62,6 +63,8 @@ class PerformanceModule : public ClangTidyModule {
 "performance-noexcept-move-constructor");
 CheckFactories.registerCheck(
 "performance-noexcept-swap");
+CheckFactories.registerCheck(
+"performance-redundant-lookup");
 CheckFactories.registerCheck(
 "performance-trivially-destructible");
 CheckFactories.registerCheck(
diff --git a/clang-tools-extra/clang-tidy/performance/RedundantLookupCheck.cpp 
b/clang-tools-extra/clang-tidy/performance/RedundantLookupCheck.cpp
new file mode 100644
index 000..fefb28682c5b8af
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/performance/RedundantLookupCheck.cpp
@@ -0,0 +1,159 @@
+//===--- RedundantLookupCheck.cpp - clang-tidy 
===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.

[clang-tools-extra] [clang-tidy] Add performance-redundant-lookup check (PR #125420)

2025-02-02 Thread via cfe-commits

llvmbot wrote:




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

Author: Balazs Benics (steakhal)


Changes

Finds redundant lookups to set|map containers.
For example: `map.count(key) && map[key] < threshold`

---

I've implemented this check in the Clang Static Analyzer, but it didn't really 
bring much value compared to its cost.
Because of this, I decided to have a second look and implement this in 
clang-tidy.
As in the 
[comment](https://github.com/llvm/llvm-project/issues/123376#issuecomment-2628966580)
 people showed interest, I decided to give a final polish to the check and 
publish it to ask for feedback.

My experience of this checker so far on the `clang` code base was that it 
didn't produce many false-positives, but those are hard to avoid due to what 
patterns we are looking for.
Frequently the findings were actionable and useful, and already fixed a couple 
issue that I found: 0e62c748d440a6d12d190e951c987efe309f40d6, 
9333d8fb0749b1ae2b152a1003d2ecc00027b3d5, 
d0a142eaea03661e8399f2c1733b93d21d55dfee, 
0d21ef4e6c50c7d4d591adf7e6dbd6232e8a99c4, 
65708bad579229cd7f62b8d0eaefda4bb20eb6d8, 
16d4453f2f5d9554ce39507fda0f33ce9066007b.

I've considered adding some fixits, and that may be possible if we focus on 
some really frequent patterns, but I decided not to invest there.
I've considered techniques to limit the scope of what "lookups" should be 
bundled together, for example the lookups that are in close proximity, or 
lookups that are within the same lexical scope, or there are no mutations 
between the lookups; but ultimately I found any of these arbitrary and 
confusing - though I admit, there could be improvements.

Let me know what you think.

---

Patch is 35.63 KiB, truncated to 20.00 KiB below, full version: 
https://github.com/llvm/llvm-project/pull/125420.diff


13 Files Affected:

- (modified) clang-tools-extra/clang-tidy/performance/CMakeLists.txt (+1) 
- (modified) clang-tools-extra/clang-tidy/performance/PerformanceTidyModule.cpp 
(+3) 
- (added) clang-tools-extra/clang-tidy/performance/RedundantLookupCheck.cpp 
(+159) 
- (added) clang-tools-extra/clang-tidy/performance/RedundantLookupCheck.h (+46) 
- (modified) clang-tools-extra/docs/ReleaseNotes.rst (+5) 
- (modified) clang-tools-extra/docs/clang-tidy/checks/list.rst (+1) 
- (added) 
clang-tools-extra/docs/clang-tidy/checks/performance/redundant-lookup.rst 
(+147) 
- (added) clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/functional 
(+21) 
- (added) clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/map (+167) 
- (added) clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/memory 
(+10) 
- (added) clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/set (+145) 
- (added) clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/utility 
(+13) 
- (added) 
clang-tools-extra/test/clang-tidy/checkers/performance/redundant-lookup.cpp 
(+142) 


``diff
diff --git a/clang-tools-extra/clang-tidy/performance/CMakeLists.txt 
b/clang-tools-extra/clang-tidy/performance/CMakeLists.txt
index c6e547c5089fb0..6f907852c9fd1c 100644
--- a/clang-tools-extra/clang-tidy/performance/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/performance/CMakeLists.txt
@@ -21,6 +21,7 @@ add_clang_library(clangTidyPerformanceModule STATIC
   NoexceptMoveConstructorCheck.cpp
   NoexceptSwapCheck.cpp
   PerformanceTidyModule.cpp
+  RedundantLookupCheck.cpp
   TriviallyDestructibleCheck.cpp
   TypePromotionInMathFnCheck.cpp
   UnnecessaryCopyInitialization.cpp
diff --git a/clang-tools-extra/clang-tidy/performance/PerformanceTidyModule.cpp 
b/clang-tools-extra/clang-tidy/performance/PerformanceTidyModule.cpp
index 9e0fa6f88b36a0..a613114e6b83bd 100644
--- a/clang-tools-extra/clang-tidy/performance/PerformanceTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/performance/PerformanceTidyModule.cpp
@@ -24,6 +24,7 @@
 #include "NoexceptDestructorCheck.h"
 #include "NoexceptMoveConstructorCheck.h"
 #include "NoexceptSwapCheck.h"
+#include "RedundantLookupCheck.h"
 #include "TriviallyDestructibleCheck.h"
 #include "TypePromotionInMathFnCheck.h"
 #include "UnnecessaryCopyInitialization.h"
@@ -62,6 +63,8 @@ class PerformanceModule : public ClangTidyModule {
 "performance-noexcept-move-constructor");
 CheckFactories.registerCheck(
 "performance-noexcept-swap");
+CheckFactories.registerCheck(
+"performance-redundant-lookup");
 CheckFactories.registerCheck(
 "performance-trivially-destructible");
 CheckFactories.registerCheck(
diff --git a/clang-tools-extra/clang-tidy/performance/RedundantLookupCheck.cpp 
b/clang-tools-extra/clang-tidy/performance/RedundantLookupCheck.cpp
new file mode 100644
index 00..fefb28682c5b8a
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/performance/RedundantLookupCheck.cpp
@@ -0,0 +1,159 @@
+//===--- RedundantLookupCheck.cpp - clang-tidy 
===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.

[clang-tools-extra] [clang-tidy] Add performance-redundant-lookup check (PR #125420)

2025-02-02 Thread Balazs Benics via cfe-commits

https://github.com/steakhal created 
https://github.com/llvm/llvm-project/pull/125420

Finds redundant lookups to set|map containers.
For example: `map.count(key) && map[key] < threshold`

---

I've implemented this check in the Clang Static Analyzer, but it didn't really 
bring much value compared to its cost.
Because of this, I decided to have a second look and implement this in 
clang-tidy.
As in the 
[comment](https://github.com/llvm/llvm-project/issues/123376#issuecomment-2628966580)
 people showed interest, I decided to give a final polish to the check and 
publish it to ask for feedback.

My experience of this checker so far on the `clang` code base was that it 
didn't produce many false-positives, but those are hard to avoid due to what 
patterns we are looking for.
Frequently the findings were actionable and useful, and already fixed a couple 
issue that I found: 0e62c748d440a6d12d190e951c987efe309f40d6, 
9333d8fb0749b1ae2b152a1003d2ecc00027b3d5, 
d0a142eaea03661e8399f2c1733b93d21d55dfee, 
0d21ef4e6c50c7d4d591adf7e6dbd6232e8a99c4, 
65708bad579229cd7f62b8d0eaefda4bb20eb6d8, 
16d4453f2f5d9554ce39507fda0f33ce9066007b.

I've considered adding some fixits, and that may be possible if we focus on 
some really frequent patterns, but I decided not to invest there.
I've considered techniques to limit the scope of what "lookups" should be 
bundled together, for example the lookups that are in close proximity, or 
lookups that are within the same lexical scope, or there are no mutations 
between the lookups; but ultimately I found any of these arbitrary and 
confusing - though I admit, there could be improvements.

Let me know what you think.

>From 78390b2af01fcd5acfbd2a313852962c873e5611 Mon Sep 17 00:00:00 2001
From: Balazs Benics 
Date: Sun, 2 Feb 2025 17:35:39 +0100
Subject: [PATCH 1/2] [clang-tidy] Add performance-redundant-lookup check

Finds redundant lookups to set|map containers.
For example: `map.count(key) && map[key] < threshold`
---
 .../clang-tidy/performance/CMakeLists.txt |   1 +
 .../performance/PerformanceTidyModule.cpp |   3 +
 .../performance/RedundantLookupCheck.cpp  | 159 ++
 .../performance/RedundantLookupCheck.h|  46 +
 clang-tools-extra/docs/ReleaseNotes.rst   |   5 +
 .../docs/clang-tidy/checks/list.rst   |   1 +
 .../checks/performance/redundant-lookup.rst   | 147 
 .../checkers/performance/redundant-lookup.cpp | 142 
 8 files changed, 504 insertions(+)
 create mode 100644 
clang-tools-extra/clang-tidy/performance/RedundantLookupCheck.cpp
 create mode 100644 
clang-tools-extra/clang-tidy/performance/RedundantLookupCheck.h
 create mode 100644 
clang-tools-extra/docs/clang-tidy/checks/performance/redundant-lookup.rst
 create mode 100644 
clang-tools-extra/test/clang-tidy/checkers/performance/redundant-lookup.cpp

diff --git a/clang-tools-extra/clang-tidy/performance/CMakeLists.txt 
b/clang-tools-extra/clang-tidy/performance/CMakeLists.txt
index c6e547c5089fb0e..6f907852c9fd1c4 100644
--- a/clang-tools-extra/clang-tidy/performance/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/performance/CMakeLists.txt
@@ -21,6 +21,7 @@ add_clang_library(clangTidyPerformanceModule STATIC
   NoexceptMoveConstructorCheck.cpp
   NoexceptSwapCheck.cpp
   PerformanceTidyModule.cpp
+  RedundantLookupCheck.cpp
   TriviallyDestructibleCheck.cpp
   TypePromotionInMathFnCheck.cpp
   UnnecessaryCopyInitialization.cpp
diff --git a/clang-tools-extra/clang-tidy/performance/PerformanceTidyModule.cpp 
b/clang-tools-extra/clang-tidy/performance/PerformanceTidyModule.cpp
index 9e0fa6f88b36a00..a613114e6b83bd3 100644
--- a/clang-tools-extra/clang-tidy/performance/PerformanceTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/performance/PerformanceTidyModule.cpp
@@ -24,6 +24,7 @@
 #include "NoexceptDestructorCheck.h"
 #include "NoexceptMoveConstructorCheck.h"
 #include "NoexceptSwapCheck.h"
+#include "RedundantLookupCheck.h"
 #include "TriviallyDestructibleCheck.h"
 #include "TypePromotionInMathFnCheck.h"
 #include "UnnecessaryCopyInitialization.h"
@@ -62,6 +63,8 @@ class PerformanceModule : public ClangTidyModule {
 "performance-noexcept-move-constructor");
 CheckFactories.registerCheck(
 "performance-noexcept-swap");
+CheckFactories.registerCheck(
+"performance-redundant-lookup");
 CheckFactories.registerCheck(
 "performance-trivially-destructible");
 CheckFactories.registerCheck(
diff --git a/clang-tools-extra/clang-tidy/performance/RedundantLookupCheck.cpp 
b/clang-tools-extra/clang-tidy/performance/RedundantLookupCheck.cpp
new file mode 100644
index 000..fefb28682c5b8af
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/performance/RedundantLookupCheck.cpp
@@ -0,0 +1,159 @@
+//===--- RedundantLookupCheck.cpp - clang-tidy 
===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENS