[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:
John Hui
2025-06-04 18:07:03 -07:00
parent 7fb85a3b67
commit c94955b571
10 changed files with 225 additions and 124 deletions

View File

@@ -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);
}
}