-
Notifications
You must be signed in to change notification settings - Fork 15.5k
[clang] Correct FixIt ranges for unused capture warnings #141148
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
0db205b
2e67f4c
00f1e7d
357995f
e721495
2f769f0
1e1872f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -84,6 +84,21 @@ SourceLocation Sema::getLocForEndOfToken(SourceLocation Loc, unsigned Offset) { | |
| return Lexer::getLocForEndOfToken(Loc, Offset, SourceMgr, LangOpts); | ||
| } | ||
|
|
||
| SourceRange Sema::getRangeForNextToken(SourceLocation Loc, | ||
| bool IncludeComments) { | ||
| if (!Loc.isValid()) | ||
| return SourceRange(); | ||
| std::optional<Token> NextToken = | ||
| Lexer::findNextToken(Loc, SourceMgr, LangOpts, IncludeComments); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we use findLocationAfterToken here instead?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm using this one function for two purposes:
It seemed like a reasonable approach, and given we're producing a fixit diagnostic I don't think the perf matters. Basically what I'm trying to do is say given an unused capture: auto foo = [a, unused, b] ... // just a pile of white space for illustrative purposes
auto bar = [a, unused, /* ... */ b] ...
auto wibble = [a, /* ... */ unused, b] ...
auto thingy = [a, unused /* ... */, b] ...produce fixits that result in auto foo = [a, b] ... // just a pile of white space for illustrative purposes
auto bar = [a, /* ... */ b] ...
auto wibble = [a, b] ...
auto thingy = [a, b] ...and for that what we want to do is find the end of the comma, and the start of the next token, simply using the "findNextToken" to just get those tokens seems reasonable. Of course as we discussed in discord, that function doesn't handle macros, and handling this correctly for macros would imply the Sema "wrapper" excludes macro wrappers (so would not just be a wrapper) or every user has to check for macros.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @cor3ntin ping for your copious spare time :D |
||
| if (!NextToken) | ||
| return SourceRange(); | ||
| SourceLocation TokenStart = NextToken->getLocation(); | ||
| SourceLocation TokenEnd = NextToken->getLastLoc(); | ||
| if (!TokenStart.isValid() || !TokenEnd.isValid()) | ||
| return SourceRange(); | ||
|
Comment on lines
+101
to
+102
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we check TokenEnd.isMacroID() ?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yup! But that kind of results in this no longer being just a wrapper so I'm not sure if that's "good" - I might make it a flag?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @cor3ntin thoughts? basically should the Lexer function take a "allow tokens that intersect macros", should the Sema wrapper, or should - because of use case - the Sema wrapper always exclude macros? I don't particularly like the last option as it means the Sema and Lexer versions of the same function name behave differently
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @cor3ntin I've added a "include macros" parameter and removed the default parameters because I think that given the lack of existing usages there's no real reason for the default args, and they present a hazard.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looks reasonable |
||
| return SourceRange(TokenStart, TokenEnd); | ||
| } | ||
|
|
||
| ModuleLoader &Sema::getModuleLoader() const { return PP.getModuleLoader(); } | ||
|
|
||
| DarwinSDKInfo * | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2164,15 +2164,29 @@ ExprResult Sema::BuildLambdaExpr(SourceLocation StartLoc, SourceLocation EndLoc, | |
| bool IsLast = (I + 1) == LSI->NumExplicitCaptures; | ||
| SourceRange FixItRange; | ||
| if (CaptureRange.isValid()) { | ||
| auto GetTrailingEndLocation = [&](SourceLocation StartPoint) { | ||
| SourceRange NextToken = | ||
| getRangeForNextToken(StartPoint, /*IncludeComments=*/true); | ||
| if (!NextToken.isValid()) | ||
| return SourceLocation(); | ||
| // Return the last location preceding the next token | ||
| return NextToken.getBegin().getLocWithOffset(-1); | ||
| }; | ||
| if (!CurHasPreviousCapture && !IsLast) { | ||
| // If there are no captures preceding this capture, remove the | ||
| // following comma. | ||
| FixItRange = SourceRange(CaptureRange.getBegin(), | ||
| getLocForEndOfToken(CaptureRange.getEnd())); | ||
| // trailing comma and anything up to the next token | ||
| SourceRange CommaRange = | ||
| getRangeForNextToken(CaptureRange.getEnd()); | ||
| SourceLocation FixItEnd = | ||
| GetTrailingEndLocation(CommaRange.getBegin()); | ||
| FixItRange = SourceRange(CaptureRange.getBegin(), FixItEnd); | ||
| } else { | ||
| // Otherwise, remove the comma since the last used capture. | ||
| FixItRange = SourceRange(getLocForEndOfToken(PrevCaptureLoc), | ||
| CaptureRange.getEnd()); | ||
| // Otherwise, remove the comma since the last used capture, and | ||
| // anything up to the next token | ||
| SourceLocation FixItStart = getLocForEndOfToken(PrevCaptureLoc); | ||
| SourceLocation FixItEnd = | ||
| GetTrailingEndLocation(CaptureRange.getEnd()); | ||
|
||
| FixItRange = SourceRange(FixItStart, FixItEnd); | ||
| } | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,250 @@ | ||
| // RUN: cp %s %t | ||
| // RUN: %clang_cc1 -x c++ -Wunused-lambda-capture -Wno-unused-value -std=c++1z -fixit %t | ||
| // RUN: grep -v CHECK %t | FileCheck %s | ||
|
|
||
|
|
||
| #define MACRO_CAPTURE(...) __VA_ARGS__ | ||
| int main() { | ||
| int a = 0, b = 0, c = 0; | ||
| auto F0 = [a, &b]() mutable { | ||
| // CHECK: auto F0 = [a]() | ||
| a++; | ||
| }; | ||
|
|
||
| auto F1 = [&a, &b]() { | ||
| // CHECK: auto F1 = []() { | ||
| }; | ||
|
|
||
| auto F2 = [&a, b]() { | ||
| // CHECK: auto F2 = []() { | ||
| }; | ||
|
|
||
| auto F3 = [&a, | ||
| &b]() { | ||
| // CHECK: auto F3 = []() { | ||
| }; | ||
|
|
||
| auto F4 = [&a | ||
| , &b]() { | ||
| // CHECK: auto F4 = []() { | ||
| }; | ||
| auto F5 = [&a ,&b]() { | ||
| // CHECK: auto F5 = []() { | ||
| }; | ||
|
|
||
| auto F0a = [a, &b]() mutable { | ||
| // CHECK: auto F0a = [a]() mutable { | ||
| a++; | ||
| }; | ||
|
|
||
| auto F1a = [&a, &b]() { | ||
| // CHECK: auto F1a = [&a]() { | ||
| a++; | ||
| }; | ||
|
|
||
| auto F2a = [&a, b]() { | ||
| // CHECK: auto F2a = [&a]() { | ||
| a++; | ||
| }; | ||
|
|
||
| auto F3a = [&a, | ||
| &b]() { | ||
| // CHECK: auto F3a = [&a]() { | ||
| a++; | ||
| }; | ||
|
|
||
| auto F4a = [&a | ||
| , &b]() { | ||
| // CHECK: auto F4a = [&a]() { | ||
| a++; | ||
| }; | ||
|
|
||
| auto F5a = [&a ,&b]() { | ||
| // CHECK: auto F5a = [&a]() { | ||
| a++; | ||
| }; | ||
| auto F0b = [a, &b]() mutable { | ||
| // CHECK: auto F0b = [ &b]() mutable | ||
| b++; | ||
| }; | ||
|
|
||
| auto F1b = [&a, &b]() { | ||
| // CHECK: auto F1b = [ &b]() { | ||
| b++; | ||
| }; | ||
|
|
||
| auto F2b = [&a, b]() mutable { | ||
| // CHECK: auto F2b = [ b]() mutable { | ||
| b++; | ||
| }; | ||
|
|
||
| auto F3b = [&a, | ||
| &b]() { | ||
| // CHECK: auto F3b = [ &b]() { | ||
| b++; | ||
| }; | ||
|
|
||
| auto F4b = [&a | ||
| , &b]() { | ||
| // CHECK: auto F4b = [ &b]() { | ||
| b++; | ||
| }; | ||
| auto F5b = [&a ,&b]() { | ||
| // CHECK: auto F5b = [&b]() { | ||
| b++; | ||
| }; | ||
|
|
||
| auto F6 = [&a, &b, &c]() { | ||
| // CHECK: auto F6 = [&a, &b]() { | ||
| a++; | ||
| b++; | ||
| }; | ||
| auto F7 = [&a, &b, &c]() { | ||
| // CHECK: auto F7 = [&a, &c]() { | ||
| a++; | ||
| c++; | ||
| }; | ||
| auto F8 = [&a, &b, &c]() { | ||
| // CHECK: auto F8 = [ &b, &c]() { | ||
| b++; | ||
| c++; | ||
| }; | ||
| auto F9 = [&a, &b , &c]() { | ||
shafik marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| // CHECK: auto F9 = [&a , &c]() { | ||
| a++; | ||
| c++; | ||
| }; | ||
| auto F10 = [&a, | ||
| &b, &c]() { | ||
| // CHECK: auto F10 = [&a, &c]() { | ||
| a++; | ||
| c++; | ||
| }; | ||
| auto F11 = [&a, &b , | ||
| &c]() { | ||
| // CHECK: auto F11 = [&a , | ||
| // CHECK-NEXT: &c]() { | ||
| a++; | ||
| c++; | ||
| }; | ||
| auto F12 = [a = 0, b , | ||
| c]() mutable { | ||
| // CHECK: auto F12 = [ b , | ||
| // CHECK-NEXT: c]() mutable { | ||
| b++; | ||
| c++; | ||
| }; | ||
| auto F13 = [a, b = 0 , | ||
| c]() mutable { | ||
| // CHECK: auto F13 = [a , | ||
| // CHECK-NEXT: c]() mutable { | ||
| a++; | ||
| c++; | ||
| }; | ||
| auto F14 = [a, b , | ||
| c | ||
| = 0]() mutable { | ||
| // CHECK: auto F14 = [a, b]() mutable { | ||
| a++; | ||
| b++; | ||
| }; | ||
|
|
||
| // We want to remove everything including the comment | ||
| // as well as the comma following the capture of `a` | ||
| auto F15 = [&a /* comment */, &b]() { | ||
| // CHECK: auto F15 = [ &b]() { | ||
| b++; | ||
| }; | ||
|
|
||
| // The comment preceding the first capture remains. This is more | ||
| // by design of the fixit logic than anything else, but we should | ||
| // consider the preceding comment might actually be a comment for | ||
| // the entire capture set, so maybe we do want it to hang around | ||
| auto F16 = [/* comment */ &a , &b]() { | ||
| // CHECK: auto F16 = [/* comment */ &b]() { | ||
| b++; | ||
| }; | ||
|
|
||
| auto F16b = [&a , /* comment */ &b]() { | ||
| // CHECK: auto F16b = [ /* comment */ &b]() { | ||
| b++; | ||
| }; | ||
|
|
||
| auto F17 = [&a /* comment */, &b]() { | ||
| // CHECK: auto F17 = [&a]() { | ||
| a++; | ||
| }; | ||
|
|
||
| auto F18 = [&a , /* comment */ &b]() { | ||
| // CHECK: auto F18 = [&a]() { | ||
| a++; | ||
| }; | ||
|
|
||
| auto F19 = [&a /* comment */, &b /* comment */]() { | ||
| // CHECK: auto F19 = [&a /* comment */]() { | ||
| a++; | ||
| }; | ||
|
|
||
| auto F20 = [MACRO_CAPTURE(&a, &b)]() { | ||
| // CHECK: auto F20 = [MACRO_CAPTURE(&a, &b)]() { | ||
| }; | ||
|
|
||
| auto F21 = [MACRO_CAPTURE(&a), &b]() { | ||
| // CHECK: auto F21 = []() { | ||
| }; | ||
|
|
||
| auto F22 = [MACRO_CAPTURE(&a,) &b]() { | ||
| // CHECK: auto F22 = [MACRO_CAPTURE(&a,) &b]() { | ||
| }; | ||
| auto F23 = [&a MACRO_CAPTURE(, &b)]() { | ||
| // CHECK: auto F23 = []() { | ||
| }; | ||
| auto F24 = [&a, MACRO_CAPTURE(&b)]() { | ||
| // CHECK: auto F24 = []() { | ||
| }; | ||
|
|
||
| auto F20a = [MACRO_CAPTURE(&a, &b)]() { | ||
| // CHECK: auto F20a = [MACRO_CAPTURE(&a, &b)]() { | ||
| a++; | ||
| }; | ||
|
|
||
| auto F21a = [MACRO_CAPTURE(&a), &b]() { | ||
| // CHECK: auto F21a = [MACRO_CAPTURE(&a)]() { | ||
| a++; | ||
| }; | ||
|
|
||
| auto F22a = [MACRO_CAPTURE(&a,) &b]() { | ||
| // Cauto F22a = [MACRO_CAPTURE(&a,) &b]() { | ||
| a++; | ||
| }; | ||
| auto F23a = [&a MACRO_CAPTURE(, &b)]() { | ||
| // CHECK: auto F23a = [&a]() { | ||
| a++; | ||
| }; | ||
| auto F24a = [&a, MACRO_CAPTURE(&b)]() { | ||
| // CHECK: auto F24a = [&a]() { | ||
| a++; | ||
| }; | ||
| auto F20b = [MACRO_CAPTURE(&a, &b)]() { | ||
| // CHECK: auto F20b = [MACRO_CAPTURE(&a, &b)]() { | ||
| b++; | ||
| }; | ||
|
|
||
| auto F21b = [MACRO_CAPTURE(&a), &b]() { | ||
| // CHECK: auto F21b = [ &b]() { | ||
| b++; | ||
| }; | ||
|
|
||
| auto F22b = [MACRO_CAPTURE(&a,) &b]() { | ||
| // CHECK: auto F22b = [MACRO_CAPTURE(&a,) &b]() { | ||
| b++; | ||
| }; | ||
| auto F23b = [&a MACRO_CAPTURE(, &b)]() { | ||
| // CHECK: auto F23b = [(, &b)]() { | ||
| b++; | ||
| }; | ||
| auto F24b = [&a, MACRO_CAPTURE(&b)]() { | ||
| // CHECK: auto F24b = [ MACRO_CAPTURE(&b)]() { | ||
| b++; | ||
| }; | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.