Cpp Core Guideline I.2, a.k.a "Avoid non-const global variables"
For detailed documentation, see:
https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#i2-avoid-non-const-global-variables
Details
- Reviewers
JonasToth aaron.ballman
Diff Detail
- Repository
- rG LLVM Github Monorepo
- Build Status
Buildable 43006 Build 43647: arc lint + arc unit
Event Timeline
clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidNonConstGlobalVariablesCheck.cpp | ||
---|---|---|
21 | This matcher already exists either as global matcher or in some other check. please refactor it out. | |
40 | const fixing is a complicated issue. I would rather not do this now, as there is currently a patch in flight that does the general case. | |
clang-tools-extra/docs/ReleaseNotes.rst | ||
97 | Please describe the check with one sentence and sync that with the check documentation. | |
clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines-avoid-non-const-global-variables.rst | ||
7 | Please adjust the documentation here as well :) | |
clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines-avoid-non-const-global-variables.cpp | ||
4 | Please add test-cases for pointers and references of builtins (int and the like) and classes/structs/enums/unions, function pointers and member function/data pointers. I am not sure about the consteval support in clang for c++20, but for each feature from a newer standard its better to have a additional test-file that will use this standard. |
Hi vingeldal,
thanks for you contribution and welcome to LLVM :)
If you have any questions or the like, feel free to ask!
Best Regards, Jonas
Updating D70265: [clang-tidy] Add clang tidy check I.2 to cppcoreguidelines
Adressed all comments:
- Extended teseting
- Updated documentation
- Removed redundant matcher which was added in initial patch
clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidNonConstGlobalVariablesCheck.cpp | ||
---|---|---|
21 | I reworked the implementation significantly and just decided I didn't need this matcher at all. | |
40 | My priority is getting a check merged, fix-it can be dropped from this patch. | |
clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines-avoid-non-const-global-variables.cpp | ||
4 | I made the test somewhat more exhaustive. Would you say consteval can be dealt with in a separate patch later, when C++20 is officially released, or would it be preferable to just hold this patch until then and incorporate that change in this patch? |
clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidNonConstGlobalVariablesCheck.cpp | ||
---|---|---|
27 | Please write the comments as full sentences with punctuation. I think ... only the data they reference .... |
clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidNonConstGlobalVariablesCheck.cpp | ||
---|---|---|
33 | The matchers here are not pointers/references. Therefore, the llvm guidelines say they should not be const. | |
65 | each of those ifs should return early, or could multiple match? if (const auto* Variable = Result.Nodes.getNodeAs<VarDecl>("non-const_variable")) { diag(...); return; } If you change the order of the ifs in the order of matches you expect to happen most, this should be a bit faster as well, as retrieving the matches introduces some overhead that is not relevant in the current situation. If you keep only one statement within the if you should ellide the braces, per coding convention. | |
clang-tools-extra/docs/ReleaseNotes.rst | ||
107 | The official name is "C++ Core Guidelines", i think we should stick to that. | |
clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines-avoid-non-const-global-variables.rst | ||
7 | Please add a link to the affected section and provide a small example (can be the example from the guidelines) what is diagnosed and why. | |
clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines-avoid-non-const-global-variables.cpp | ||
47 | diagnosing those might be undesired. maybe having an option to enable/disable this would be nice? |
Updating D70265: [clang-tidy] Add clang tidy check I.2 to cppcoreguidelines
- Added an option to not check member variables.
- Add checking for member variables referencing or pointing to non-const data.
- Added some early returns in check implementation.
- Expanded documentation, with an example and link to C++ Core Guidelines
clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidNonConstGlobalVariablesCheck.cpp | ||
---|---|---|
65 | There could be multiple matches but there could still be some early returns. namespace { There would be two warnings for i_ptr, since it is:
I'll add early returns where possible. ...Now that I think about it I realize I'v missed checking for member variables referencing or pointing to non-const data, | |
clang-tools-extra/docs/ReleaseNotes.rst | ||
107 | I'll remove the empty line, I must ask about this formatting though. | |
clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines-avoid-non-const-global-variables.cpp | ||
47 | I can see your point and I agree that an option would be a good idea. |
clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidNonConstGlobalVariablesCheck.cpp | ||
---|---|---|
30 | Why are you filtering out anonymous namespaces? | |
65 | Based on my reading of the C++ core guideline, I think there should be a different two diagnostics. One for the declaration of i and one for the declaration of i_ptr, because of this from the rule:
| |
90 | I'd sink the local variables into the if statement: if (const auto *Var = Result.Nodes.getNodeAs<VarDecl>("non-const_variable")) { ... } (If you don't want to wrap lines, you can make the string literals a bit shorter.) | |
107 | pointed to -> pointed-to | |
121 | Spurious semicolon | |
124–125 | Can re-flow this string literal. | |
131 | Spurious semicolon. | |
134 | Can re-flow this string literal. | |
135 | pointed to -> pointed-to | |
clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines-avoid-non-const-global-variables.rst | ||
10 | Add backticks around things that should be in code font like NoMembers. | |
45 | Backticks here as well. | |
53 | Rather than NoMembers, how about going with IgnoreDataMembers? | |
clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines-avoid-non-const-global-variables-NoMembers.cpp | ||
25 ↗ | (On Diff #235176) | Please add a newline to the end of the file. |
clang-tools-extra/docs/clang-tidy/checks/list.rst | ||
---|---|---|
407 | list.rst changed, you should update this! |
Updating D70265: [clang-tidy] Add clang tidy check I.2 to cppcoreguidelines
- Updated list.rst
- changed: "pointed to" -> "pointed-to"
- CHanged option name: NoMembers -> IgnoreDataMembers
- Moved matcher declaration into if-statement
- Fixed some formatting issues
- Removed forgotten FIXME-comments that were no longer relevant
clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidNonConstGlobalVariablesCheck.cpp | ||
---|---|---|
30 | If it's in an anonymous namespace it's no longer globally accessible, it's only accessible within the file it is declared. | |
65 | If the variable i was in a named namespace it would be matched by a diagnostic, i_ptr is matched by another diagnostic for pointer providing global access to non-const data (as well as the matcher for global non-const variables) | |
124–125 | Sorry, I don't understand what you mean by re-flow. | |
clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines-avoid-non-const-global-variables.rst | ||
53 | Yes, that's much better, thanks. | |
clang-tools-extra/docs/clang-tidy/checks/list.rst | ||
407 | I'm a bit concerned with this previous change of of list.rst. Now that I need to add a check to this file, I don't know what severity to give it. I can't find any definition of severity levels and I really want to avoid getting into a long discussion, or having different reviewers not agreeing on my patch, concerning what severity we should give this check. |
clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidNonConstGlobalVariablesCheck.cpp | ||
---|---|---|
30 | It is, however, at namespace scope which is what the C++ Core Guideline suggests to diagnose. Do you have evidence that this is the interpretation the guideline authors were looking for (perhaps they would like to update their suggested guidance for diagnosing)? There are two dependency issues to global variables -- one is surprising linkage interactions (where the extern nature of the symbol is an issue across module boundaries) and the other is surprising name lookup behavior within the TU (where the global nature of the symbol is an issue). e.g., namespace { int ik; } void f() { for (int ij = 0; ik < 10; ij++) { // Oops, typo, but still compiles } } That's why I think the guideline purposefully does not exclude things like anonymous namespaces. | |
124–125 | Sorry for not being clear -- the string literal spans three lines when it only needs to span two by re-writing the literal. e.g., "member variable %0 provides global access to non-const type, " "consider making the pointed-to data const" | |
clang-tools-extra/docs/clang-tidy/checks/list.rst | ||
407 | I'd like to follow that discussion so that I can be consistent with my review advice, too. |
clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidNonConstGlobalVariablesCheck.cpp | ||
---|---|---|
124–125 | Ah, yes that's looks better of course. Will fix that in coming update. |
clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidNonConstGlobalVariablesCheck.cpp | ||
---|---|---|
30 | I don't have any evidence. To me the guideline still looks a bit draft-like, so I just tried my best guess as to the intent of the guideline.
Keeping the variable in an anonymous namespace seems to deals with the issues which I interpret to be the focus of this guideline. Admittedly the wording doesn't mention an exception for anonymous namespaces, and the sentence you have referenced seems to suggest that any non-const variable in namespace scope should be matched. |
clang-tools-extra/docs/clang-tidy/checks/list.rst | ||
---|---|---|
407 | Good point! I propose this patch to explain this is a lazy copy and paste of |
clang-tools-extra/docs/clang-tidy/checks/list.rst | ||
---|---|---|
411 | It should not be there. It won't be referenced on the list. If you are uncomfortable setting a severity, leave it empty but it should clearly move above, not on the toctree |
Updating D70265: [clang-tidy] Add clang tidy check I.2 to cppcoreguidelines
- Adjusted the line breaks of some string literals.
- Modified list.rst
clang-tools-extra/docs/clang-tidy/checks/list.rst | ||
---|---|---|
407 | Great, will make sure to contribute some feedback to that. |
clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidNonConstGlobalVariablesCheck.cpp | ||
---|---|---|
30 |
This sort of thing comes up with coding guidelines from time to time and the way we usually handle it is to ask the guideline authors for guidance. If they come back with an answer, then we go that route (while the authors fix the text) and if they don't come back with an answer, we muddle our way through. Would you mind filing an issue with the C++ Core Guideline folks to see if they can weigh in on the topic? If they don't respond in a timely fashion, I think it would make more sense to err on the side of caution and diagnose declarations within anonymous namespaces. This matches the current text from the core guideline, and we can always relax the rule if we find it causes a lot of heartache in the wild. (If you have data about how often this particular aspect of the check triggers on large real-world code bases, that could help us make a decision too.) |
clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidNonConstGlobalVariablesCheck.cpp | ||
---|---|---|
30 | I sent a pull request to the guide lines, suggesting a clarification. If that is addressed in the near future I guess we can go on what they say about the pull request. If it takes to long I'll just modify the code to warn in anonymous namespace as well. |
clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidNonConstGlobalVariablesCheck.cpp | ||
---|---|---|
30 | Thank you, I think that approach makes sense. |
clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidNonConstGlobalVariablesCheck.cpp | ||
---|---|---|
111 | missing return? | |
clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines-avoid-non-const-global-variables.rst | ||
7 | I would prefer if the I.2 of C++ Core Guidelines is a link and to remove the link-string below this paragraph. | |
30 | This is technically not valid code, as the pointer needs to be initialized and a ; is missing. I think that should be fixed to not mislead inexperienced programmers. |
Updating D70265: [clang-tidy] Add clang tidy check I.2 to cppcoreguidelines
- Fixed code example in documentation to make it valid code.
- Fixed the documentation link to I.2 C++ Core Guidelines.
clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidNonConstGlobalVariablesCheck.cpp | ||
---|---|---|
111 | No, one of the below matchers may also be valid, in case we have a non-const member variable which is also a pointer or reference to non-const data. |
There are many comments not marked as done. could you please do that with addressed issues? It is hard to otherwise see whats still left.
clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidNonConstGlobalVariablesCheck.cpp | ||
---|---|---|
111 | I see. Please add a FALLTHROUGH comment or the like, as it breaks the pattern a lot and is not obvious from local code. |
clang-tools-extra/docs/clang-tidy/checks/list.rst | ||
---|---|---|
407 | It seems to me like this list is still generated the old way if one uses add_new_check.py to add a new check. |
Updating D70265: [clang-tidy] Add CppCoreGuidelines I.2 "Avoid non-const global variables" check
Created the requested pull request to C++ Core guidelines and since it is now merged this patch is updated accordingly.
- Anonymous namespaces are no longer ignored by the check as guideline editors explained that was not wanted.
- The option to ignore data members was reomved, they are now always ignored.
- Documentation was updated to mention that this check also covers C++ COre Guideline R.6, since R.6 is simply a duplication of I.2.
The pull request to C++ Core guidelines have been merged: https://github.com/isocpp/CppCoreGuidelines/pull/1553
Herb set me straight regarding the anonymous namespaces so I had to change that.
While waiting for the pull request to be reviewed I looked at more guidelines and also changed my mind about optionally ignoring data members, so I removed that option and made this check always ignore data members.
Data members are probably better dealt with in separate rules after all.
Sorry for the rather significant changes coming in this late when you all have already put so much time into reviewing, at least the check changed to be smaller in scope -which felt like the right way to go.
clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidNonConstGlobalVariablesCheck.cpp | ||
---|---|---|
56–72 | I think these cases should be combined to avoid duplication. if (const auto *VD = Result.Nodes.getNodeAs<VarDecl>("whatever")) { diag(VD->getLocation(), "variable %0 provides global access to a non-const object; considering making the %select{referenced|pointed-to}1 data 'const'") << VD << VD->getType()->isReferenceType(); return; } the matcher needs to be changed to bind to the same id for both cases. Note, this rewords the diagnostic slightly to avoid type/object confusion (the variable provides access to an object of a non-const type, not to the type itself). | |
clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines-avoid-non-const-global-variables.rst | ||
21 | This isn't legal code. | |
34 | This isn't legal code either. You may want to run the example through the compiler to catch these sort of things. |
clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidNonConstGlobalVariablesCheck.cpp | ||
---|---|---|
56–72 | You mean just the pointer and reference cases right? That matcher seems to get much more complicated, I'm having some trouble accomplishing that. Are you sure that's necessary? What would the cases of duplication be? |
clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidNonConstGlobalVariablesCheck.cpp | ||
---|---|---|
56–72 |
Yup!
Not strictly necessary, so if this turns out to be hard, I'm fine skipping it. However, I was thinking it would be something like: // Registering checks Finder->addMatcher(GlobalReferenceToNonConst.bind("qual-non-const"), this); Finder->addMatcher(GlobalPointerToNonConst.bind("qual-non-const"), this); // In check if (const auto *VD = Result.Nodes.getNodeAs<VarDecl>("qual-non-const")) { diag(VD->getLocation(), "variable %0 provides global access to a non-const object; considering making the %select{pointed-to|referenced}1 data 'const'") << VD << VD->getType()->isReferenceType(); return; } |
Made example code in documentation legal C++
Changed implementation according to comments so that both the pointer matcher
and the reference matcher now bind to the same id
clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidNonConstGlobalVariablesCheck.cpp | ||
---|---|---|
56–72 | Oh, I didn't understand I could just keep the two matchers and use the same binding string. I should have read more carefully. | |
clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines-avoid-non-const-global-variables.rst | ||
34 | Oops, will make sure and do that next time to save us all some time and me some embarrassment. |
Yes please.
Do I need to, or can I, do anything about the failing builds (https://reviews.llvm.org/B49012) first? They seem unrelated to my change but I'm not sure what caused that.
Happy to do so -- I've commit on your behalf in 512767eb3fe9c34c655a480d034147c54f1d4f85. Thank you for the new check!
Do I need to, or can I, do anything about the failing builds (https://reviews.llvm.org/B49012) first? They seem unrelated to my change but I'm not sure what caused that.
They're unrelated to your change, so there's nothing for you to do.
Hello.
2 points:
rG512767eb3fe9c34c655a480d034147c54f1d4f85 doesn't reference this review,
so it's a bit hard to find. Please try to reference it next time.
Is this check really intentionally complaining about function-scope static variables?
How are they global?
https://godbolt.org/z/2QMemL
void bar(int*); void foo() { static int a; bar(&a); }
<source>:4:14: warning: variable 'a' is non-const and globally accessible, consider making it const [cppcoreguidelines-avoid-non-const-global-variables] static int a; ^
Will do
Is this check really intentionally complaining about function-scope static variables?
How are they global?
https://godbolt.org/z/2QMemLvoid bar(int*); void foo() { static int a; bar(&a); }<source>:4:14: warning: variable 'a' is non-const and globally accessible, consider making it const [cppcoreguidelines-avoid-non-const-global-variables] static int a; ^
I think that's a false positive, I think it's caught because the variable is static, I'll submit a patch where that doesn't happen ASAP.
After looking more closely at the code I think the issue is within hasLocalStorage() which is called in hasGlobalStorage(). My expectation would be that anything inside of function scope would be considered local but I'm not very certain.
Any thoughts on whether hasLocalStorage() should be modified or if I should change the check and use some more ad-hoc implementation, instead of hasGlobalStorage(), to determine if the variable is local or global?
It's not ideal to have known-false-positive for long, so please post some fix, i think we'll figure it out there.
Not everything at function scope has local storage -- for instance, a variable declared with static or extern at local scope would not have local storage (storage != scope). isLocalVarDeclOrParm() or isLocalVarDecl() may help you out here.
This matcher already exists either as global matcher or in some other check. please refactor it out.