This is an archive of the discontinued LLVM Phabricator instance.

[clang-tidy] Add portability-std-allocator-const check
ClosedPublic

Authored by MaskRay on Apr 12 2022, 11:40 PM.

Details

Summary

Report use of `std::vector<const T>` (and similar containers of const
elements). These are not allowed in standard C++ due to undefined
`std::allocator<const T>`. They do not compile with libstdc++ or MSVC.
Future libc++ will remove the extension (D120996).
See docs/clang-tidy/checks/portability-std-allocator-const.rst for detail.

I have attempted clean-up in a large code base. Here are some statistics:

  • 98% are related to the container std::vector, among deque/forward_list/list/multiset/queue/set/stack/vector.
  • 24% are related to std::vector<const std::string>.
  • Both std::vector<const absl::string_view> and std::vector<const int> contribute 2%. The other contributors spread over various class types.

The check can be useful to other large code bases and may serve as an example
for future libc++ strictness improvement.

Diff Detail

Event Timeline

MaskRay created this revision.Apr 12 2022, 11:40 PM
MaskRay requested review of this revision.Apr 12 2022, 11:40 PM
Herald added a project: Restricted Project. · View Herald TranscriptApr 12 2022, 11:40 PM
Herald added a subscriber: cfe-commits. · View Herald Transcript
MaskRay edited the summary of this revision. (Show Details)Apr 12 2022, 11:51 PM

This looks nice, my main substantive question is about matching VarDecl vs TypeLoc.
Maybe there's a good reason to keep it this way but I'd like to understand why.

The rest is nits, feel free to ignore any bits you disagree with.

clang-tools-extra/clang-tidy/portability/PortabilityTidyModule.cpp
28

without the punctuation, the "t" AllocatorConstT/allocator-const-t doesn't seem so obvious IMO, I'd consider dropping it from the check name.

Also the docs and even the implementation are focused on containers, "portability-const-container" might be easier to remember/understand for people not immersed in the details here.

All up to you though.

clang-tools-extra/clang-tidy/portability/StdAllocatorConstTCheck.cpp
25 ↗(On Diff #422404)

If creating allocator<const> is always invalid, why be so careful here about which containers we're matching?

27 ↗(On Diff #422404)

we can tell the "not caught" containers from the list below, but it'd be useful for the comment to say why

28 ↗(On Diff #422404)

coupling this check to what libc++ currently supports makes it harder to understand, what's the cost of including unordered_set in this list anyway?

30 ↗(On Diff #422404)

Matching VarDecl specifically is an interesting choice. Maybe a good one: it does a good job of ensuring we diagnose at an appropriate place and only once.

Other tempting choices are:

  • typeloc(loc(...)) - requires the type to be spelled somewhere, I think this can work even in template instantiations
  • expr(hasType(...)) - problem is it's likely to find multiple matches for the same root cause

My guess is matching the TypeLoc would work better: catching some extra cases without much downside.

31 ↗(On Diff #422404)

Maybe you'd want to handle cases where the type is buried a bit, like vector& and vector* etc.

Matching TypeLoc would take care of this for you, as matchers will see the nested TypeLoc.

50 ↗(On Diff #422404)

I'm a bit concerned that the second half:

  • is jargon that will confuse some people
  • excludes MSVC (are there others?)
  • will be inaccurate once libc++ changes

Also nits: "undefined" might be confusingly close to "not defined" in the "no visible definition" sense, and "remove const to work with" reads a little oddly because it's a person that's removing but the code that's working.

What about "container using std::allocator<const T> is invalid; remove const"
or if focusing on the libc++ situation: "container using std::allocator<const T> is a libc++ extension; remove const for compatibility with other standard libraries"?

clang-tools-extra/docs/clang-tidy/checks/portability-std-allocator-const-t.rst
6 ↗(On Diff #422404)

this seems a bit standard-ese for the intro paragraph.

Maybe add a paragraph before it: "This check reports use of vector<const T> (and similar containers of const elements). These are not allowed in standard C++, and should usually be vector<T> instead."

11 ↗(On Diff #422404)

It's a little confusing to call out only libstdc++ here without context when libc++ is the odd-one out.

Maybe first mention that libc++ supports this as an extension that may be removed in the future?

And if we're mentioning libstdc++'s behavior we should probably mention MS STL too which is similar to libstdc++: https://godbolt.org/z/c4o5nc66v

21 ↗(On Diff #422404)

again, I'd probably phrase this as "only compiled with libc++"

Shouldn't this check be enabled only when libstdc++ is used?

clang-tools-extra/docs/clang-tidy/checks/portability-std-allocator-const-t.rst
6 ↗(On Diff #422404)

Please synchronize first sentence with Release Notes.

Shouldn't this check be enabled only when libstdc++ is used?

No, because we would like to enforce this in libc++ as well!

Thanks @MaskRay for pursuing this!

ldionne added inline comments.Apr 13 2022, 10:55 AM
clang-tools-extra/docs/clang-tidy/checks/portability-std-allocator-const-t.rst
11 ↗(On Diff #422404)

I think I'd rather not mention libc++ at all -- I don't know that we ever documented that extension, and since we're actively trying to remove it, it might be better to just not say anything. Or we could say "It works on libc++, but it's non-standard and it may not work in the future".

MaskRay updated this revision to Diff 422579.Apr 13 2022, 11:36 AM
MaskRay marked 10 inline comments as done.
MaskRay edited the summary of this revision. (Show Details)

address comments

MaskRay marked an inline comment as done.Apr 13 2022, 11:36 AM
MaskRay added inline comments.
clang-tools-extra/clang-tidy/portability/PortabilityTidyModule.cpp
28

Renamed to portability-std-allocator-const. The intention is to catch std::allocator. If the check gets improved to catch more cases, we won't need to rename it from std-container-const to std-allocator-const :)

clang-tools-extra/clang-tidy/portability/StdAllocatorConstTCheck.cpp
30 ↗(On Diff #422404)

Thanks for Sam's offline help. I've got a form which is able to match many cases:)

50 ↗(On Diff #422404)

Thanks for the suggestion! Adopted the libc++ oriented diagnostic.

sammccall accepted this revision.Apr 13 2022, 11:58 AM

LG from my side. It won't catch all cases, but I think it gets the common ones.

Up to you/Louis how best to describe the libc++ behavior.

clang-tools-extra/test/clang-tidy/checkers/portability-std-allocator-const.cpp
66

maybe using my_vector2 = my_vector;, with no extra diagnostics

77

This case will always be an error, but isn't diagnosed until you see an instantiation.

If it's important to catch these standalone e.g. when analyzing headers, you could adapt the matcher like:

hasUnqualifiedDesugaredType(anyOf(
  recordDecl(...), // current case
  templateSpecializationType( // when dependent, TST is canonical
    templateArgumentCountIs(1), // std::vector<const dependent>
    hasTemplateArgument(0, refersToType(qualType(isConstQualified())))
  )
))
This revision is now accepted and ready to land.Apr 13 2022, 11:58 AM
clang-tools-extra/docs/clang-tidy/checks/portability-std-allocator-const.rst
7

Please omit This check. Also please make first sentence same as in Release Notes.

MaskRay updated this revision to Diff 422619.Apr 13 2022, 12:58 PM
MaskRay marked 3 inline comments as done.
MaskRay edited the summary of this revision. (Show Details)

Catch std::vector<const dependent>

MaskRay updated this revision to Diff 422640.Apr 13 2022, 1:27 PM

improve test.
mention in the diagnostic this is a deprecated libc++ extension

clang-tools-extra/test/clang-tidy/checkers/portability-std-allocator-const.cpp
77

Thanks for Sam's offline help! Incorporated this to the matcher.

MaskRay retitled this revision from [clang-tidy] Add portability-std-allocator-const-t check to [clang-tidy] Add portability-std-allocator-const check.Apr 13 2022, 10:17 PM
MaskRay edited the summary of this revision. (Show Details)Apr 13 2022, 10:32 PM
This revision was landed with ongoing or failed builds.Apr 13 2022, 10:35 PM
This revision was automatically updated to reflect the committed changes.
thakis added a subscriber: thakis.Apr 14 2022, 3:37 AM

Looks like this breaks tests on windows: http://45.33.8.238/win/56334/step_8.txt

Please take a look and revert for now if it takes a while to fix.

Thanks Nico!

@MaskRay my guess is this is related to implicit -fdelayed-template-parsing on windows, which causes non-instantiated templates not really to be parsed.
If that's the case, best just to add -fno-delayed-template-parsing to the test and accept that these cases will be missed on windows.