This is an archive of the discontinued LLVM Phabricator instance.

[clang-tidy] implement new check 'misc-const-correctness' to add 'const' to unmodified variables
ClosedPublic

Authored by JonasToth on Nov 27 2018, 4:04 AM.

Details

Summary

This patch connects the check for const-correctness with the new general
utility to add const to variables.
The code-transformation is only done, if the detected variable for const-ness
is not part of a group-declaration.

The check allows to control multiple facets of adding const, e.g. if pointers themself should be
marked as const if they are not used.

Diff Detail

Event Timeline

There are a very large number of changes, so older changes are hidden. Show Older Changes
JonasToth updated this revision to Diff 370813.Sep 5 2021, 9:35 AM
  • add reproducer for '__unaligned' bug
JonasToth added a comment.EditedSep 5 2021, 9:45 AM

I analyzed the false positive and its probably not so simple to fix.

'NoOp' is always used to add qualifiers. This is true for const and __unaligned, but there seems to be no further information to disambiguate those.
Maybe this requires even something in the front-end to change. WDYT @aaron.ballman ?

Edit: All qualifiers would trigger this problem: const, volatile, restrict and __unaligned (MS extension).
It is likely not a simple as checking 'isConst-Before/After', because pointers are casted a bit more complicated. But something along those lines might be a fix.

ping

Hi Jonas,

Using patch32 in production (on top of "release/13.x" branch) ever since it was made available. No crashes.

Only one false positive reported, which I'm not sure even is a false positive, is when I used std::visit and passed in a reference to an object as the first argument, the checker recommended I annotate the variable as const, even though std::visit takes the argument by &&, and changing it to const would have resulted in a miscompile. I think there's also some difference in how std::visit is implemented between various standard C++ libraries, so not sure if that's a special case or something more general.

Quoting from memory:

 std::variant<Foo, Bar, Baz> theThing;

 class Parameter
 {
    int operator()(const Foo& foo)
    {
     ...
    }
    int operator()(const Bar& bar)
    {
     ...
    }
 } param;  // cannot be const, but the checker warns that it could be const

std::visit(param, theThing)

I'm hoping to have some time next week to distill a minimal test case.

Thank you,
florin

@aaron.ballman ping for review :)

I do not intend to fix the raised issues with this revision. I think it is more valuable to get this check in and get more user-feedback. But the found bugs are on the todo-list!

Thanks for pinging on this, it had fallen off my radar! @JonasToth -- did you ever get the chance to talk with @cjdb about the overlap mentioned in https://reviews.llvm.org/D54943#2950100? (The other review also seems to have stalled out, so I'm not certain where things are at.)

Thanks for pinging on this, it had fallen off my radar! @JonasToth -- did you ever get the chance to talk with @cjdb about the overlap mentioned in https://reviews.llvm.org/D54943#2950100? (The other review also seems to have stalled out, so I'm not certain where things are at.)

We talked only short in the review, but I am not sure if there is such a big overlap between the checks. The other check looks at arguments, which is something this check currently skips completely.
I think, this check should continue to avoid this analysis, too.

The underlying ExprMutAnalyzer is reusable (I think so, if not that should be fixed). But my current understanding is, that the argument checker already knows the values are const, because it matches on const & parameters.
IMHO keeping the checks independent would be better. Otherwise a future combined check can be aliased with specific configurations.

Thanks for pinging on this, it had fallen off my radar! @JonasToth -- did you ever get the chance to talk with @cjdb about the overlap mentioned in https://reviews.llvm.org/D54943#2950100? (The other review also seems to have stalled out, so I'm not certain where things are at.)

We talked only short in the review, but I am not sure if there is such a big overlap between the checks. The other check looks at arguments, which is something this check currently skips completely.
I think, this check should continue to avoid this analysis, too.

That's what sort of worries me though -- a function parameter is notionally a local variable, so the fact that one check tries to solve one half of the problem and the other check tries to solve the other half feels unclean to me. I'm fine with this check ignoring parameters for now, but I think there should only be one check for "make this const if it can be made so".

The underlying ExprMutAnalyzer is reusable (I think so, if not that should be fixed). But my current understanding is, that the argument checker already knows the values are const, because it matches on const & parameters.
IMHO keeping the checks independent would be better. Otherwise a future combined check can be aliased with specific configurations.

I think surfacing the checks as separate checks makes a lot of sense (once is about C++ Core Guideline conformance and the other is about performance), but I'm not sure I understand why the implementations should be different yet.

That said, I think this particular check is more mature and closer to landing than the other one. Based on the amount of effort in this patch already, unless there are strong objections, I think we should base the "make this const if it can be made so" checks on this one.

clang-tools-extra/clang-tidy/cppcoreguidelines/ConstCorrectnessCheck.cpp
168 ↗(On Diff #366516)

That's the correct approach -- we don't try to apply style choices to fixes in clang-tidy unless they're uncontentious (removing extra whitespace kinda stuff); we expect the user to run clang-format to fix those sort of things up.

49–51 ↗(On Diff #370813)

The new-fangled way to do this is to implement an override for isLanguageVersionSupported() (as in https://github.com/llvm/llvm-project/blob/main/clang-tools-extra/clang-tidy/abseil/DurationDivisionCheck.h#L26)

111–116 ↗(On Diff #370813)
clang-tools-extra/clang-tidy/cppcoreguidelines/ConstCorrectnessCheck.h
31 ↗(On Diff #365253)

Are we still intending to be conservative here? This defaults some of the Transform to 1 instead of 0, but that's different than what the documentation currently reads.

clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines-const-correctness-transform-values.cpp
40 ↗(On Diff #370813)

Are we being conservative here about the diagnostic? It seems like the instantiations in the TU would mean that this could be declared const.

107–108 ↗(On Diff #370813)

This is a false positive. Making np_local3 be const will break the code: https://godbolt.org/z/EExKfsW4G

114 ↗(On Diff #370813)

The test really doesn't have anything to do with casts, does it?

120 ↗(On Diff #370813)

This is a problem -- I see no license on that site, but I do see "All rights reserved". I don't think we should copy from there.

164–167 ↗(On Diff #370813)

Can you also add a test:

TMPClass<double> p_local2; // Don't attempt to make this const
p_local2.sometimesConst();
clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines-const-correctness-values.cpp
13 ↗(On Diff #370813)

It'd be handy to show how we handle a static global, which can be known to be const technically.

17 ↗(On Diff #370813)

Similarly, showing anonymous namespace variables would help, as those are like a static global and can also theoretically known to be const. (Note, I don't insist on catching those cases! Just would like test coverage with some comments on what to expect + docs if it seems important to tell users.)

555–574 ↗(On Diff #370813)

Same comments here as above.

JonasToth added inline comments.Dec 15 2021, 7:14 AM
clang-tools-extra/clang-tidy/cppcoreguidelines/ConstCorrectnessCheck.h
31 ↗(On Diff #365253)

good question. given the current result of it being quiet solid we might have it on? But I am fine with it being off.
I am not sure if it would induce too much transformations to handle. I am unsure.

clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines-const-correctness-transform-values.cpp
40 ↗(On Diff #370813)

Yes, the check ignores templates when matching. The reasoning behind this was: too much at once. TU-local templates should be transformable, but it must be ensured that all instantiations can be const. Probably the best solution would be that the template uses concepts and the concepts allow const at this place.
This is hopefully follow-up :)

120 ↗(On Diff #370813)

True. I will try to copy&paste from libc++ instead and remove the reference :)

aaron.ballman added inline comments.Dec 15 2021, 7:28 AM
clang-tools-extra/clang-tidy/cppcoreguidelines/ConstCorrectnessCheck.h
31 ↗(On Diff #365253)

Based on the test coverage we have, I think I'd be fine with it being on by default, but I do think the most conservative approach would be to be off by default. I don't have a strong opinion either way aside from "the documentation should match the code."

clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines-const-correctness-transform-values.cpp
40 ↗(On Diff #370813)

Okay, that sounds good to me!

120 ↗(On Diff #370813)

Thank you. :-)

cjdb added a comment.Jan 13 2022, 4:32 PM

That said, I think this particular check is more mature and closer to landing than the other one. Based on the amount of effort in this patch already, unless there are strong objections, I think we should base the "make this const if it can be made so" checks on this one.

Ack. How about we worry about D107873 once this patch lands, and once I've had a chance to rebase it on top of this?

+1

Let's get this in :)

JonasToth updated this revision to Diff 400280.Jan 15 2022, 7:20 AM
JonasToth marked 10 inline comments as done.
  • Merge branch 'main' into feature_rebase_const_transform_20210808
  • fix: fixes for most review comments
clang-tools-extra/clang-tidy/cppcoreguidelines/ConstCorrectnessCheck.h
31 ↗(On Diff #365253)

I matched the documentation to that its on by default. It is kinda what the check promises, so I think having it off is not user-friendly.
I removed the Experimental note for it as well. It seems to be "good enough" that it does not interfere with everyday programming and even complex things.
The different validation runs seem to agree somewhat (even though there are still issues).

clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines-const-correctness-transform-values.cpp
40 ↗(On Diff #370813)

Yes we are conservative. Once its a templated variable the analysis bails out. Non-templated variables in a template are analyzed though.

The 'check all instantiations' are not implemented, but there is already something that goes a little bit in that direction.
Line 118: TemplateDiagnosticsCache.contains(Variable->getBeginLoc())), which is necessary to deduplicate emitted diagnostics in templates.
Similarly, TU-local templates could be checked all-together and if the variable could be const every single time, the diagnostic can be emitted.

But I feel that checking the concepts would be a much better approach to this. This would include the non-local templates as well. At the same time, most templates don't use them yet.

114 ↗(On Diff #370813)

Thats correct :D Renamed

JonasToth updated this revision to Diff 400288.Jan 15 2022, 8:17 AM
JonasToth marked 2 inline comments as done.
  • Merge branch 'main' into feature_rebase_const_transform_20210808
  • fix: address other review comments
  • remove one false positive with pointers in a range-based for loop on arrays
JonasToth marked an inline comment as done.Jan 15 2022, 8:27 AM

addressed all outstanding review comments.

clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines-const-correctness-transform-values.cpp
107–108 ↗(On Diff #370813)

Thanks for spotting this!
The analyzer did not consider the int * non_const_ptr: np_local3 to be a mutation, as its not a reference type.

444   // The range variable is a reference to a builtin array. In that case the
  1   // array is considered **modified if the loop-variable is a non-const reference**.
  2   const auto DeclStmtToNonRefToArray = declStmt(hasSingleDecl(varDecl(hasType(
  3       hasUnqualifiedDesugaredType(referenceType(pointee(arrayType())))))));
  4   const auto RefToArrayRefToElements = match(
  5       findAll(stmt(cxxForRangeStmt(
  6                        hasLoopVariable(varDecl(hasType(nonConstReferenceType()))
  7                                            .bind(NodeID<Decl>::value)),
  8                        hasRangeStmt(DeclStmtToNonRefToArray),
  9                        hasRangeInit(canResolveToExpr(equalsNode(Exp)))))
 10                   .bind("stmt")),
 11       Stm, Context);

Added the fix to the patch, is that ok with you?

clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines-const-correctness-values.cpp
17 ↗(On Diff #370813)

Globals are not matched in any way. But there is cppcoreguidelines-avoid-non-const-global-variables which warns for non-const globals already.

The docs say This check implements detection of local variables which could be declared as, so its implicit that globals are not caught. I think that its enough. I will add a reference to cppcoreguidelines-avoid-non-const-global-variables though.

JonasToth updated this revision to Diff 400291.Jan 15 2022, 8:30 AM
JonasToth marked an inline comment as done.
  • improve documentation a bit
JonasToth updated this revision to Diff 400293.Jan 15 2022, 8:31 AM
  • fix release note ordering
JonasToth updated this revision to Diff 400296.Jan 15 2022, 9:11 AM
  • fix: adjust unit test for new array-pointer condition in range-based for loops
njames93 added inline comments.Jan 27 2022, 3:07 PM
clang-tools-extra/clang-tidy/cppcoreguidelines/ConstCorrectnessCheck.h
29 ↗(On Diff #400296)

Can all these options be defaulted to Boolean values, as the options parser has special parsing logic for bool

  • use boolean for option parsing
  • use boolean for options
  • fix snafoo on arc usage
JonasToth marked an inline comment as done.Jan 29 2022, 12:30 PM

ping @aaron.ballman just in case the patch is now down on the list, I am sorry for my high latency :(

clang-tools-extra/clang-tidy/cppcoreguidelines/ConstCorrectnessCheck.h
29 ↗(On Diff #400296)

you are right. I updated it.

JonasToth marked an inline comment as done.Jan 30 2022, 11:47 AM
clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines-const-correctness-templates.cpp
16 ↗(On Diff #404297)

CHECK-FIXES?

clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines-const-correctness-transform-pointer-as-values.cpp
12 ↗(On Diff #404297)

Shouldn't this validate that the const was placed in the correct position?
e.g. const double * is a different meaning from double *const

Apply to all the other CHECK-FIXES as well

0x8000-0000 added inline comments.Jan 31 2022, 6:17 PM
clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines-const-correctness-transform-pointer-as-values.cpp
12 ↗(On Diff #404297)

Can we have the checker merged in first, then we can worry about the automatic fixer?

JonasToth marked 2 inline comments as done.Feb 1 2022, 1:49 AM
JonasToth added inline comments.
clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines-const-correctness-templates.cpp
16 ↗(On Diff #404297)

see my comment in the other test.

clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines-const-correctness-transform-pointer-as-values.cpp
12 ↗(On Diff #404297)

the checker is its own utility with its own tests and proper test coverage.
yes const double* and double* const are different and are correctly inserted, but that is not tested here, but here: https://github.com/llvm/llvm-project/blob/main/clang-tools-extra/unittests/clang-tidy/AddConstTest.cpp

Similar to the actual const analysis. That has its own test-suite (https://github.com/llvm/llvm-project/blob/main/clang/unittests/Analysis/ExprMutationAnalyzerTest.cpp)

The tests here are concerned with the diagnostics and "real" code. Afterall, this functionality is too complex to have all of it checked with these kind of tests.
I think the separate testing in specialized unit-tests (as is now) for the specialized functions is the right approach and the CHECK-FIXES are not necessary in this instance, maybe even bad, because it makes the tests unclearer.

JonasToth marked 2 inline comments as done.Feb 1 2022, 1:52 AM
JonasToth added inline comments.
clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines-const-correctness-transform-pointer-as-values.cpp
12 ↗(On Diff #404297)

sry: The _fixer_ is its own utility ...

Additionally: The test is run on big projects with transformation (LLVM, some Qt Stuff).
First everything is transformed and then recompiled. The compiler tells what was incorrectly inserted :)

Thats part of the testing too.

clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines-const-correctness-transform-pointer-as-values.cpp
12 ↗(On Diff #404297)

The tests here are concerned with the diagnostics and "real" code.

OK, great! I didn't realize it was covered by unit tests,
which is perfectly fine with me :)

JonasToth added inline comments.Feb 2 2022, 7:07 AM
clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines-const-correctness-transform-pointer-as-values.cpp
12 ↗(On Diff #404297)

Perfect!

Yeah, the check and patches here are a bit all over the place, which is another reason why a merge would be really great :D

@aaron.ballman - can this land for Clang14, or does it have wait for 15?

Are there any other reviewers that can approve this?

@aaron.ballman - can this land for Clang14, or does it have wait for 15?

Are there any other reviewers that can approve this?

Sadly, cherry-picking to 14 is very unlikely, as this is not a bugfix. :/
On the other hand, it missed so many releases so lets stay positive :)

Aaron did the review and I think he should accept too.

@aaron.ballman - can this land for Clang14, or does it have wait for 15?

Are there any other reviewers that can approve this?

Sadly, cherry-picking to 14 is very unlikely, as this is not a bugfix. :/
On the other hand, it missed so many releases so lets stay positive :)

Aaron did the review and I think he should accept too.

This won't get backported now, besides giving it a few months to test(and potentially iron out) on the dev branch isn't such a bad thing.

clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt
33 ↗(On Diff #404297)

Will this work under clangd as I don't think that links in the clangAnalysis libraries @sammccall?

clang-tools-extra/clang-tidy/cppcoreguidelines/ConstCorrectnessCheck.cpp
27 ↗(On Diff #404297)

nit

84 ↗(On Diff #404297)

allOf is unnecessary here as declStmt is implicitly an allOf matcher.
Also findAll isn't the right matcher in this case, you likely want forEachDescendant. findAll will try and match on the CompoundStmt which is always going to fail.

102–105 ↗(On Diff #404297)

This sounds like a bug that should be raised with ASTMatchers, if its reproducible.

160 ↗(On Diff #404297)

This is unnecessary, llvm::Optional already has an alias inside the clang namespace.

162–168 ↗(On Diff #404297)

DiagnosticBuilders accept optional fixits as syntactic sugar.
Same goes for below.

clang-tools-extra/docs/ReleaseNotes.rst
128–132

This'll need rebasing after branching.

clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines-const-correctness-templates.cpp
3–6 ↗(On Diff #404297)
clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines-const-correctness-transform-pointer-as-values.cpp
3–5 ↗(On Diff #404297)
clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines-const-correctness-transform-values.cpp
3–5 ↗(On Diff #404297)
clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines-const-correctness-unaligned.cpp
3–5 ↗(On Diff #404297)
clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines-const-correctness-values.cpp
3–5 ↗(On Diff #404297)
JonasToth added inline comments.Feb 7 2022, 12:25 AM
clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt
33 ↗(On Diff #404297)

I did not check if it works and clangd does not explicitly link to Analysis, but only to the checks.
Doesn't the LINK_LIBS induce transitive dependency resolution for linking?

clang-tools-extra/clang-tidy/cppcoreguidelines/ConstCorrectnessCheck.cpp
102–105 ↗(On Diff #404297)

I found no way to reproduce it (yet).
The crashes occured during verification on big projects and run-clang-tidy kind of obfuscated where the crash happened.

This is something for the iron out part of the check I think.

sammccall added inline comments.Feb 7 2022, 1:13 AM
clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt
33 ↗(On Diff #404297)

I don't know whether you need to add it everywhere, I suspect transitive is OK.

In any case this isn't a new dependency, Sema depends on Analysis and clangd depends on Sema.

clang-tools-extra/clang-tidy/cppcoreguidelines/ConstCorrectnessCheck.cpp
29 ↗(On Diff #404297)

This returns an iterator (i.e. a pointer), which is being converted to a boolean.

i.e. it's always returning true. I think this is why you're seeing nullptr crashes on Variable.

29 ↗(On Diff #404297)

this is depending on ::internal details, which doesn't seem OK.
I think you'd need to find another way to do it, or move this to ASTMatchers (in a separate patch).

102–105 ↗(On Diff #404297)

This is a bug in a helper added in this patch, I've added a comment above.

njames93 added inline comments.Feb 7 2022, 7:29 AM
clang-tools-extra/clang-tidy/cppcoreguidelines/ConstCorrectnessCheck.cpp
102–105 ↗(On Diff #404297)

Ahh, hopefully once D118520 lands it should be easier to track down these bugs and create reproducers

JonasToth updated this revision to Diff 410179.Feb 20 2022, 1:08 PM
JonasToth marked 14 inline comments as done.
  • address reviews comments, part 1
  • Merge branch 'main' into feature_rebase_const_transform_20210808
  • fixing iterator-to-bool conversion and addressing other more review comments
JonasToth marked 3 inline comments as done.Feb 20 2022, 1:10 PM
JonasToth added inline comments.
clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt
33 ↗(On Diff #404297)

i verified that the check works with clangd without any additional build settings.

clang-tools-extra/clang-tidy/cppcoreguidelines/ConstCorrectnessCheck.cpp
29 ↗(On Diff #404297)

it is common to write custom matchers in clang-tidy first and use the internal matcher apis as well in clang-tidy.

$ cd llvm-project/clang-tools-extra/clang-tidy
$ git grep "internal::" | wc -l
86

Usually, they are extracted once they are generally useful. And many checks introduce custom matchers for better readability.
I can extract this matcher later if you want, but right now with the longevity of the patch and it being common practice in clang-tidy I am opposed to it.

29 ↗(On Diff #404297)

thank you for that hint! that would explain the issues indeed. I try to verify your proposed fix by re-transforming LLVM.
some other subscribers checked their own codebases as well. this would give us more confidence too.

if the crash is still there, I would like to keep the 'safety-if' in check and try to find the issue with the matcher.
The path @njames93 linked seemed like it would help there a lot.

84 ↗(On Diff #404297)

switching to forEachDescendant, both seem to work.
I am unsure why I used findAll, but I know that I started out with forEachDescendant for some bits of the check and it was insufficent.
But it could very well be, that these pieces are now in the ExprMutAnalyzer.

102–105 ↗(On Diff #404297)

I am rerunning the whole analysis on LLVM to see if the crash is still there after the proposed fix of sam (thanks!).
I will remove the code completely if the crash is gone. Otherwise I'd rather keep the safety in and try to fix the underlying issue afterwards.

JonasToth marked an inline comment as done.Feb 20 2022, 1:30 PM
JonasToth added a comment.EditedFeb 21 2022, 5:10 AM

Another full run on LLVM with:

$ ./clang-tools-extra/clang-tidy/tool/run-clang-tidy.py \
      -clang-tidy-binary ~/software/llvm-project/build_clang_tidy/bin/clang-tidy \
      -checks="-*,cppcoreguidelines-const-correctness" \
      -config "{CheckOptions: [{key: 'cppcoreguidelines-const-correctness.TransformValues', value: 1}, {key: 'cppcoreguidelines-const-correctness.TransformReferences', value: 1}, {key: 'cppcoreguidelines-const-correctness.WarnPointersAsValues', value: true}, {key: 'cppcoreguidelines-const-correctness.TransformPointersAsValues', value: true}]}" \
      -fix 2>&1 | tee ../transformation.log

Yields to this: 3822 files changed, 159256 insertions(+), 159256 deletions(-)
and requires manual changes:


23 files changed, 45 insertions(+), 45 deletions(-)

the issues were:

  • missing {} initialization after a variable became const if a destructor was missing
  • the analysis got confused for some insert(llvm::Function* f) calls and made the objects const. Those are false positives, but with a really low rate

after applying the patch ninja check-all is successfull.

Branch Compare

Herald added a project: Restricted Project. · View Herald TranscriptMar 8 2022, 11:42 PM
aaron.ballman resigned from this revision.Mar 21 2022, 9:43 AM

Sorry, for my own sanity, I've stopped reviewing C++ Core Guideline reviews for their diagnostic behavior until the guideline authors put effort into specifying realistic enforcement guidance.

Sorry, for my own sanity, I've stopped reviewing C++ Core Guideline reviews for their diagnostic behavior until the guideline authors put effort into specifying realistic enforcement guidance.

And how can we continue now? I fear that this is effectively the death of this check. :/
Isn't the "make this const" good enough?

Sorry, for my own sanity, I've stopped reviewing C++ Core Guideline reviews for their diagnostic behavior until the guideline authors put effort into specifying realistic enforcement guidance.

And how can we continue now? I fear that this is effectively the death of this check. :/
Isn't the "make this const" good enough?

Our rule of thumb in clang-tidy when checking against a coding standard is that the coding standard is the law as to how the check should work (what it diagnoses, what it doesn't diagnose). When the coding standard gives guidance that can reasonably be handled another way, we give config options so that users can pick the behavior they need to enforce. The trouble is: the C++ Core Guidelines rarely have a useful enforcement to suggest and the rules themselves are often not sufficiently precise to tease out what to enforce. This puts the onus on *us* to decide what the behavior should be, which is inappropriate unless the guideline authors are involved in the discussions and reflect the decisions in the guidelines. To date, that has (almost?) never happened with any C++ Core Guideline checks.

In this specific case, the guideline being checked is https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#es25-declare-an-object-const-or-constexpr-unless-you-want-to-modify-its-value-later-on and its enforcement guidance is:

Look to see if a variable is actually mutated, and flag it if not. Unfortunately, it might be impossible to detect when a non-const was not intended to vary (vs when it merely did not vary).

This is not useful enforcement guidance, so as a code reviewer, I have to spend *considerable* time trying to figure out what's reasonable and what's not. I've gone through this with basically each C++ Core Guideline check (many of them don't even TRY to give enforcement, like this: https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#enforcement-345), so this is not at all specific to you or something that's your fault. But that's why I no longer am willing to review C++ Core Guideline checks; they're a burden to support without the guideline authors active engagement, which has never materialized.

So how can we continue now is definitely a good question. I think there's plenty of utility in this check already, and so my recommendation would be to pull out all mentions of the C++ Core Guidelines (so we don't have to stick specifically to what they recommend) and AUTOSAR (see https://reviews.llvm.org/D112730#3332366 for details) and move the check to misc- (I don't see a better module for it at the moment as it's not really bugprone and it's not really readability, but I'm not strongly tied to which module it lives in). From there, I hope we can converge on the check MUCH faster because I think the "make this const" is good enough (and SUPER USEFUL). However, I don't know what your goals or requirements are (if you need this to adhere to the C++ Core Guidelines specifically), so another option is for the other reviewers to sign off on it; I won't actively block the addition of new C++ Core Guideline checks.

(Personally, I'm of the opinion we should pull the C++ Core Guidelines modules out of clang-tidy and distribute the existing checks amongst the other modules. Then we no longer have to worry about what the guidelines say, we can use them purely as inspiration for things we feel may be useful to check, but without tying ourselves to supporting their document. However, that's a decision which requires a far wider audience and is way outside the scope of this check.)

I share @aaron.ballman option that given the flakiness of the guidelines, the check shouldn't mention that it addresses the c++core guidelines. Instead moving the check into another module(misc) would still provide immense value, with the side effect that it is still able to help teams enforce parts of the core guidelines.

ok. then i will continue under misc-const-correctness. thank you for the clarifications!

JonasToth updated this revision to Diff 424787.Apr 24 2022, 9:45 AM
  • refactor: rename check to 'misc-const-correctness' and adjust the tests accordingly
  • docs: adjust release notes and adjust check docs slightly
JonasToth retitled this revision from [clang-tidy] implement const-transformation for cppcoreguidelines-const-correctness to [clang-tidy] implement const-transformation for misc-const-correctness.Apr 24 2022, 9:46 AM
JonasToth updated this revision to Diff 424788.Apr 24 2022, 9:48 AM
  • fix: remove clangAnalysis link in cppcoreguidelines and add it in misc
JonasToth retitled this revision from [clang-tidy] implement const-transformation for misc-const-correctness to [clang-tidy] implement new check 'misc-const-correctness' to add 'const' to unmodified variables.Apr 24 2022, 9:50 AM
JonasToth edited the summary of this revision. (Show Details)
njames93 added inline comments.Apr 24 2022, 11:12 PM
clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.cpp
104

Hasn't this already been addressed, if so can this block just be removed.

clang-tools-extra/docs/ReleaseNotes.rst
122

Unrelated change.

clang-tools-extra/docs/clang-tidy/checks/list.rst
38–39

This should be committed separately.

clang-tools-extra/test/clang-tidy/checkers/misc-const-correctness-transform-values.cpp
27 ↗(On Diff #424788)

This check directive isn't going to be effective.
Try CHECK-FIXES: const int p_local0 = 2;
Same goes for all the ones below.

JonasToth updated this revision to Diff 427423.May 5 2022, 1:13 PM
JonasToth marked 3 inline comments as done.
  • addressing review comments and remove unrelated changes
JonasToth marked an inline comment as done.May 5 2022, 1:19 PM

Thank you for the review! I adjusted the patch.

JonasToth added inline comments.May 6 2022, 5:55 AM
clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.h
25

missing change to new docs

JonasToth updated this revision to Diff 427629.May 6 2022, 7:06 AM
  • fix documentation reference

friendly ping :) (maybe @njames93 ?)

Pretty much good to go, just a few nits with the tests.
Can you add CHECK-FIXES directives for all warnings if there should be a fixit.
If there shouldn't be one could you either add a comment saying there shouldn't be one, or put a CHECK-FIXES-NOT directive.

JonasToth updated this revision to Diff 432774.May 29 2022, 2:48 AM
  • addded CHECK-FIXES in clang-tidy tests
  • merged latest main into branch

ping :)
@njames93 I added more CHECK-FIXES and CHECK-FIXES-NOT statements in the tests.

njames93 accepted this revision.Jun 8 2022, 12:43 AM

LGTM, just a couple points but not essential.

clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.cpp
185–188
clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.h
36

It may be worth adding some validation to these. If AnalyzeValues, AnalyzeReferences and WarnPointersAsValues are all false, this whole check is basically a no-op.

This revision is now accepted and ready to land.Jun 8 2022, 12:43 AM
JonasToth added inline comments.Jun 8 2022, 6:18 AM
clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.h
36

Whats the proper way to react? Short-circuit the registerMatchers, similar to language support?
I think thats the way I would go about this.

njames93 added inline comments.Jun 8 2022, 10:23 AM
clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.h
36

ClangTidyCheck::configurationDiag is the way to warn about issues with config.

LegalizeAdulthood requested changes to this revision.Jun 29 2022, 8:30 AM

Clang-tidy tests and docs have been moved to subdirectories by module, please rebase to main:HEAD

This revision now requires changes to proceed.Jun 29 2022, 8:30 AM
carlosgalvezp added inline comments.Jul 4 2022, 1:20 AM
clang-tools-extra/docs/clang-tidy/checks/misc-const-correctness.rst
10 ↗(On Diff #432774)

AUTOSAR mentions should be removed as per previous comment from @aaron.ballman

JonasToth updated this revision to Diff 443484.Jul 10 2022, 2:32 AM
JonasToth marked 4 inline comments as done.
  • test: move tests into group specific directory
  • refactor: use more idiomatic c++ for scope-based analyzer creation/access
  • feat: provide config validation if analysis if completely configured off
  • refactor: move documentation as well
  • test: add test for incorrect configurations
JonasToth added inline comments.Jul 10 2022, 2:32 AM
clang-tools-extra/docs/clang-tidy/checks/misc-const-correctness.rst
10 ↗(On Diff #432774)

Why? I dont think its wrong to hint that there are coding standards that require const to be used consistently.
The check does not run under cppcoreguidelines- because the guidelines are not exact on their requirements. But "the spirit" is still the same. I would like to keep the references.

Clang-tidy tests and docs have been moved to subdirectories by module, please rebase to main:HEAD

I moved the tests and the documentation as well.
tests + documentation do build/pass

JonasToth updated this revision to Diff 443485.Jul 10 2022, 2:36 AM
  • fix: doc list and release-notes reference
JonasToth updated this revision to Diff 443486.Jul 10 2022, 2:44 AM
  • refactor: adjust warning message for mis-configuration to show which check is configured wrong

@njames93 @LegalizeAdulthood I did integrate the requested changes regarding warning for bad configs, refactoring of map-access and the directory structure of test-files and documentation.

given the high interest in the patch and the need to iron out potential false-positives that will only be spotted on diverse code bases, i would like to commit now and address outstanding issues in follow ups, that are much smaller and easier to manage.

njames93 accepted this revision.Jul 20 2022, 10:43 PM

LGTM with those changes.

This revision was not accepted when it landed; it landed in state Needs Review.Jul 24 2022, 10:39 AM
This revision was automatically updated to reflect the committed changes.

Thank you very much for all you patience, reviews and tests!
I hope that the following improvements are now simpler to integrate and that the test matures well. :)

This check is enabled by default in LLVM (Checks: misc-* in llvm-project/.clang-tidy)

The warning on mutable non-ref local variables is pretty noisy: a *lot* of existing code does not do this, for defensible reasons (some of us think that the ratio of extra safety to extra syntactic noise for locals is too low). The LLVM style guide doesn't take a position on this.

Should this check

  • be disabled for LLVM? (i.e. this is opt-in for codebases with strong const-correctness rules, LLVM does not, it was unintentionally enabled by misc-*)
  • be configured only to warn on references? (i.e. we expect that is de-facto LLVM style and so uncontroversial)

This check is enabled by default in LLVM (Checks: misc-* in llvm-project/.clang-tidy)

The warning on mutable non-ref local variables is pretty noisy: a *lot* of existing code does not do this, for defensible reasons (some of us think that the ratio of extra safety to extra syntactic noise for locals is too low). The LLVM style guide doesn't take a position on this.

Should this check

  • be disabled for LLVM? (i.e. this is opt-in for codebases with strong const-correctness rules, LLVM does not, it was unintentionally enabled by misc-*)
  • be configured only to warn on references? (i.e. we expect that is de-facto LLVM style and so uncontroversial)

IMHO just enable for references in LLVM, thats still enough warnings to take a while to process and the guide is clear on that.
If the community wants it for values as well, that can be done later anyway.

Do you think the defaults for this check should change? Originally, it was "default on" because cppcoreguidelines state so.

I agree with Sam on this one, especially on big files it just floods and hides non-stylistic diagnostics (you even hit max diagnostics exceeded errors).

I believe unless someone takes the extra mile to fix all the value types in LLVM to be const-correct, this is harmful then being useful. I can't speak for the projects out in the wild for the overall default (i have a personal preference for having everything const-correct, but it's mostly for stylistic reasons ...), but i think the default of diagnosing values for LLVM should definitely be off. Do you mind performing that change for .clang-tidy configs if others are not objecting on this either?

This check is enabled by default in LLVM (Checks: misc-* in llvm-project/.clang-tidy)

The warning on mutable non-ref local variables is pretty noisy: a *lot* of existing code does not do this, for defensible reasons (some of us think that the ratio of extra safety to extra syntactic noise for locals is too low). The LLVM style guide doesn't take a position on this.

Should this check

  • be disabled for LLVM? (i.e. this is opt-in for codebases with strong const-correctness rules, LLVM does not, it was unintentionally enabled by misc-*)
  • be configured only to warn on references? (i.e. we expect that is de-facto LLVM style and so uncontroversial)

IMHO just enable for references in LLVM, thats still enough warnings to take a while to process and the guide is clear on that.
If the community wants it for values as well, that can be done later anyway.

Do you think the defaults for this check should change? Originally, it was "default on" because cppcoreguidelines state so.

I agree with Sam that this definitely needs to be disabled for LLVM -- we don't have anywhere near enough const correctness to get significant value out of this check yet, even if it was limited to just references. As for whether we should default to only warn on references by default -- I think that might be a better approach now that this is no longer tied directly to the C++ Core Guidelines.

FYI I've sent D135829 to block this check from running in clangd, after tracking it down as the cause of a >10x regression in reparse times in a project that inadvertently enabled it (misc-*).

Looking at the implementation, this check seems to be expensive by design rather than some bug: it's matching many nodes, and then doing expensive nested analysis for each in the callback.

It's reasonable to want checks like that, OTOH it would be nice to have some way to distinguish them from the "classic" checks that aim to run in roughly linear time.
(It's not just clangd: we've had issues with a batch clang-tidy run over our codebase not finishing due to slow checks, and takes a lot of investigation before you can tell if this is a bug or not)

FYI I've sent D135829 to block this check from running in clangd, after tracking it down as the cause of a >10x regression in reparse times in a project that inadvertently enabled it (misc-*).

Looking at the implementation, this check seems to be expensive by design rather than some bug: it's matching many nodes, and then doing expensive nested analysis for each in the callback.

It's reasonable to want checks like that, OTOH it would be nice to have some way to distinguish them from the "classic" checks that aim to run in roughly linear time.
(It's not just clangd: we've had issues with a batch clang-tidy run over our codebase not finishing due to slow checks, and takes a lot of investigation before you can tell if this is a bug or not)

To quantify this: wanting to be a bit more systematic, I measured the performance overhead of each check in clangd by having it parse SemaExpr.cpp with 0 checks and then each check in isolation.

This check added 1600% overhead, the next highest had <30%.

Unfortunately the detailed numbers aren't reliable as it looks like some kind of throttling kicked in towards the end, but here they are anyway...

I[14:53:46.703] Built preamble of size 53505060 for file /usr/local/google/home/sammccall/src/llvm-project/clang/lib/Sema/SemaExpr.cpp version null in 5.70 seconds
I[14:53:46.708] Building AST...
I[14:53:48.945] Indexing AST...
I[14:53:49.049] Timing AST build with individual clang-tidy checks
I[14:53:54.958]   Baseline = 1968848738 ns
I[14:54:00.873]   abseil-cleanup-ctad = 0.16%
I[14:54:06.707]   abseil-duration-addition = -1.45%
I[14:54:12.527]   abseil-duration-comparison = -1.69%
I[14:54:18.422]   abseil-duration-conversion-cast = -0.32%
I[14:54:24.281]   abseil-duration-division = -0.93%
I[14:54:30.186]   abseil-duration-factory-float = -0.06%
I[14:54:36.107]   abseil-duration-factory-scale = 0.15%
I[14:54:42.344]   abseil-duration-subtraction = 5.31%
I[14:54:48.646]   abseil-duration-unnecessary-conversion = 4.86%
I[14:54:54.617]   abseil-faster-strsplit-delimiter = 0.77%
I[14:55:00.460]   abseil-no-internal-dependencies = -1.21%
I[14:55:06.280]   abseil-no-namespace = -1.61%
I[14:55:12.221]   abseil-redundant-strcat-calls = 0.04%
I[14:55:18.138]   abseil-str-cat-append = 0.05%
I[14:55:23.972]   abseil-string-find-startswith = -1.23%
I[14:55:29.847]   abseil-string-find-str-contains = -0.47%
I[14:55:35.676]   abseil-time-comparison = -1.49%
I[14:55:41.798]   abseil-time-subtraction = 3.43%
I[14:55:47.911]   abseil-upgrade-duration-conversions = 3.52%
I[14:55:55.547]   altera-id-dependent-backward-branch = 29.26%
I[14:56:01.362]   altera-kernel-name-restriction = -1.51%
I[14:56:07.195]   altera-single-work-item-barrier = -1.38%
I[14:56:13.013]   altera-struct-pack-align = -1.60%
I[14:56:19.057]   altera-unroll-loops = 2.21%
I[14:56:24.935]   android-cloexec-accept = -0.85%
I[14:56:30.802]   android-cloexec-accept4 = -0.61%
I[14:56:36.663]   android-cloexec-creat = -0.77%
I[14:56:42.556]   android-cloexec-dup = -0.19%
I[14:56:48.498]   android-cloexec-epoll-create = 0.49%
I[14:56:54.439]   android-cloexec-epoll-create1 = 0.38%
I[14:57:00.388]   android-cloexec-fopen = 0.67%
I[14:57:06.329]   android-cloexec-inotify-init = 0.56%
I[14:57:12.280]   android-cloexec-inotify-init1 = 0.25%
I[14:57:18.201]   android-cloexec-memfd-create = 0.14%
I[14:57:24.180]   android-cloexec-open = 1.10%
I[14:57:30.128]   android-cloexec-pipe = 0.33%
I[14:57:36.072]   android-cloexec-pipe2 = 0.48%
I[14:57:41.991]   android-cloexec-socket = 0.11%
I[14:57:47.902]   android-comparison-in-temp-failure-retry = -0.11%
I[14:57:53.827]   boost-use-to-string = 0.23%
I[14:57:59.841]   bugprone-argument-comment = 1.73%
I[14:58:05.870]   bugprone-assert-side-effect = 1.98%
I[14:58:11.787]   bugprone-assignment-in-if-condition = -0.06%
I[14:58:17.759]   bugprone-bad-signal-to-kill-thread = 0.88%
I[14:58:23.800]   bugprone-bool-pointer-implicit-conversion = 2.28%
I[14:58:29.827]   bugprone-branch-clone = 1.85%
I[14:58:35.748]   bugprone-copy-constructor-init = 0.11%
I[14:58:41.752]   bugprone-dangling-handle = 1.61%
I[14:58:47.659]   bugprone-dynamic-static-initializers = -0.13%
I[14:58:53.849]   bugprone-easily-swappable-parameters = 4.48%
I[14:58:59.756]   bugprone-exception-escape = -0.15%
I[14:59:05.881]   bugprone-fold-init-type = 3.61%
I[14:59:11.851]   bugprone-forward-declaration-namespace = 1.15%
I[14:59:17.776]   bugprone-forwarding-reference-overload = 0.18%
I[14:59:24.046]   bugprone-implicit-widening-of-multiplication-result = 6.05%
I[14:59:29.984]   bugprone-inaccurate-erase = 0.46%
I[14:59:35.922]   bugprone-incorrect-roundings = 0.42%
I[14:59:42.053]   bugprone-infinite-loop = 3.53%
I[14:59:47.972]   bugprone-integer-division = 0.07%
I[14:59:53.980]   bugprone-lambda-function-name = 1.90%
I[14:59:59.896]   bugprone-macro-parentheses = -0.14%
I[15:00:05.809]   bugprone-macro-repeated-side-effects = -0.14%
I[15:00:11.849]   bugprone-misplaced-operator-in-strlen-in-alloc = 2.08%
I[15:00:17.760]   bugprone-misplaced-pointer-arithmetic-in-alloc = -0.07%
I[15:00:23.719]   bugprone-misplaced-widening-cast = 0.78%
I[15:00:29.638]   bugprone-move-forwarding-reference = 0.11%
I[15:00:35.721]   bugprone-multiple-statement-macro = 2.92%
I[15:00:41.793]   bugprone-narrowing-conversions = 2.74%
I[15:00:47.723]   bugprone-no-escape = 0.17%
I[15:00:54.142]   bugprone-not-null-terminated-result = 8.33%
I[15:01:00.152]   bugprone-parent-virtual-call = 1.57%
I[15:01:06.077]   bugprone-posix-return = 0.19%
I[15:01:11.943]   bugprone-redundant-branch-condition = -0.80%
I[15:01:18.014]   bugprone-reserved-identifier = 2.77%
I[15:01:23.873]   bugprone-shared-ptr-array-mismatch = -0.81%
I[15:01:29.728]   bugprone-signal-handler = -1.62%
I[15:01:35.608]   bugprone-signed-char-misuse = -0.55%
I[15:01:41.477]   bugprone-sizeof-container = -0.74%
I[15:01:47.433]   bugprone-sizeof-expression = 0.77%
I[15:01:53.557]   bugprone-spuriously-wake-up-functions = 3.48%
I[15:01:59.452]   bugprone-string-constructor = -0.50%
I[15:02:05.316]   bugprone-string-integer-assignment = -0.90%
I[15:02:11.184]   bugprone-string-literal-with-embedded-nul = -0.65%
I[15:02:17.286]   bugprone-stringview-nullptr = 3.27%
I[15:02:23.129]   bugprone-suspicious-enum-usage = -1.21%
I[15:02:28.970]   bugprone-suspicious-include = -1.22%
I[15:02:34.921]   bugprone-suspicious-memory-comparison = 0.74%
I[15:02:40.972]   bugprone-suspicious-memset-usage = 2.39%
I[15:02:46.816]   bugprone-suspicious-missing-comma = -1.37%
I[15:02:52.638]   bugprone-suspicious-realloc-usage = -1.58%
I[15:02:58.624]   bugprone-suspicious-semicolon = 1.01%
I[15:03:04.720]   bugprone-suspicious-string-compare = 3.07%
I[15:03:10.584]   bugprone-swapped-arguments = -0.95%
I[15:03:16.537]   bugprone-terminating-continue = 0.57%
I[15:03:22.416]   bugprone-throw-keyword-missing = -0.45%
I[15:03:28.288]   bugprone-too-small-loop-variable = -0.99%
I[15:03:34.217]   bugprone-unchecked-optional-access = 0.19%
I[15:03:40.179]   bugprone-undefined-memory-manipulation = 0.95%
I[15:03:46.123]   bugprone-undelegated-constructor = 0.50%
I[15:03:51.955]   bugprone-unhandled-exception-at-new = -1.36%
I[15:03:57.833]   bugprone-unhandled-self-assignment = -0.68%
I[15:04:03.855]   bugprone-unused-raii = 1.75%
I[15:04:09.925]   bugprone-unused-return-value = 2.59%
I[15:04:16.410]   bugprone-use-after-move = 9.53%
I[15:04:22.345]   bugprone-virtual-near-miss = 0.41%
I[15:04:28.518]   cert-con36-c = 3.37%
I[15:04:34.631]   cert-con54-cpp = 3.24%
I[15:04:40.624]   cert-dcl03-c = 1.32%
I[15:04:46.722]   cert-dcl16-c = 3.02%
I[15:04:52.583]   cert-dcl21-cpp = -1.00%
I[15:04:58.671]   cert-dcl37-c = 3.26%
I[15:05:04.517]   cert-dcl50-cpp = -1.29%
I[15:05:10.595]   cert-dcl51-cpp = 2.76%
I[15:05:16.415]   cert-dcl54-cpp = -1.54%
I[15:05:22.273]   cert-dcl58-cpp = -0.83%
I[15:05:28.121]   cert-dcl59-cpp = -1.09%
I[15:05:34.035]   cert-env33-c = 0.20%
I[15:05:39.921]   cert-err09-cpp = -0.64%
I[15:05:46.038]   cert-err33-c = 3.53%
I[15:05:51.971]   cert-err34-c = 0.20%
I[15:05:57.935]   cert-err52-cpp = 1.09%
I[15:06:03.781]   cert-err58-cpp = -1.11%
I[15:06:09.710]   cert-err60-cpp = 0.42%
I[15:06:15.689]   cert-err61-cpp = 1.16%
I[15:06:21.769]   cert-exp42-c = 2.86%
I[15:06:27.781]   cert-fio38-c = 1.71%
I[15:06:33.780]   cert-flp30-c = 1.39%
I[15:06:39.859]   cert-flp37-c = 2.86%
I[15:06:45.827]   cert-mem57-cpp = 0.93%
I[15:06:51.845]   cert-msc30-c = 1.59%
I[15:06:57.956]   cert-msc32-c = 3.32%
I[15:07:03.990]   cert-msc50-cpp = 2.06%
I[15:07:10.077]   cert-msc51-cpp = 2.83%
I[15:07:16.002]   cert-msc54-cpp = 0.17%
I[15:07:21.984]   cert-oop11-cpp = 1.25%
I[15:07:27.964]   cert-oop54-cpp = 1.14%
I[15:07:34.096]   cert-oop57-cpp = 3.71%
I[15:07:40.056]   cert-oop58-cpp = 0.77%
I[15:07:46.069]   cert-pos44-c = 1.75%
I[15:07:52.088]   cert-pos47-c = 1.69%
I[15:07:58.084]   cert-sig30-c = 1.25%
I[15:08:04.044]   cert-str34-c = 0.76%
I[15:08:10.870]   concurrency-mt-unsafe = 15.72%
I[15:08:16.897]   concurrency-thread-canceltype-asynchronous = 1.95%
I[15:08:22.952]   cppcoreguidelines-avoid-c-arrays = 2.35%
I[15:08:28.912]   cppcoreguidelines-avoid-const-or-ref-data-members = 0.72%
I[15:08:34.871]   cppcoreguidelines-avoid-do-while = 0.79%
I[15:08:40.889]   cppcoreguidelines-avoid-goto = 1.90%
I[15:08:46.949]   cppcoreguidelines-avoid-magic-numbers = 2.41%
I[15:08:52.913]   cppcoreguidelines-avoid-non-const-global-variables = 0.83%
I[15:08:58.996]   cppcoreguidelines-c-copy-assignment-signature = 3.04%
I[15:09:04.961]   cppcoreguidelines-explicit-virtual-functions = 0.96%
I[15:09:11.048]   cppcoreguidelines-init-variables = 3.01%
I[15:09:17.007]   cppcoreguidelines-interfaces-global-init = 0.70%
I[15:09:23.009]   cppcoreguidelines-macro-usage = 1.53%
I[15:09:29.126]   cppcoreguidelines-narrowing-conversions = 3.59%
I[15:09:35.228]   cppcoreguidelines-no-malloc = 3.36%
I[15:09:41.192]   cppcoreguidelines-non-private-member-variables-in-classes = 0.78%
I[15:09:47.503]   cppcoreguidelines-owning-memory = 6.59%
I[15:09:53.591]   cppcoreguidelines-prefer-member-initializer = 2.34%
I[15:10:00.060]   cppcoreguidelines-pro-bounds-array-to-pointer-decay = 9.07%
I[15:10:06.096]   cppcoreguidelines-pro-bounds-constant-array-index = 2.29%
I[15:10:12.050]   cppcoreguidelines-pro-bounds-pointer-arithmetic = 0.73%
I[15:10:18.007]   cppcoreguidelines-pro-type-const-cast = 0.80%
I[15:10:24.057]   cppcoreguidelines-pro-type-cstyle-cast = 2.29%
I[15:10:30.115]   cppcoreguidelines-pro-type-member-init = 2.41%
I[15:10:36.067]   cppcoreguidelines-pro-type-reinterpret-cast = 0.64%
I[15:10:42.112]   cppcoreguidelines-pro-type-static-cast-downcast = 1.88%
I[15:10:48.093]   cppcoreguidelines-pro-type-union-access = 1.14%
I[15:10:54.092]   cppcoreguidelines-pro-type-vararg = 1.31%
I[15:11:00.273]   cppcoreguidelines-slicing = 4.66%
I[15:11:06.235]   cppcoreguidelines-special-member-functions = 0.68%
I[15:11:12.207]   cppcoreguidelines-virtual-class-destructor = 1.03%
I[15:11:18.242]   darwin-avoid-spinlock = 2.00%
I[15:11:24.189]   darwin-dispatch-once-nonstatic = 0.51%
I[15:11:30.167]   fuchsia-default-arguments-calls = 1.33%
I[15:11:36.106]   fuchsia-default-arguments-declarations = 0.43%
I[15:11:42.022]   fuchsia-header-anon-namespaces = 0.21%
I[15:11:47.980]   fuchsia-multiple-inheritance = 0.91%
I[15:11:53.934]   fuchsia-overloaded-operator = 0.70%
I[15:11:59.892]   fuchsia-statically-constructed-objects = 0.56%
I[15:12:05.919]   fuchsia-trailing-return = 1.92%
I[15:12:11.864]   fuchsia-virtual-inheritance = 0.55%
I[15:12:18.022]   google-build-explicit-make-pair = 4.03%
I[15:12:23.980]   google-build-namespaces = 0.76%
I[15:12:29.939]   google-build-using-namespace = 0.73%
I[15:12:35.903]   google-default-arguments = 0.96%
I[15:12:41.914]   google-explicit-constructor = 1.65%
I[15:12:47.887]   google-global-names-in-headers = 0.95%
I[15:12:53.835]   google-objc-avoid-nsobject-new = 0.44%
I[15:12:59.781]   google-objc-avoid-throwing-exception = 0.49%
I[15:13:05.739]   google-objc-function-naming = 0.72%
I[15:13:11.690]   google-objc-global-variable-declaration = 0.76%
I[15:13:17.658]   google-readability-avoid-underscore-in-googletest-name = 0.83%
I[15:13:23.663]   google-readability-braces-around-statements = 1.32%
I[15:13:29.711]   google-readability-casting = 2.32%
I[15:13:35.758]   google-readability-function-size = 2.29%
I[15:13:41.719]   google-readability-namespace-comments = 0.78%
I[15:13:47.679]   google-readability-todo = 0.60%
I[15:13:53.669]   google-runtime-int = 1.72%
I[15:13:59.520]   google-runtime-operator = -0.82%
I[15:14:05.676]   google-upgrade-googletest-case = 4.10%
I[15:14:11.587]   hicpp-avoid-c-arrays = -0.16%
I[15:14:17.513]   hicpp-avoid-goto = 0.19%
I[15:14:23.400]   hicpp-braces-around-statements = -0.49%
I[15:14:29.237]   hicpp-deprecated-headers = -1.44%
I[15:14:35.076]   hicpp-exception-baseclass = -1.18%
I[15:14:41.036]   hicpp-explicit-conversions = 0.94%
I[15:14:47.058]   hicpp-function-size = 1.90%
I[15:14:53.634]   hicpp-invalid-access-moved = 11.37%
I[15:14:59.670]   hicpp-member-init = 1.99%
I[15:15:05.802]   hicpp-move-const-arg = 3.62%
I[15:15:11.718]   hicpp-multiway-paths-covered = 0.08%
I[15:15:17.653]   hicpp-named-parameter = 0.48%
I[15:15:23.618]   hicpp-new-delete-operators = 0.86%
I[15:15:30.027]   hicpp-no-array-decay = 8.34%
I[15:15:35.953]   hicpp-no-assembler = 0.30%
I[15:15:42.011]   hicpp-no-malloc = 2.68%
I[15:15:47.936]   hicpp-noexcept-move = 0.38%
I[15:15:53.877]   hicpp-signed-bitwise = 0.70%
I[15:15:59.810]   hicpp-special-member-functions = 0.59%
I[15:16:05.912]   hicpp-static-assert = 3.17%
I[15:16:11.907]   hicpp-undelegated-constructor = 1.37%
I[15:16:18.067]   hicpp-uppercase-literal-suffix = 4.34%
I[15:16:24.067]   hicpp-use-auto = 1.45%
I[15:16:30.259]   hicpp-use-emplace = 4.81%
I[15:16:36.259]   hicpp-use-equals-default = 1.45%
I[15:16:42.178]   hicpp-use-equals-delete = 0.14%
I[15:16:48.092]   hicpp-use-noexcept = -0.07%
I[15:16:54.148]   hicpp-use-nullptr = 2.27%
I[15:17:00.092]   hicpp-use-override = 0.52%
I[15:17:06.064]   hicpp-vararg = 1.01%
I[15:17:12.164]   linuxkernel-must-check-errs = 3.20%
I[15:17:18.158]   llvm-else-after-return = 1.68%
I[15:17:24.094]   llvm-header-guard = 0.52%
I[15:17:29.968]   llvm-include-order = -0.16%
I[15:17:35.809]   llvm-namespace-comment = -1.05%
I[15:17:41.799]   llvm-prefer-isa-or-dyn-cast-in-conditionals = 1.33%
I[15:17:47.642]   llvm-prefer-register-over-unsigned = -1.30%
I[15:17:53.578]   llvm-qualified-auto = 0.38%
I[15:17:59.450]   llvm-twine-local = -0.74%
I[15:18:05.442]   llvmlibc-callee-namespace = 1.33%
I[15:18:11.395]   llvmlibc-implementation-in-namespace = 0.96%
I[15:18:17.250]   llvmlibc-restrict-system-libc-headers = -0.91%
I[15:18:23.136]   misc-confusable-identifiers = -0.39%
I[15:20:04.112]   misc-const-correctness = 1609.95%
I[15:20:10.079]   misc-definitions-in-headers = 0.88%
I[15:20:16.024]   misc-misleading-bidirectional = 0.72%
I[15:20:21.992]   misc-misleading-identifier = 0.88%
I[15:20:27.944]   misc-misplaced-const = 0.60%
I[15:20:33.888]   misc-new-delete-overloads = 0.56%
I[15:20:39.933]   misc-no-recursion = 1.85%
I[15:20:45.844]   misc-non-copyable-objects = -0.14%
I[15:20:51.786]   misc-non-private-member-variables-in-classes = 0.59%
I[15:20:57.895]   misc-redundant-expression = 3.19%
I[15:21:03.999]   misc-static-assert = 3.17%
I[15:21:09.943]   misc-throw-by-value-catch-by-reference = 0.71%
I[15:21:15.966]   misc-unconventional-assign-operator = 1.86%
I[15:21:21.954]   misc-uniqueptr-reset-release = 1.36%
I[15:21:27.892]   misc-unused-alias-decls = 0.58%
I[15:21:33.825]   misc-unused-parameters = 0.41%
I[15:21:39.783]   misc-unused-using-decls = 0.77%
I[15:21:45.757]   modernize-avoid-bind = 1.12%
I[15:21:51.786]   modernize-avoid-c-arrays = 1.95%
I[15:21:57.694]   modernize-concat-nested-namespaces = -0.29%
I[15:22:03.548]   modernize-deprecated-headers = -0.92%
I[15:22:09.452]   modernize-deprecated-ios-base-aliases = -0.01%
I[15:22:15.479]   modernize-loop-convert = 2.01%
I[15:22:21.410]   modernize-macro-to-enum = 0.28%
I[15:22:27.297]   modernize-make-shared = -0.39%
I[15:22:33.163]   modernize-make-unique = -0.64%
I[15:22:39.015]   modernize-pass-by-value = -1.22%
I[15:22:44.959]   modernize-raw-string-literal = 0.56%
I[15:22:50.901]   modernize-redundant-void-arg = 0.40%
I[15:22:56.817]   modernize-replace-auto-ptr = 0.35%
I[15:23:02.697]   modernize-replace-disallow-copy-and-assign-macro = -0.38%
I[15:23:08.621]   modernize-replace-random-shuffle = 0.08%
I[15:23:14.599]   modernize-return-braced-init-list = 1.19%
I[15:23:20.493]   modernize-shrink-to-fit = -0.36%
I[15:23:26.337]   modernize-unary-static-assert = -1.23%
I[15:23:32.239]   modernize-use-auto = -0.17%
I[15:23:38.260]   modernize-use-bool-literals = 1.78%
I[15:23:44.142]   modernize-use-default-member-init = -0.50%
I[15:23:50.232]   modernize-use-emplace = 2.78%
I[15:23:56.190]   modernize-use-equals-default = 0.64%
I[15:24:02.048]   modernize-use-equals-delete = -0.98%
I[15:24:07.905]   modernize-use-nodiscard = -1.01%
I[15:24:13.802]   modernize-use-noexcept = -0.28%
I[15:24:19.805]   modernize-use-nullptr = 1.74%
I[15:24:25.658]   modernize-use-override = -0.99%
I[15:24:31.529]   modernize-use-trailing-return-type = -0.55%
I[15:24:37.430]   modernize-use-transparent-functors = -0.01%
I[15:24:43.380]   modernize-use-uncaught-exceptions = 0.64%
I[15:24:49.327]   modernize-use-using = 0.50%
I[15:24:55.208]   objc-assert-equals = -1.02%
I[15:25:01.027]   objc-avoid-nserror-init = -1.76%
I[15:25:06.837]   objc-dealloc-in-category = -1.88%
I[15:25:12.695]   objc-forbidden-subclassing = -0.81%
I[15:25:18.502]   objc-missing-hash = -1.62%
I[15:25:24.328]   objc-nsdate-formatter = -1.46%
I[15:25:30.161]   objc-nsinvocation-argument-lifetime = -1.34%
I[15:25:35.988]   objc-property-declaration = -1.43%
I[15:25:41.790]   objc-super-self = -1.94%
I[15:25:47.602]   openmp-exception-escape = -1.76%
I[15:25:53.433]   openmp-use-default-none = -1.36%
I[15:25:59.359]   performance-faster-string-find = -0.12%
I[15:26:05.195]   performance-for-range-copy = -1.31%
I[15:26:11.018]   performance-implicit-conversion-in-loop = -1.48%
I[15:26:16.917]   performance-inefficient-algorithm = -0.07%
I[15:26:22.843]   performance-inefficient-string-concatenation = 0.38%
I[15:26:28.693]   performance-inefficient-vector-operation = -1.03%
I[15:26:34.693]   performance-move-const-arg = 1.54%
I[15:26:40.509]   performance-move-constructor-init = -1.56%
I[15:26:46.339]   performance-no-automatic-move = -1.52%
I[15:26:52.171]   performance-no-int-to-ptr = -1.28%
I[15:26:58.034]   performance-noexcept-move-constructor = -0.74%
I[15:27:03.880]   performance-trivially-destructible = -0.95%
I[15:27:10.202]   performance-type-promotion-in-math-fn = 7.07%
I[15:27:16.262]   performance-unnecessary-copy-initialization = 2.65%
I[15:27:22.114]   performance-unnecessary-value-param = -0.94%
I[15:27:27.981]   portability-restrict-system-includes = -1.09%
I[15:27:34.000]   portability-simd-intrinsics = 1.84%
I[15:27:39.835]   portability-std-allocator-const = -1.29%
I[15:27:45.667]   readability-avoid-const-params-in-decls = -1.41%
I[15:27:51.582]   readability-braces-around-statements = 0.19%
I[15:27:57.475]   readability-const-return-type = -0.61%
I[15:28:03.314]   readability-container-contains = -1.38%
I[15:28:09.179]   readability-container-data-pointer = -0.80%
I[15:28:15.832]   readability-container-size-empty = 12.57%
I[15:28:21.814]   readability-convert-member-functions-to-static = -1.06%

[it looks like we got somehow throttled from here, as *everything* is ~10% slower]

I[15:28:28.358]   readability-delete-null-pointer = 10.63%
I[15:28:34.883]   readability-duplicate-include = 10.66%
I[15:28:41.466]   readability-else-after-return = 11.32%
I[15:28:48.054]   readability-function-cognitive-complexity = 11.38%
I[15:28:54.690]   readability-function-size = 12.32%
I[15:29:01.325]   readability-identifier-length = 12.55%
I[15:29:08.081]   readability-identifier-naming = 14.38%
I[15:29:14.875]   readability-implicit-bool-conversion = 14.85%
I[15:29:21.419]   readability-inconsistent-declaration-parameter-name = 10.82%
I[15:29:28.028]   readability-isolate-declaration = 11.83%
I[15:29:34.667]   readability-magic-numbers = 12.21%
I[15:29:41.272]   readability-make-member-function-const = 11.79%
I[15:29:47.908]   readability-misleading-indentation = 12.32%
I[15:29:54.455]   readability-misplaced-array-index = 10.67%
I[15:30:00.987]   readability-named-parameter = 10.59%
I[15:30:07.655]   readability-non-const-parameter = 12.98%
I[15:30:14.270]   readability-qualified-auto = 12.06%
I[15:30:20.814]   readability-redundant-access-specifiers = 10.62%
I[15:30:27.458]   readability-redundant-control-flow = 12.43%
I[15:30:34.063]   readability-redundant-declaration = 11.83%
I[15:30:40.574]   readability-redundant-function-ptr-dereference = 10.10%
I[15:30:47.078]   readability-redundant-member-init = 10.08%
I[15:30:53.562]   readability-redundant-preprocessor = 9.60%
I[15:31:00.520]   readability-redundant-smartptr-get = 17.26%
I[15:31:07.190]   readability-redundant-string-cstr = 12.80%
I[15:31:13.807]   readability-redundant-string-init = 12.20%
I[15:31:20.418]   readability-simplify-boolean-expr = 11.01%
I[15:31:26.926]   readability-simplify-subscript-expr = 10.07%
I[15:31:33.442]   readability-static-accessed-through-instance = 10.18%
I[15:31:39.969]   readability-static-definition-in-anonymous-namespace = 10.39%
I[15:31:46.528]   readability-string-compare = 11.01%
I[15:31:53.143]   readability-suspicious-call-argument = 11.94%
I[15:31:59.772]   readability-uniqueptr-delete-release = 11.98%
I[15:32:06.531]   readability-uppercase-literal-suffix = 14.46%
I[15:32:13.167]   readability-use-anyofallof = 12.40%
I[15:32:19.781]   zircon-temporary-objects = 12.10%
I[15:32:26.324]   Final baseline = 2179632621 ns
E[15:32:26.324] Warning: initial/final baseline differs by >2%, throttling?