This is an archive of the discontinued LLVM Phabricator instance.

[clangd] Enable diagnostic fixes within macro argument expansions.
ClosedPublic

Authored by sammccall on Apr 16 2020, 4:39 PM.

Details

Summary

This seems like a pretty safe case, and common enough to be useful.

Diff Detail

Event Timeline

sammccall created this revision.Apr 16 2020, 4:39 PM
Herald added a project: Restricted Project. · View Herald TranscriptApr 16 2020, 4:39 PM
hokein added inline comments.Apr 17 2020, 1:14 AM
clang-tools-extra/clangd/Diagnostics.cpp
563

I feel a bit nervous about this (even it is for macro-arg expansion only), as macro is very tricky.

I think we may result in invalid code after applying the fixits in some cases:

  1. if the fix is to remove an unused variable (interestingly, clang doesn't provide fixit to remove an unused variable, but for unused lambda capture, it does)
#define LA(arg1, arg2) [arg1, arg2] { return arg2;}
void test1(int x, int y) {
  LA(x, y); // after fixit, it would become LA(, y)? or LA(y)
}
  1. if the fix is to add some characters to the macro argument, e.g. adding a dereference *, the semantic of the code after macro expansion maybe changed.
void f1(int &a);
void f2(int *a) {
   f1(a); // clang will emits a diagnostic with a fixit adding preceding a `*` to a.
}

maybe we should be more conservative? just whitelist some diagnostics? fixing typos seems pretty safe.

sammccall marked an inline comment as done.Apr 17 2020, 4:18 AM
sammccall added inline comments.
clang-tools-extra/clangd/Diagnostics.cpp
563

your test1 example doesn't trigger this case because the fix has to delete a comma that's provided by the macro body - this patch doesn't change behavior.

To construct an example that follows this schema:

struct S { S(int *x); };
int *x;
S s1(*x); // fixit -> S s1(x);
#define CONCAT(a,b) a b
S s2(CONCAT(*, x)); // fixit -> S s2(CONCAT(, x));

The fixed code compiles fine and addresses the error in the expected way. It may not be *idiomatic*, but this is also a pathological case. I think it's at least as good to offer the fix in this case, and certainly it's not a good reason to drop support for others..


void f1(int &a);

I can't follow this example, there are no macros?
Why would the insertion change semantics?


maybe we should be more conservative? just whitelist some diagnostics? fixing typos seems pretty safe.

I think this leaves a lot of value on the table - we've been conservative so far.
The problem with whitelists is they're incomplete and outdated (e.g. we have a whitelist for include fixer that's very incomplete, and I haven't managed to get around to fixing it, and neither has anyone else).
So I think we should use this (or a blacklist) only if we can show this plausibly causes real problems.

(To put this another way: by being too aggressive we'll get more feedback, by being more conservative we'll continue to get none)

hokein added inline comments.Apr 20 2020, 6:32 AM
clang-tools-extra/clangd/Diagnostics.cpp
563

your test1 example doesn't trigger this case because the fix has to delete a comma that's provided by the macro body - this patch doesn't change behavior.

ah, you are right.

I can't follow this example, there are no macros?
Why would the insertion change semantics?

sorry, the example was incomplete, the case is like

int f1(int &a);
#define ABC(x) *x + f1(x);
void f2(int *a) {
  ABC(a); // fixit -> ABC(*a), incorrect for the `*x` in macro body.
}

if the macro argument is being used in multiple places of the macro body, it probably leads to problems. I suspect this is common in practice, we should not allow fixit in this case.

think this leaves a lot of value on the table - we've been conservative so far.

fair enough.

sammccall marked an inline comment as done.Apr 20 2020, 8:34 AM
sammccall added inline comments.
clang-tools-extra/clangd/Diagnostics.cpp
563

if the macro argument is being used in multiple places of the macro body, it probably leads to problems. I suspect this is common in practice, we should not allow fixit in this case.

This is definitely true.

As discussed offline, this is mitigated by:

  • stringified expansions are common, but don't really count for this purpose
  • fix will often make sense for all occurrences (we can't detect this), in which case not offering it is worse
  • remaining cases where fix makes sense for one case and not others probably aren't that numerous
  • behaviour in this case is to apply the fix and diagnose the resulting error from the other expansion, which isn't that terrible (as far as macro diagnostic experience goes)

Agreed to do it anyway and wait for feedback, I'll document this though.

hokein accepted this revision.Apr 20 2020, 11:44 AM
This revision is now accepted and ready to land.Apr 20 2020, 11:44 AM
This revision was automatically updated to reflect the committed changes.