-
Notifications
You must be signed in to change notification settings - Fork 10.5k
[SourceKit] Support location info for macro-expanded Clang imports #82006
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
[SourceKit] Support location info for macro-expanded Clang imports #82006
Conversation
@swift-ci please test |
ccc5e18
to
17ce04f
Compare
I don't think that's true, both |
lib/Basic/SourceLoc.cpp
Outdated
case GeneratedSourceInfo::PrettyPrinted: | ||
case GeneratedSourceInfo::DefaultArgument: | ||
case GeneratedSourceInfo::AttributeFromClang: | ||
return SourceRange(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this the behavior that we want? What happens when there is a generated default argument inside the macro generated buffer?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the current behavior, which I'm trying to preserve.
That said, I'm not sure how we should handle the case you bring up; @bnbarham do you have thoughts on this?
I believe the full statement is true, ie.
At least in the cases we have today (the only original location we care about is the file that the macro was expanded from) EDIT: Ah, I see, you were referring to the fact that neither getExternalRawLocsForDecl or getMappedLocation were using the returned location 👍 |
lib/AST/Module.cpp
Outdated
std::tie(UnderlyingBufferID, MainLoc) = | ||
D->getModuleContext()->getOriginalLocation(MainLoc); | ||
if (BufferID != UnderlyingBufferID) | ||
auto R = SM.getUnexpandedMacroRange(MainLoc); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm fine with the less generic name, but it's unfortunate we have to do another findBufferContainingLoc
when we would already have done it in getUnexpandedMacroRange
(it's not the cheapest operation ever, though IIRC we do cache the last lookup so... maybe it's fine). Is there any reason not to return the buffer id as well?
But also, this still needs to assign MainLoc
if we get a range back
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I figured this should be ok because it's cached:
Lines 521 to 525 in 17ce04f
// Check the last buffer we looked in. | |
if (auto lastBufferID = LocCache.lastBufferID) { | |
if (isInBuffer(*lastBufferID)) | |
return *lastBufferID; | |
} |
I'm happy to revert to returning the tuple if you don't want to rely on this.
// restore them if we encounter generated buffer kinds we don't care about. | ||
auto VDRange = range; | ||
auto VDBufferID = bufferID; | ||
while (auto *info = SM.getGeneratedSourceInfo(bufferID)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably worth splitting this one out into its own function here as well IMO
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I ended up refactoring the Location
-setting code into a helper (and also simplifying the loop's control flow) so that the generated buffer-chasing logic becomes the focus of setLocationInfo()
.
You're right, I somehow missed this entirely 🤦 |
17ce04f
to
3e5dbf0
Compare
@swift-ci please test |
3e5dbf0
to
3cb05d3
Compare
@swift-ci please test |
3cb05d3
to
aa6a39c
Compare
@swift-ci please test |
aa6a39c
to
637c4bf
Compare
@swift-ci please test |
637c4bf
to
33b80ba
Compare
@swift-ci please test |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
@swift-ci please test macos platform |
static void setLocationInfoForRange(SourceManager &SM, SourceRange R, | ||
unsigned BufID, | ||
LocationInfo &Location, | ||
bool presumed = false) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's here and MappedDecl
where the parameter name switches casing, would be nice to have them be consistent one way or the other
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I fixed this up (haven't pushed yet), but the MappedDecl
parameter already lives alongside mixed case names: https://github.com/swiftlang/swift/pull/82006/files#diff-d29df7eb941cda5dc4dc36df1f3220fe4468a9b58d4fa8ca251c76337f353589L8672
So maybe I should leave that as is?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I fixed this up (haven't pushed yet), but the MappedDecl parameter already lives alongside mixed case names
It was module
and MappedDecl
is new right? I'd just be locally consistent if nothing else 😅 (so mappedDecl
).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, right. But the MappedDecl
parameter in getClangSwiftAttrSourceFile()
comes from the MappedDecl
parameter in importNontrivialAttribute()
, which already has it upper-cased. I was aiming to keep the variable name consistent across the function call boundary..
// through other macro expansions. Fall back to setting location based | ||
// on VD's original source range because mapping location back to the | ||
// original file might be tricky. | ||
setLocationInfoForRange(SM, VDRange, VDBufID, Location); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What did we do here previously 🤔?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The previous behavior is buried in getOriginalRange()
, which is to just bail out and use the original SourceRange
:
Lines 879 to 886 in 00aa22f
case GeneratedSourceInfo::ReplacedFunctionBody: | |
// There's not really any "original" location for locations within | |
// replaced function bodies. The body is actually different code to the | |
// original file. | |
case GeneratedSourceInfo::PrettyPrinted: | |
case GeneratedSourceInfo::AttributeFromClang: | |
// No original location, return the original buffer/location | |
return {startBufferID, startRange}; |
Now that you mention this, I'm not sure if that is the correct behavior, but I wasn't sure whether this scenario is even reachable (ReplacedFunctionBody
would mostly be for source edits, right? So they shouldn't be embedded within other generated macro buffers?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, they shouldn't ever be embedded within a macro. TBH I would prefer checking for ReplacedFunctionBody
specifically and re-using getUnexpanded...
for the macro portion (which would also eliminate the duplicate code there).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Re-using getUnexpanded...()
will need special handling for the AttributeFromClang
case though, which is unnecessary for the two other call sites. I could push the special case logic in there, but my thinking was that by inlining getUnexpandedMacroRange()
here I could keep the complexity close to where the slightly different behavior is needed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah... :( Okay
Currently, when we jump-to-definition for decls that are macro-expanded from Clang imported decls (e.g., safe overloads generated by @_SwiftifyImport), setLocationInfo() emits a bongus location pointing to a generated buffer, leading the IDE to try to jump to a file that does not exist. The root cause here is that setLocationInfo() calls getOriginalRange() (earlier, getOriginalLocation()), which was not written to account for such cases where a macro is generated from another generated buffer whose kind is 'AttributeFromClang'. This patch fixes setLocationInfo() with some refactoring: - getOriginalRange() is inlined into setLocationInfo(), so that the generated buffer-handling logic is localized to that function. This includes how it handles buffers generated for ReplacedFunctionBody. - getOriginalLocation() is used in a couple of other places that only care about macros expanded from the same buffer (so other generated buffers not not relevant). This "macro-chasing" logic is simplified and moved from ModuleDecl::getOriginalRange() to a free-standing function, getMacroUnexpandedRange() (there is no reason for it to be a method of ModuleDecl). - GeneratedSourceInfo now carries an extra ClangNode field, which is populated by getClangSwiftAttrSourceFile() when constructing a generated buffer for an 'AttributeFromClang'. This could probably be union'ed with one or more of the other fields in the future. rdar://151020332
33b80ba
to
44aba13
Compare
@swift-ci please test |
@swift-ci please test Linux platform |
@swift-ci please test |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks!
@swift-ci please test |
1 similar comment
@swift-ci please test |
Currently, when we jump-to-definition for decls that are macro-expanded
from Clang imported decls (e.g., safe overloads generated by
@_SwiftifyImport
),setLocationInfo()
emits a bongus location pointing toa generated buffer, leading the IDE to try to jump to a file that does
not exist.
The root cause here is that
setLocationInfo()
callsgetOriginalRange()
(earlier,
getOriginalLocation()
), which was not written to account forsuch cases where a macro is generated from another generated buffer
whose kind is
AttributeFromClang
.This patch fixes
setLocationInfo()
with some refactoring:getOriginalRange()
is inlined intosetLocationInfo()
, so that thegenerated buffer-handling logic is localized to that function. This
includes how it handles buffers generated for
ReplacedFunctionBody
.getOriginalLocation()
is used in a couple of other places that onlycare about macros expanded from the same buffer (so other generated
buffers not not relevant). This "macro-chasing" logic is simplified
and moved from
ModuleDecl::getOriginalRange()
to a free-standingfunction,
getMacroUnexpandedRange()
(there is no reason for it to bea method of
ModuleDecl
).GeneratedSourceInfo
now carries an extraClangNode
field, which ispopulated by
getClangSwiftAttrSourceFile()
when constructinga generated buffer for an
AttributeFromClang
. This could probablybe union'ed with one or more of the other fields in the future.
rdar://151020332