Skip to content

Commit 1d56f2e

Browse files
committed
[SourceKit] Properly handle cursor info range for macro expansions
Make `getOriginalLocation` work with source ranges, and adjust the cursor info logic to map the range into the original buffer. This fixes the case where we were using bogus range lengths for macro expansion decls. rdar://151411756
1 parent 90b232c commit 1d56f2e

File tree

4 files changed

+79
-35
lines changed

4 files changed

+79
-35
lines changed

include/swift/AST/Module.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -434,10 +434,18 @@ class ModuleDecl
434434
/// \c nullptr if the source location isn't in this module.
435435
SourceFile *getSourceFileContainingLocation(SourceLoc loc);
436436

437+
// Retrieve the buffer ID and source range of the outermost node that
438+
// caused the generation of the buffer containing \p range. \p range and its
439+
// buffer if it isn't in a generated buffer or has no original range.
440+
std::pair<unsigned, SourceRange> getOriginalRange(SourceRange range) const;
441+
437442
// Retrieve the buffer ID and source location of the outermost location that
438443
// caused the generation of the buffer containing \p loc. \p loc and its
439444
// buffer if it isn't in a generated buffer or has no original location.
440-
std::pair<unsigned, SourceLoc> getOriginalLocation(SourceLoc loc) const;
445+
std::pair<unsigned, SourceLoc> getOriginalLocation(SourceLoc loc) const {
446+
auto [buffer, range] = getOriginalRange(loc);
447+
return std::make_pair(buffer, range.Start);
448+
}
441449

442450
/// Creates a map from \c #filePath strings to corresponding \c #fileID
443451
/// strings, diagnosing any conflicts.

lib/AST/Module.cpp

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -847,14 +847,14 @@ SourceFile *ModuleDecl::getSourceFileContainingLocation(SourceLoc loc) {
847847
return nullptr;
848848
}
849849

850-
std::pair<unsigned, SourceLoc>
851-
ModuleDecl::getOriginalLocation(SourceLoc loc) const {
852-
assert(loc.isValid());
850+
std::pair<unsigned, SourceRange>
851+
ModuleDecl::getOriginalRange(SourceRange range) const {
852+
assert(range.isValid());
853853

854854
SourceManager &SM = getASTContext().SourceMgr;
855-
unsigned bufferID = SM.findBufferContainingLoc(loc);
855+
unsigned bufferID = SM.findBufferContainingLoc(range.Start);
856856

857-
SourceLoc startLoc = loc;
857+
auto startRange = range;
858858
unsigned startBufferID = bufferID;
859859
while (const GeneratedSourceInfo *info =
860860
SM.getGeneratedSourceInfo(bufferID)) {
@@ -866,12 +866,12 @@ ModuleDecl::getOriginalLocation(SourceLoc loc) const {
866866
// Location was within a macro expansion, return the expansion site, not
867867
// the insertion location.
868868
if (info->attachedMacroCustomAttr) {
869-
loc = info->attachedMacroCustomAttr->getLocation();
869+
range = info->attachedMacroCustomAttr->getRange();
870870
} else {
871871
ASTNode expansionNode = ASTNode::getFromOpaqueValue(info->astNode);
872-
loc = expansionNode.getStartLoc();
872+
range = expansionNode.getSourceRange();
873873
}
874-
bufferID = SM.findBufferContainingLoc(loc);
874+
bufferID = SM.findBufferContainingLoc(range.Start);
875875
break;
876876
}
877877
case GeneratedSourceInfo::DefaultArgument:
@@ -883,11 +883,11 @@ ModuleDecl::getOriginalLocation(SourceLoc loc) const {
883883
case GeneratedSourceInfo::PrettyPrinted:
884884
case GeneratedSourceInfo::AttributeFromClang:
885885
// No original location, return the original buffer/location
886-
return {startBufferID, startLoc};
886+
return {startBufferID, startRange};
887887
}
888888
}
889889

890-
return {bufferID, loc};
890+
return {bufferID, range};
891891
}
892892

893893
ArrayRef<SourceFile *>
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// RUN: %empty-directory(%t)
2+
// REQUIRES: swift_swift_parser
3+
4+
// RUN: split-file --leading-lines %s %t
5+
6+
// RUN: %host-build-swift -swift-version 5 -emit-library -o %t/%target-library-name(MacroDefinition) -module-name=MacroDefinition %t/MacroDefinition.swift -g -no-toolchain-stdlib-rpath
7+
8+
//--- MacroDefinition.swift
9+
10+
import SwiftSyntax
11+
import SwiftSyntaxMacros
12+
13+
public struct AddFuncMacro: DeclarationMacro, PeerMacro {
14+
public static func expansion(
15+
of macro: some FreestandingMacroExpansionSyntax,
16+
in context: some MacroExpansionContext
17+
) -> [DeclSyntax] {
18+
["func foo(_ x: String) {}"]
19+
}
20+
public static func expansion(
21+
of node: AttributeSyntax,
22+
providingPeersOf declaration: some DeclSyntaxProtocol,
23+
in context: some MacroExpansionContext
24+
) throws -> [DeclSyntax] {
25+
["func foo(_ x: Int) {}"]
26+
}
27+
}
28+
29+
//--- main.swift
30+
31+
@attached(peer, names: named(foo))
32+
macro AddFuncPeer(x: Int) = #externalMacro(module: "MacroDefinition", type: "AddFuncMacro")
33+
34+
@freestanding(declaration, names: named(foo))
35+
macro AddFunc(x: Int) = #externalMacro(module: "MacroDefinition", type: "AddFuncMacro")
36+
37+
@AddFuncPeer(x: 0)
38+
#AddFunc(x: 0)
39+
40+
foo(0)
41+
// RUN: %sourcekitd-test -req=cursor %t/main.swift -pos=%(line-1):1 -- %t/main.swift -load-plugin-library %t/%target-library-name(MacroDefinition) -module-name MacroUser | %FileCheck --check-prefix PEER %s
42+
// PEER: source.lang.swift.ref.function.free ([[@LINE-5]]:1-[[@LINE-5]]:19)
43+
44+
foo("")
45+
// RUN: %sourcekitd-test -req=cursor %t/main.swift -pos=%(line-1):1 -- %t/main.swift -load-plugin-library %t/%target-library-name(MacroDefinition) -module-name MacroUser | %FileCheck --check-prefix FREESTANDING %s
46+
// FREESTANDING: source.lang.swift.ref.function.free ([[@LINE-8]]:1-[[@LINE-8]]:15)

tools/SourceKit/lib/SwiftLang/SwiftSourceDocInfo.cpp

Lines changed: 14 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -877,11 +877,6 @@ static void setLocationInfoForClangNode(ClangNode ClangNode,
877877
}
878878
}
879879

880-
static unsigned getCharLength(SourceManager &SM, SourceRange TokenRange) {
881-
SourceLoc CharEndLoc = Lexer::getLocForEndOfToken(SM, TokenRange.End);
882-
return SM.getByteDistance(TokenRange.Start, CharEndLoc);
883-
}
884-
885880
static void setLocationInfo(const ValueDecl *VD,
886881
LocationInfo &Location) {
887882
ASTContext &Ctx = VD->getASTContext();
@@ -891,29 +886,24 @@ static void setLocationInfo(const ValueDecl *VD,
891886

892887
auto Loc = VD->getLoc(/*SerializedOK=*/true);
893888
if (Loc.isValid()) {
894-
auto getParameterListRange =
895-
[&](const ValueDecl *VD) -> std::optional<unsigned> {
896-
if (auto FD = dyn_cast<AbstractFunctionDecl>(VD)) {
897-
SourceRange R = FD->getParameterListSourceRange();
898-
if (R.isValid())
899-
return getCharLength(SM, R);
900-
}
901-
return std::nullopt;
902-
};
903-
unsigned NameLen;
904-
if (auto SigLen = getParameterListRange(VD)) {
905-
NameLen = SigLen.value();
906-
} else if (VD->hasName()) {
907-
NameLen = VD->getBaseName().userFacingName().size();
908-
} else {
909-
NameLen = getCharLength(SM, Loc);
889+
// For most cases we just want the range of the name itself. One exception
890+
// is for functions, where we also want to include the parameter list.
891+
SourceRange Range = Loc;
892+
if (auto *FD = dyn_cast<AbstractFunctionDecl>(VD)) {
893+
if (auto R = FD->getParameterListSourceRange())
894+
Range = R;
910895
}
911896

912-
auto [DeclBufID, DeclLoc] =
913-
VD->getModuleContext()->getOriginalLocation(Loc);
897+
auto [DeclBufID, DeclRange] =
898+
VD->getModuleContext()->getOriginalRange(Range);
899+
900+
auto DeclCharRange =
901+
Lexer::getCharSourceRangeFromSourceRange(SM, DeclRange);
902+
auto DeclLoc = DeclCharRange.getStart();
903+
914904
Location.Filename = SM.getIdentifierForBuffer(DeclBufID);
915905
Location.Offset = SM.getLocOffsetInBuffer(DeclLoc, DeclBufID);
916-
Location.Length = NameLen;
906+
Location.Length = DeclCharRange.getByteLength();
917907
std::tie(Location.Line, Location.Column) =
918908
SM.getLineAndColumnInBuffer(DeclLoc, DeclBufID);
919909
if (auto GeneratedSourceInfo = SM.getGeneratedSourceInfo(DeclBufID)) {

0 commit comments

Comments
 (0)