This is an archive of the discontinued LLVM Phabricator instance.

Improve the strict prototype diagnostic behavior
ClosedPublic

Authored by aaron.ballman on May 17 2022, 10:22 AM.

Details

Summary

Post-commit feedback on https://reviews.llvm.org/D122895 pointed out that the diagnostic wording for some code was using "declaration" in a confusing way, such as:

int foo(); // warning: a function declaration without a prototype is deprecated in all versions of C and is not supported in C2x

int foo(int arg) { // warning: a function declaration without a prototype is deprecated in all versions of C and is not supported in C2x
  return 5;
}

And that we had other minor issues with the diagnostics being somewhat confusing.

This patch addresses the confusion by reworking the implementation to be a bit more simple and a bit less chatty. Specifically, it changes the warning and note diagnostics to be able to specify "declaration" or "definition" as appropriate, and it changes the function merging logic so that the function without a prototype is always what gets warned on, and the function with a prototype is sometimes what gets noted. Additionally, when diagnosing a K&R C definition that is preceded by a function without a prototype, we don't note the prior declaration, we warn on it because it will also be changing behavior in C2x.

Diff Detail

Event Timeline

aaron.ballman created this revision.May 17 2022, 10:22 AM
Herald added a project: Restricted Project. · View Herald TranscriptMay 17 2022, 10:22 AM
aaron.ballman requested review of this revision.May 17 2022, 10:22 AM
Herald added a project: Restricted Project. · View Herald TranscriptMay 17 2022, 10:22 AM

This confuses me.

Looking at behavior with default flags:

We won't emit a -Wdeprecated-non-prototype warning for int foo();, until we subsequently find int foo(int arg) { return 5; }. Since we definitely have the context of what's going on at that point, in order to have determined that there's a conflict, what prevents doing the "right thing": emitting only 1 warning (at the definition site) and 1 note (at the declaration site)?

This confuses me.

Looking at behavior with default flags:

We won't emit a -Wdeprecated-non-prototype warning for int foo();, until we subsequently find int foo(int arg) { return 5; }.

Close, but not quite. With default flags, we won't warn for int foo(); until we see a subsequent int foo(int arg) (regardless of whether it's a declaration or a definition).

Since we definitely have the context of what's going on at that point, in order to have determined that there's a conflict, what prevents doing the "right thing": emitting only 1 warning (at the definition site) and 1 note (at the declaration site)?

I'm going to continue to investigate; I recall arriving at this complex logic because I was trying to balance the deprecation warning diagnostics against the changes behavior diagnostics to keep the chattiness down. However, maybe this logic can now be simplified if I reword the diagnostic messages somewhat and rearrange some of the logic. I'll see what I can come up with.

Went with a more simple implementation approach and the diagnostic results seem to be a mild improvement in terms of wording and behavior.

aaron.ballman retitled this revision from Fix strict prototype diagnostic wording for definitions to Improve the strict prototype diagnostic behavior.May 18 2022, 4:38 AM
aaron.ballman edited the summary of this revision. (Show Details)

Good improvement, but an additional change to wording for just the zero-arg non-prototype function declaration case could help a lot. The fact that zero-arg it's only being warned about because of the "...because of" note isn't particularly clear -- especially when the "because of" isn't emitted.

So, suggestion for zero-arg-specific warning text:
this function %select{declaration|definition}0 without a prototype is deprecated before C2x, and is treated as a zero-parameter prototype in C2x, conflicting with a %select{previous|subsequent}1 declaration. Then, the note_func_decl_changes_behavior text can just be e.g.: conflicting prototype is here

That'd result in: printf 'int f();\nint f(a) int a; {return 1;}' | build/bin/clang -c -fsyntax-only -xc - showing:

<stdin>:2:5: warning: a function definition without a prototype is deprecated in all versions of C and is not supported in C2x [-Wdeprecated-non-prototype]
int f(a) int a; {return 1;}
    ^
<stdin>:1:5: warning: this function declaration without a prototype is deprecated before C2x, and is treated as a zero-parameter prototype in C2x, conflicting with a subsequent declaration [-Wdeprecated-non-prototype]
int f();
    ^
dexonsmith resigned from this revision.May 24 2022, 2:15 PM

(I’ll be happy with whatever you two sort out here!)

Update based on review feedback.

Good improvement, but an additional change to wording for just the zero-arg non-prototype function declaration case could help a lot. The fact that zero-arg it's only being warned about because of the "...because of" note isn't particularly clear -- especially when the "because of" isn't emitted.

So, suggestion for zero-arg-specific warning text:

Thanks for the suggestion! I've implemented something along these lines in this patch and I think the diagnostics are better this way.

jyknight accepted this revision.May 25 2022, 11:04 AM
jyknight added inline comments.
clang/lib/Sema/SemaDecl.cpp
3959

"when the new declaration is a definition without a prototype" perhaps?

This revision is now accepted and ready to land.May 25 2022, 11:04 AM
This revision was landed with ongoing or failed builds.May 26 2022, 5:36 AM
This revision was automatically updated to reflect the committed changes.
aaron.ballman marked an inline comment as done.
aaron.ballman marked an inline comment as done.May 26 2022, 5:36 AM
aaron.ballman added inline comments.
clang/lib/Sema/SemaDecl.cpp
3959

Done!