This is an archive of the discontinued LLVM Phabricator instance.

[clang-tidy] add check modernize-use-const-instead-of-define
AbandonedPublic

Authored by AlexanderLanin on Feb 7 2017, 4:22 PM.

Details

Summary

Suggestion for a new check that will warn on #defines that should rather be constant values. Const variables should be preferred as #define does not obey type checking and scope rules.

Please feel free to criticize as strict as you like, this is my very first patch to llvm/clang. Also I'm not sure about the check name itself...

Diff Detail

Event Timeline

AlexanderLanin created this revision.Feb 7 2017, 4:22 PM
Eugene.Zelenko edited reviewers, added: alexfh, hokein, aaron.ballman, malcolm.parsons; removed: cfe-commits, Restricted Project.Feb 7 2017, 7:09 PM
Eugene.Zelenko set the repository for this revision to rL LLVM.
Eugene.Zelenko added a subscriber: cfe-commits.

Please mention this check in docs/ReleaseNotes.rst (in alphabetical order).

This check is also should be included into cppcoreguidelines module. Or may be moved there?

clang-tidy/modernize/UseConstInsteadOfDefineCheck.cpp
42

In LLVM Functions should be static, not inside anonymous namespace. Same below.

68

Unnecessary empty line.

docs/clang-tidy/checks/modernize-use-const-instead-of-define.rst
7

If I'm not mistaken, preprocessor has directives, not statements. Please also enclose #define in ``.

10

A should be capitalized. Please also use code-block.

test/clang-tidy/modernize-use-const-instead-of-define.cpp
20

Unnecessary empty line.

39

Unnecessary empty line.

malcolm.parsons added inline comments.Feb 8 2017, 2:37 AM
clang-tidy/modernize/UseConstInsteadOfDefineCheck.cpp
21

s/MacroParentheses/ConstantValues/ ?

docs/clang-tidy/checks/modernize-use-const-instead-of-define.rst
12

s/voif/void/
Why is this in a function anyway?

test/clang-tidy/modernize-use-const-instead-of-define.cpp
4

s/allthough/Although/

7

Check the message?

AlexanderLanin marked 9 inline comments as done.Feb 11 2017, 1:13 PM

Not sure about CppCoreGuidelines as several guidelines have the same rule and I only used CppCoreGuidelines as it's convenient to link a specific rule. But I can move it if you like?!

clang-tidy/modernize/UseConstInsteadOfDefineCheck.cpp
42

mhh copied from MacroParentheses check

docs/clang-tidy/checks/modernize-use-const-instead-of-define.rst
12

that's why I wrote strange example ;-)
Hope the new one is better ?!

test/clang-tidy/modernize-use-const-instead-of-define.cpp
7

I didn't check the exact message since I changed the wording several times and it's currently the same message for everything that's detected anyway. Shall I check the exact message anyway?

AlexanderLanin marked 2 inline comments as done.

Fixes as reported in review

jbcoe added a subscriber: jbcoe.Feb 11 2017, 1:27 PM
aaron.ballman edited edge metadata.Feb 12 2017, 6:07 AM

One big concern I have with this is the potential to introduce ODR violations into the user's code. We may want to limit this check to only trigger on macros that are defined in the primary source file of the TU rather than something in the include stack.

One other problem with this advice is that a constant variable isn't always the same thing as a macro replacement value. For instance, a const variable requires the ability to take the address of the variable, which means that space must be reserved for it at runtime. A frequent way around this is to use enumerator rather than constant variables for integral types. The enumerations are not prone to ODR violations and enumerators are not things with storage.

clang-tidy/modernize/UseConstInsteadOfDefineCheck.cpp
41

Make sure the comments have proper capitalization and punctuation, here and elsewhere.

43

I don't think most of these helper functions really clarify the code all that much (except for perhaps isAnyCharLiteral(), but even that's debatable). I would just fold these into the code.

83–84

I don't really agree with this comment. L'1' is a reasonable character constant and not at all strange. You should add tests for that case and perhaps clarify the comment.

86

What about string literals? e.g., #define NAME "foobar"

clang-tidy/modernize/UseConstInsteadOfDefineCheck.h
19–21

Missing punctuation at the end of the sentences in these comments.

docs/clang-tidy/checks/modernize-use-const-instead-of-define.rst
14

Extraneous ` before the comment.

Thanks for the feedback. As I'm new to this I cannot say whether checking only the file in question fits better with clang-tidy’s policy or not.
Also, I’m not sure about ODR. Of course, it’s a possibility, but isn’t the programmer responsible for that? This will be more of an issue as soon as this check provides a Fix-It solution.

As for the other part, I've checked some guidelines (without any order or selection process)
MISRA C++: Don’t use #define, use const variables; Also don’t do math on enums
CppCoreGuidelines: Don’t use #define, use constexpr variables
SEI CERT C++: No mention of #define as far as I can tell
JSF AV C++: Don’t use #define, use const variables
HIC++: Don’t use #define, use const objects (reference to JSF AV C++ and MISRA C++)

So basically they're all the same. The only question is const vs constexpr

AlexanderLanin marked 5 inline comments as done.Feb 14 2017, 2:29 PM
AlexanderLanin added inline comments.
clang-tidy/modernize/UseConstInsteadOfDefineCheck.cpp
86

Excluded (for now) due to string concatenation done by preprocessor when you write NAME1 NAME2 in your code. I've added a section into modernize-use-const-instead-of-define.rst to explain this.

AlexanderLanin marked an inline comment as done.

Applied review comments and added test cases regarding parenthesis, floats, doubles, wide chars etc

Thanks for the feedback. As I'm new to this I cannot say whether checking only the file in question fits better with clang-tidy’s policy or not.
Also, I’m not sure about ODR. Of course, it’s a possibility, but isn’t the programmer responsible for that? This will be more of an issue as soon as this check provides a Fix-It solution.

It's still an issue without the FixIt because the diagnostic guides the user to do something that may make their well-formed code into ill-formed (no diagnostic required!) code. I think the correct way to handle this is to ensure we don't diagnose macro definitions that exist in a header file, at least for C++ code.

As for the other part, I've checked some guidelines (without any order or selection process)
MISRA C++: Don’t use #define, use const variables; Also don’t do math on enums
CppCoreGuidelines: Don’t use #define, use constexpr variables
SEI CERT C++: No mention of #define as far as I can tell
JSF AV C++: Don’t use #define, use const variables
HIC++: Don’t use #define, use const objects (reference to JSF AV C++ and MISRA C++)

So basically they're all the same. The only question is const vs constexpr

I don't think the guidance in this check is incorrect, I just think the check is implemented too broadly. As for 'const' vs 'constexpr', such a decision may differ when compiling in C vs C++ mode.

alexfh removed a reviewer: alexfh.Mar 14 2018, 7:44 AM
mgehre added a subscriber: mgehre.Oct 2 2018, 3:17 PM
mgehre removed a subscriber: mgehre.
mgehre added a subscriber: mgehre.
AlexanderLanin abandoned this revision.Feb 1 2020, 1:24 PM

This is now detected by cppcoreguidelines-macro-usage, so this seems out of date.