mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
[SourceKit] Support location info for macro-expanded Clang imports
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
(cherry picked from commit 44aba1382d)
This commit is contained in:
@@ -26,6 +26,7 @@
|
||||
#include "swift/AST/ModuleNameLookup.h"
|
||||
#include "swift/AST/NameLookup.h"
|
||||
#include "swift/AST/SwiftNameTranslation.h"
|
||||
#include "swift/Basic/Assertions.h"
|
||||
#include "swift/Basic/SourceManager.h"
|
||||
#include "swift/Frontend/Frontend.h"
|
||||
#include "swift/Frontend/PrintingDiagnosticConsumer.h"
|
||||
@@ -48,6 +49,7 @@
|
||||
#include "clang/Index/USRGeneration.h"
|
||||
#include "clang/Lex/Lexer.h"
|
||||
|
||||
#include "llvm/Support/Compiler.h"
|
||||
#include "llvm/Support/MemoryBuffer.h"
|
||||
|
||||
#include <numeric>
|
||||
@@ -877,56 +879,110 @@ static void setLocationInfoForClangNode(ClangNode ClangNode,
|
||||
}
|
||||
}
|
||||
|
||||
static void setLocationInfoForRange(SourceManager &SM, SourceRange R,
|
||||
unsigned BufID,
|
||||
LocationInfo &Location,
|
||||
bool Presumed = false) {
|
||||
CONDITIONAL_ASSERT(BufID == SM.findBufferContainingLoc(R.Start) &&
|
||||
"SourceRange R should be in BufID");
|
||||
|
||||
auto CR = Lexer::getCharSourceRangeFromSourceRange(SM, R);
|
||||
|
||||
Location.Filename = SM.getIdentifierForBuffer(BufID);
|
||||
Location.Length = CR.getByteLength();
|
||||
Location.Offset = SM.getLocOffsetInBuffer(CR.getStart(), BufID);
|
||||
|
||||
if (Presumed)
|
||||
std::tie(Location.Line, Location.Column) =
|
||||
SM.getPresumedLineAndColumnForLoc(CR.getStart(), BufID);
|
||||
else
|
||||
std::tie(Location.Line, Location.Column) =
|
||||
SM.getLineAndColumnInBuffer(CR.getStart(), BufID);
|
||||
}
|
||||
|
||||
static void setLocationInfo(const ValueDecl *VD,
|
||||
LocationInfo &Location) {
|
||||
ASTContext &Ctx = VD->getASTContext();
|
||||
SourceManager &SM = Ctx.SourceMgr;
|
||||
auto *Importer = static_cast<ClangImporter *>(Ctx.getClangModuleLoader());
|
||||
|
||||
auto ClangNode = VD->getClangNode();
|
||||
if (auto loc = VD->getLoc(/*SerializedOK=*/true)) {
|
||||
// For most cases we just want the range of the name itself; it suffices to
|
||||
// make Range from just Loc because Lexer::getCharSourceRangeFromSourceRange
|
||||
// will grow the range to encompass the end of the token at Loc.
|
||||
SourceRange range = loc;
|
||||
|
||||
auto Loc = VD->getLoc(/*SerializedOK=*/true);
|
||||
if (Loc.isValid()) {
|
||||
// For most cases we just want the range of the name itself. One exception
|
||||
// is for functions, where we also want to include the parameter list.
|
||||
SourceRange Range = Loc;
|
||||
// One exception is for functions, where we also want to include the range
|
||||
// of the parameter list.
|
||||
if (auto *FD = dyn_cast<AbstractFunctionDecl>(VD)) {
|
||||
if (auto R = FD->getParameterListSourceRange())
|
||||
Range = R;
|
||||
if (auto FDR = FD->getParameterListSourceRange())
|
||||
range = FDR;
|
||||
}
|
||||
|
||||
auto [DeclBufID, DeclRange] =
|
||||
VD->getModuleContext()->getOriginalRange(Range);
|
||||
unsigned bufID = SM.findBufferContainingLoc(range.Start);
|
||||
|
||||
auto DeclCharRange =
|
||||
Lexer::getCharSourceRangeFromSourceRange(SM, DeclRange);
|
||||
auto DeclLoc = DeclCharRange.getStart();
|
||||
// If this range is from a generated buffer, recursively "unexpand" macros
|
||||
// to chase after where the macro was originally expanded from.
|
||||
//
|
||||
// However, we don't care about certain kinds of generated buffers, so save
|
||||
// the original range and buffer ID so we can set location according to VD.
|
||||
auto VDRange = range;
|
||||
auto VDBufID = bufID;
|
||||
while (auto *info = SM.getGeneratedSourceInfo(bufID)) {
|
||||
switch (info->kind) {
|
||||
#define MACRO_ROLE(Name, Description) \
|
||||
case GeneratedSourceInfo::Name##MacroExpansion:
|
||||
#include "swift/Basic/MacroRoles.def"
|
||||
if (auto *customAttr = info->attachedMacroCustomAttr)
|
||||
range = customAttr->getRange();
|
||||
else
|
||||
range = ASTNode::getFromOpaqueValue(info->astNode).getSourceRange();
|
||||
bufID = SM.findBufferContainingLoc(range.Start);
|
||||
continue; // Continue while-loop to recursively un-expand macros
|
||||
|
||||
Location.Filename = SM.getIdentifierForBuffer(DeclBufID);
|
||||
Location.Offset = SM.getLocOffsetInBuffer(DeclLoc, DeclBufID);
|
||||
Location.Length = DeclCharRange.getByteLength();
|
||||
std::tie(Location.Line, Location.Column) =
|
||||
SM.getLineAndColumnInBuffer(DeclLoc, DeclBufID);
|
||||
if (auto GeneratedSourceInfo = SM.getGeneratedSourceInfo(DeclBufID)) {
|
||||
if (GeneratedSourceInfo->kind ==
|
||||
GeneratedSourceInfo::ReplacedFunctionBody) {
|
||||
// The location was in a temporary source buffer that just contains the
|
||||
// function body and which we created while reusing the ASTContext for
|
||||
// the rest of the file. Map the location back to the original file.
|
||||
unsigned OriginalBufID = SM.findBufferContainingLoc(
|
||||
GeneratedSourceInfo->originalSourceRange.getStart());
|
||||
auto OriginalStartOffset = SM.getLocOffsetInBuffer(
|
||||
GeneratedSourceInfo->originalSourceRange.getStart(), OriginalBufID);
|
||||
auto GeneratedStartOffset = SM.getLocOffsetInBuffer(
|
||||
GeneratedSourceInfo->generatedSourceRange.getStart(), DeclBufID);
|
||||
Location.Offset += OriginalStartOffset - GeneratedStartOffset;
|
||||
std::tie(Location.Line, Location.Column) =
|
||||
SM.getPresumedLineAndColumnForLoc(DeclLoc, DeclBufID);
|
||||
case GeneratedSourceInfo::ReplacedFunctionBody:
|
||||
if (bufID == VDBufID) {
|
||||
// The location was in a temporary source buffer that just contains
|
||||
// the function body, which we created while reusing the ASTContext
|
||||
// for the rest of the file. Set the location so that it maps back to
|
||||
// the original file.
|
||||
setLocationInfoForRange(SM, VDRange, VDBufID, Location,
|
||||
/*Presumed=*/true);
|
||||
// Adjust offset according to generated buffer.
|
||||
auto originalLoc = info->originalSourceRange.getStart();
|
||||
auto originalBufID = SM.findBufferContainingLoc(originalLoc);
|
||||
auto generatedLoc = info->generatedSourceRange.getStart();
|
||||
auto generatedBufID = SM.findBufferContainingLoc(generatedLoc);
|
||||
Location.Offset +=
|
||||
SM.getLocOffsetInBuffer(originalLoc, originalBufID) -
|
||||
SM.getLocOffsetInBuffer(generatedLoc, generatedBufID);
|
||||
} else {
|
||||
// We somehow encountered a replaced function body while looking
|
||||
// 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);
|
||||
}
|
||||
return;
|
||||
case GeneratedSourceInfo::AttributeFromClang:
|
||||
// This buffer was generated for an imported ClangNode, so set location
|
||||
// info according to that.
|
||||
if (auto node = info->clangNode)
|
||||
setLocationInfoForClangNode(node, Importer, Location);
|
||||
else
|
||||
setLocationInfoForRange(SM, VDRange, VDBufID, Location);
|
||||
return;
|
||||
case GeneratedSourceInfo::DefaultArgument:
|
||||
case GeneratedSourceInfo::PrettyPrinted:
|
||||
setLocationInfoForRange(SM, VDRange, VDBufID, Location);
|
||||
return;
|
||||
}
|
||||
llvm_unreachable("All switch cases either explicitly continue or return");
|
||||
}
|
||||
} else if (ClangNode) {
|
||||
ClangImporter *Importer =
|
||||
static_cast<ClangImporter*>(Ctx.getClangModuleLoader());
|
||||
setLocationInfoForClangNode(ClangNode, Importer, Location);
|
||||
|
||||
setLocationInfoForRange(SM, range, bufID, Location);
|
||||
} else if (auto CNode = VD->getClangNode()) {
|
||||
setLocationInfoForClangNode(CNode, Importer, Location);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user