This is an archive of the discontinued LLVM Phabricator instance.

[clang-tidy] Add check to catch comparisons in TEMP_FAILURE_RETRY
ClosedPublic

Authored by george.burgess.iv on Mar 29 2018, 12:32 PM.

Details

Summary

TEMP_FAILURE_RETRY(x) is a macro that executes x until (x) != -1 || errno != EINTR, and evaluates to the result of the last execution of x. It is provided by both glibc and Bionic, and sometimes is defined outside of libc.

I've been told that a somewhat common mistake people make with this is to include a condition in TEMP_FAILURE_RETRY's argument, e.g.

// Wrote
TEMP_FAILURE_RETRY(foo() == 1)
// Meant
TEMP_FAILURE_RETRY(foo()) == 1

...In the former case, the TEMP_FAILURE_RETRY is useless, since (x == 1) != -1 is always true. It will appear to work, though, since TEMP_FAILURE_RETRY will execute foo() at least once, and it'll evaluate to the value of the condition.

We do have a warning in clang to catch things like (x == y) == -1 (-Wtautological-constant-out-of-range-compare). This warns about these comparisons in C++ on Bionic. It doesn't catch them in C, since typeof(x == y) is an int in C, and it doesn't catch them when using glibc, since glibc just uses a long int to store the result of (x).

Diff Detail

Repository
rL LLVM

Event Timeline

Eugene.Zelenko added inline comments.
clang-tidy/bugprone/ComparisonInTempFailureRetryCheck.cpp
51 ↗(On Diff #140312)

Please add // namespace and remove one empty line below.

docs/ReleaseNotes.rst
60 ↗(On Diff #140312)

Please use alphabetical order in new checks list. Also rebase from trunk and use :doc: and adjust link.

docs/clang-tidy/checks/bugprone-comparison-in-temp-failure-retry.rst
6 ↗(On Diff #140312)

Please make first statement same as in Release Notes.

Eugene.Zelenko retitled this revision from Add a clang-tidy check to catch comparisons in TEMP_FAILURE_RETRY to [clang-tidy] Add check to catch comparisons in TEMP_FAILURE_RETRY.Mar 29 2018, 1:01 PM
Eugene.Zelenko added a project: Restricted Project.

Will be good idea to clarify where TEMP_FAILURE_RETRY come from.

george.burgess.iv marked 3 inline comments as done.
george.burgess.iv edited the summary of this revision. (Show Details)

Addressed feedback.

Thanks!

Will be good idea to clarify where TEMP_FAILURE_RETRY come from.

Updated the CL summary and added "TEMP_FAILURE_RETRY is a macro provided by both glibc and Bionic." to ComparisonInTempFailureRetryCheck.h. Did you have anything else in mind?

docs/clang-tidy/checks/bugprone-comparison-in-temp-failure-retry.rst
6 ↗(On Diff #140312)

Interpreted "statement" as "sentence". Please let me know if you meant to include "Having such a use...", as well

You noted, that the C++ warning would catch this case, but does not get triggered in C. Would it be wort to generalize the concern and have a warning that catch the comparison, regardless of the macro?

Do other major libraries define a similar macro but name it differently? If so, including there names would be worthwhile.

clang-tidy/bugprone/ComparisonInTempFailureRetryCheck.cpp
22 ↗(On Diff #140324)

I think this matcher already exists.

https://github.com/JonasToth/clang-tools-extra/blob/master/clang-tidy/utils/Matchers.h

isEqualityOperator would work? See the other matchers there too. If the matchers there would not do it, maybe add one there.

george.burgess.iv marked an inline comment as done.

Addressed feedback

Thanks for the feedback!

You noted, that the C++ warning would catch this case, but does not get triggered in C. Would it be wort to generalize the concern and have a warning that catch the comparison, regardless of the macro?

I'm not quite sure how we would go about that with confidence. The code we'd need to catch looks something like:

typeof(foo() == y) x;
/* snip */
bar(x == -1); // warning: comparison is pointless

If we could tell that x's type was inferred from a comparison op, we could warn here. However, if the user used x as anything but a C++ bool in /* snip */, the warning would be incorrect. We could maybe do some sort of range analysis by walking the AST, but that sounds like more like a static analyzer thing.

...And none of that would catch glibc's TEMP_FAILURE_RETRY macro, unless we wanted to apply said analysis to all integral variables.

Do other major libraries define a similar macro but name it differently? If so, including there names would be worthwhile.

Dunno. A quick search for alternatives on MacOS said "no; #define it yourself," and I know so little about Windows that I have no clue if a TEMP_FAILURE_RETRY-like macro would even be reasonable there. :)

If you have anything in mind, I'm happy to add it.

I'm not quite sure how we would go about that with confidence. The code we'd need to catch looks something like:

typeof(foo() == y) x;
/* snip */
bar(x == -1); // warning: comparison is pointless

If we could tell that x's type was inferred from a comparison op, we could warn here. However, if the user used x as anything but a C++ bool in /* snip */, the warning would be incorrect. We could maybe do some sort of range analysis by walking the AST, but that sounds like more like a static analyzer thing.

...And none of that would catch glibc's TEMP_FAILURE_RETRY macro, unless we wanted to apply said analysis to all integral variables.

Yes that sounds reasonable. As i am no good C programer i have little insight in these issues. Please correct me if i am wrong!

C89 has no bool type and no stdbool.h but it has been added to later standards? That means the generalization could theoretically be done for later C standards, because we could check if the bool typedef had been used? If yes, would the check benefit?

I do not want to overthink the whole issue, because your check addresses a valid usecase and has general appliance. But if we can do more/better, we should do it :)

If you have anything in mind, I'm happy to add it.

No clue :D I think @aaron.ballman knows C good and might be a better reviewer for this code in general :)

C89 has no bool type and no stdbool.h but it has been added to later standards? That means the generalization could theoretically be done for later C standards, because we could check if the bool typedef had been used? If yes, would the check benefit?

Sure, and we do complain about x == -1 if the type of x is _Bool, but the type of x == y appears to be an int in all of {c89, c99, c11}: https://godbolt.org/g/BciGZT :)

Alright. Thank you for clarification :)

clang-tidy/bugprone/ComparisonInTempFailureRetryCheck.cpp
75 ↗(On Diff #140459)

You can match RHS in your matcher by adding another bind for the binaryOperator.

76 ↗(On Diff #140459)

this assertion is redundant with the new matcher.

78 ↗(On Diff #140459)

You could even provide a fixit to do this. But this can be done in later patches, too.

test/clang-tidy/bugprone-comparison-in-temp-failure-retry.c
1 ↗(On Diff #140459)

Could you please add a test with control structures, like loops you used in your example?

5 ↗(On Diff #140459)

I think you could add one test, that shows using long int instead of typeof is diagnosed, given glibc uses this approach (or even copy there macro)

george.burgess.iv marked 5 inline comments as done.

Addressed feedback

clang-tidy/bugprone/ComparisonInTempFailureRetryCheck.cpp
78 ↗(On Diff #140459)

I initially tried this, but a complete solution appears to be nontrivial when nested macros start to happen. Added a FIXME nonetheless. :)

test/clang-tidy/bugprone-comparison-in-temp-failure-retry.c
1 ↗(On Diff #140459)

Added the control_flow function

5 ↗(On Diff #140459)

Added a variant with long int near the bottom.

LGTM, but @aaron.ballman, @alexfh or someone else should review it before comitting.

alexfh requested changes to this revision.Apr 5 2018, 6:51 AM

The TEMP_FAILURE_RETRY macro is specific to the GNU C library (and environments that attempt to mimic it). The generic bugprone- module is not the best place for this check. I suggest moving it to android- as other similar checks are already there (and I suspect that your interest in this check is also related to android?). Alternatively, if there are other checks specific to the GNU C library planned, we could add a new module for it.

clang-tidy/bugprone/ComparisonInTempFailureRetryCheck.cpp
77 ↗(On Diff #140675)

Diagnostic messages are not full sentences and should not start with a capital letter.

docs/clang-tidy/checks/bugprone-comparison-in-temp-failure-retry.rst
7 ↗(On Diff #140675)

The documentation should provide some context w.r.t what the TEMP_FAILURE_RETRY macro is and where it comes from (maybe also link to its documentation).

This revision now requires changes to proceed.Apr 5 2018, 6:51 AM
george.burgess.iv marked 2 inline comments as done.

Addressed feedback

Thanks for the feedback!

and I suspect that your interest in this check is also related to android?

Yup :)

Alternatively, if there are other checks specific to the GNU C library planned, we could add a new module for it.

I have nothing planned, so I'm happy with this sitting under android-.

docs/clang-tidy/checks/bugprone-comparison-in-temp-failure-retry.rst
7 ↗(On Diff #140675)

Added a paragraph after this one.

aaron.ballman accepted this revision.Apr 9 2018, 12:48 PM

LGTM with a few minor nits to be fixed.

docs/clang-tidy/checks/android-comparison-in-temp-failure-retry.rst
4 ↗(On Diff #141687)

Underlining is off here.

25 ↗(On Diff #141687)

s/Since/Because

george.burgess.iv marked 2 inline comments as done.

Address feedback

Thanks!

I plan to commit this tomorrow, to give time for any last-minute comments.

Thanks again to everyone for the review. :)

alexfh accepted this revision.Apr 10 2018, 8:41 AM

LG. Thanks!

This revision is now accepted and ready to land.Apr 10 2018, 8:41 AM
This revision was automatically updated to reflect the committed changes.