This is an archive of the discontinued LLVM Phabricator instance.

[RFC] Emit note pointing to a discarded qualifier [-Wincompatible-pointer-types-discards-qualifiers]
Needs ReviewPublic

Authored by adek05 on Dec 13 2015, 11:33 PM.

Details

Summary

Finding original declaration which is suffering from discarding a qualifier is somewhat tricky and it doesn't always make sense in all of the contexts that DiagnoseAssignmentResult is called.
I haven't figured out yet how to make this change work for ObjC Setters which I think is the only place left which should handle this diagnostic.
Current approach is sort of best-effort, but doesn't really guarantee much.

Alternative way to go would be to factor this extra diagnostic out and write it in a similar fashion to DiagnoseSelfAssignment and call it in some of the contexts when note could be useful.
The problem I see with that, is that it would have issues with handling overriden assignment operators or would not work for non built-in types.

Test Plan:

$ cat test.c
int main() {
  char * c;
  char const ** c2 = &c;

  char * d;
  char volatile ** d2 = &d;
}
test.c:3:17: warning: initializing 'const char **' with an expression of type 'char **' discards qualifiers in nested pointer types [-Wincompatible-pointer-types-discards-qualifiers]
  char const ** c2 = &c;
                ^    ~~
test.c:3:3: note: nested pointer type with discarded 'const' qualifier
  char const ** c2 = &c;
  ^~~~~~~~~~~~~~~~
test.c:6:20: warning: initializing 'volatile char **' with an expression of type 'char **' discards qualifiers in nested pointer types [-Wincompatible-pointer-types-discards-qualifiers]
  char volatile ** d2 = &d;
                   ^    ~~
test.c:6:3: note: nested pointer type with discarded 'const' qualifier
  char volatile ** d2 = &d;
  ^~~~~~~~~~~~~~~~~~~

Diff Detail

Event Timeline

adek05 updated this revision to Diff 42687.Dec 13 2015, 11:33 PM
adek05 retitled this revision from to [RFC] Emit note pointing to a discarded qualifier [-Wincompatible-pointer-types-discards-qualifiers].
adek05 updated this object.
adek05 added a reviewer: jroelofs.
adek05 added a subscriber: cfe-commits.
adek05 updated this object.Dec 13 2015, 11:36 PM
adek05 updated this object.
jroelofs edited edge metadata.Dec 14 2015, 10:58 AM

Can you write up a couple of testcases for the new diagnostic? There should be some examples you can follow in clang/test/Sema ans clang/test/SemaCXX.

I'd also be interested in seeing a concrete example of the setter thing you were talking about in ObjC/ObjC++.

aaron.ballman edited edge metadata.Dec 14 2015, 1:47 PM

I would be interested in seeing test cases where this is an improvement. For instance, would it be equally as useful to pass in the type range to the existing diagnostic so that the type gets highlighted as well (and skip the note entirely)?

@aaron.ballman I think this could be hard to achieve without an extra note if you have something like:

cat test2.c
int main() {
        char *c = 'a';

        char volatile** cc = &c;
        cc = &c;
}
test2.c:2:15: warning: incompatible integer to pointer conversion initializing 'char *' with an expression of type 'int' [-Wint-conversion]
        char *c = 'a';
              ^   ~~~
test2.c:4:25: warning: initializing 'volatile char **' with an expression of type 'char **' discards qualifiers in nested pointer types [-Wincompatible-pointer-types-discards-qualifiers]
        char volatile** cc = &c;
                        ^    ~~
test2.c:4:9: note: nested pointer type with discarded 'const' qualifier
        char volatile** cc = &c;
        ^~~~~~~~~~~~~~~~~~
test2.c:5:12: warning: assigning to 'volatile char **' from 'char **' discards qualifiers in nested pointer types [-Wincompatible-pointer-types-discards-qualifiers]
        cc = &c;
           ^ ~~
3 warnings generated.

Sadly, my code doesn't print a note in the second case, which I would have to debug. In case of initialization, I think we can just print one line and omit note.
For assignment which is not an initialization, it might be useful to point to the declaration of a variable which is assigned.

I will try to handle the second case and write tests for this. Let me know if you feel like it is still worth proceeding.

adek05 added inline comments.Dec 14 2015, 5:47 PM
lib/Sema/SemaPseudoObject.cpp
739–751

This is the place which I suspect may trigger this warning, but I need to read the code more careful to come up with an example.

@aaron.ballman I think this could be hard to achieve without an extra note if you have something like:

cat test2.c
int main() {
        char *c = 'a';

        char volatile** cc = &c;
        cc = &c;
}
test2.c:2:15: warning: incompatible integer to pointer conversion initializing 'char *' with an expression of type 'int' [-Wint-conversion]
        char *c = 'a';
              ^   ~~~
test2.c:4:25: warning: initializing 'volatile char **' with an expression of type 'char **' discards qualifiers in nested pointer types [-Wincompatible-pointer-types-discards-qualifiers]
        char volatile** cc = &c;
                        ^    ~~
test2.c:4:9: note: nested pointer type with discarded 'const' qualifier
        char volatile** cc = &c;
        ^~~~~~~~~~~~~~~~~~
test2.c:5:12: warning: assigning to 'volatile char **' from 'char **' discards qualifiers in nested pointer types [-Wincompatible-pointer-types-discards-qualifiers]
        cc = &c;
           ^ ~~
3 warnings generated.

Sadly, my code doesn't print a note in the second case, which I would have to debug. In case of initialization, I think we can just print one line and omit note.
For assignment which is not an initialization, it might be useful to point to the declaration of a variable which is assigned.

I will try to handle the second case and write tests for this. Let me know if you feel like it is still worth proceeding.

I'm still not convinced this adds value as a note. The diagnostic has the type information for the destination as well as the source. A note pointing where the destination type lives doesn't seem to add any new information, but pointing out which qualifiers are discarded as part of the original diagnostic does add value. So I think that the idea has merit, but I don't think the note is the way to go. Instead, I wonder if it makes sense to just update the existing diagnostic to have further information when it is available.

Also, the note is currently incorrect -- it is claiming that the nested pointer type has a discarded const qualifier, but it has a discarded volatile qualifier. The code needs to figure out which qualifiers are discarded (and those qualifiers may be disjoint in the type specifier). Consider a type like "const char * volatile * const * restrict * const" -- the diagnostic would have to point out which qualifiers are discarded, and it could be that restrict and volatile are discarded but the consts are fine.

Perhaps as a first pass, you could handle the case where only a single qualifier is discarded, and add the qualifier and a SourceRange for that particular qualifier to the existing diagnostic. e.g., Diag(Loc, diag::warn_incompatible_qualified_id) << FirstType << SecondType << DiscardedQualifier << TypeSourceRange; Future iterations could then track multiple discarded qualifiers and their source ranges. (Note, I have no idea how easy or hard this might be.)