Skip to content

Commit f76ed37

Browse files
authored
RangeInfo: Treat comments transparently when resolving a given range. rdar://30926193 (#8052)
1 parent b6e503d commit f76ed37

File tree

3 files changed

+93
-55
lines changed

3 files changed

+93
-55
lines changed

lib/IDE/SwiftSourceDocInfo.cpp

Lines changed: 47 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "swift/IDE/CommentConversion.h"
2020
#include "swift/IDE/Utils.h"
2121
#include "swift/Markup/XMLUtils.h"
22+
#include "swift/Subsystems.h"
2223

2324
#include "clang/AST/ASTContext.h"
2425
#include "clang/AST/DeclObjC.h"
@@ -225,9 +226,11 @@ void ResolvedRangeInfo::print(llvm::raw_ostream &OS) {
225226
OS << "</Type>\n";
226227
}
227228

228-
OS << "<Context>";
229-
printContext(OS, RangeContext);
230-
OS << "</Context>\n";
229+
if (RangeContext) {
230+
OS << "<Context>";
231+
printContext(OS, RangeContext);
232+
OS << "</Context>\n";
233+
}
231234

232235
if (!HasSingleEntry) {
233236
OS << "<Entry>Multi</Entry>\n";
@@ -341,6 +344,9 @@ struct RangeResolver::Implementation {
341344
ContainedInRange(ContainedInRange) {}
342345
};
343346

347+
std::vector<Token> AllTokens;
348+
Token &StartTok;
349+
Token &EndTok;
344350
SourceLoc Start;
345351
SourceLoc End;
346352
StringRef Content;
@@ -412,43 +418,6 @@ struct RangeResolver::Implementation {
412418
return true;
413419
}
414420

415-
static SourceLoc getNonwhitespaceLocBefore(SourceManager &SM,
416-
unsigned BufferID,
417-
unsigned Offset) {
418-
CharSourceRange entireRange = SM.getRangeForBuffer(BufferID);
419-
StringRef Buffer = SM.extractText(entireRange);
420-
421-
const char *BufStart = Buffer.data();
422-
if (Offset >= Buffer.size())
423-
return SourceLoc();
424-
425-
for (unsigned Off = Offset; Off != 0; Off --) {
426-
if (!clang::isWhitespace(*(BufStart + Off))) {
427-
return SM.getLocForOffset(BufferID, Off);
428-
}
429-
}
430-
return clang::isWhitespace(*BufStart) ? SourceLoc() :
431-
SM.getLocForOffset(BufferID, 0);
432-
}
433-
434-
static SourceLoc getNonwhitespaceLocAfter(SourceManager &SM,
435-
unsigned BufferID,
436-
unsigned Offset) {
437-
CharSourceRange entireRange = SM.getRangeForBuffer(BufferID);
438-
StringRef Buffer = SM.extractText(entireRange);
439-
440-
const char *BufStart = Buffer.data();
441-
if (Offset >= Buffer.size())
442-
return SourceLoc();
443-
444-
for (unsigned Off = Offset; Off < Buffer.size(); Off ++) {
445-
if (!clang::isWhitespace(*(BufStart + Off))) {
446-
return SM.getLocForOffset(BufferID, Off);
447-
}
448-
}
449-
return SourceLoc();
450-
}
451-
452421
DeclContext *getImmediateContext() {
453422
for (auto It = ContextStack.rbegin(); It != ContextStack.rend(); It ++) {
454423
if (auto *DC = It->Parent.getAsDeclContext())
@@ -457,9 +426,14 @@ struct RangeResolver::Implementation {
457426
return static_cast<DeclContext*>(&File);
458427
}
459428

460-
Implementation(SourceFile &File, SourceLoc Start, SourceLoc End) :
461-
File(File), Ctx(File.getASTContext()), SM(Ctx.SourceMgr), Start(Start),
462-
End(End), Content(getContent()) {}
429+
Implementation(SourceFile &File, std::vector<Token> AllTokens,
430+
unsigned StartIdx, unsigned EndIdx) :
431+
File(File), Ctx(File.getASTContext()), SM(Ctx.SourceMgr),
432+
AllTokens(AllTokens), StartTok(AllTokens[StartIdx]), EndTok(AllTokens[EndIdx]),
433+
Start(StartTok.getLoc()), End(EndTok.getLoc()),
434+
Content(getContent()) {
435+
assert(Start.isValid() && End.isValid());
436+
}
463437

464438
public:
465439
bool hasResult() { return Result.hasValue(); }
@@ -490,14 +464,33 @@ struct RangeResolver::Implementation {
490464
unsigned Length) {
491465
SourceManager &SM = File.getASTContext().SourceMgr;
492466
unsigned BufferId = File.getBufferID().getValue();
493-
SourceLoc StartLoc = Implementation::getNonwhitespaceLocAfter(SM, BufferId,
494-
StartOff);
495-
SourceLoc EndLoc = Implementation::getNonwhitespaceLocBefore(SM, BufferId,
496-
StartOff + Length - 1);
497-
StartLoc = Lexer::getLocForStartOfToken(SM, StartLoc);
498-
EndLoc = Lexer::getLocForStartOfToken(SM, EndLoc);
499-
return StartLoc.isInvalid() || EndLoc.isInvalid() ? nullptr :
500-
new Implementation(File, StartLoc, EndLoc);
467+
468+
LangOptions Opts = File.getASTContext().LangOpts;
469+
Opts.AttachCommentsToDecls = true;
470+
std::vector<Token> AllTokens = tokenize(Opts, SM, BufferId, 0, 0, false);
471+
auto TokenComp = [&](Token &LHS, SourceLoc Loc) {
472+
return SM.isBeforeInBuffer(LHS.getLoc(), Loc);
473+
};
474+
475+
SourceLoc StartRaw = SM.getLocForOffset(BufferId, StartOff);
476+
SourceLoc EndRaw = SM.getLocForOffset(BufferId, StartOff + Length);
477+
478+
// This points to the first token after or on the start loc.
479+
auto StartIt = std::lower_bound(AllTokens.begin(), AllTokens.end(), StartRaw,
480+
TokenComp);
481+
// This points to the first token after or on the end loc;
482+
auto EndIt = std::lower_bound(AllTokens.begin(), AllTokens.end(), EndRaw,
483+
TokenComp);
484+
// Erroneous case.
485+
if (StartIt == AllTokens.end() || EndIt == AllTokens.begin())
486+
return nullptr;
487+
488+
// The start token is inclusive.
489+
unsigned StartIdx = StartIt - AllTokens.begin();
490+
491+
// The end token is exclusive.
492+
unsigned EndIdx = EndIt - 1 - AllTokens.begin();
493+
return new Implementation(File, std::move(AllTokens), StartIdx, EndIdx);
501494
}
502495

503496
static Implementation *createInstance(SourceFile &File, SourceLoc Start,
@@ -728,7 +721,9 @@ struct RangeResolver::Implementation {
728721

729722
StringRef getContent() {
730723
SourceManager &SM = File.getASTContext().SourceMgr;
731-
return CharSourceRange(SM, Start, Lexer::getLocForEndOfToken(SM, End)).str();
724+
return CharSourceRange(SM, StartTok.hasComment() ?
725+
StartTok.getCommentStart() : StartTok.getLoc(),
726+
Lexer::getLocForEndOfToken(SM, End)).str();
732727
}
733728
};
734729

test/IDE/range_info_comments.swift

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
func foo() -> Int{
2+
// some comments
3+
var aaa = 1 + 2
4+
aaa = aaa + 3
5+
if aaa == 3 { aaa = 4 }
6+
// some comments
7+
return aaa
8+
}
9+
10+
func foo1() -> Int{
11+
/// some comments
12+
var aaa = 1 + 2
13+
aaa = aaa + 3
14+
if aaa == 3 { aaa = 4 }
15+
/// some comments
16+
return aaa
17+
}
18+
19+
func foo2() -> Int{
20+
/* some comments*/
21+
var aaa = 1 + 2
22+
aaa = aaa + 3
23+
if aaa == 3 { aaa = 4 }
24+
/* some comments*/
25+
return aaa
26+
}
27+
28+
// RUN: %target-swift-ide-test -range -pos=2:1 -end-pos 6:19 -source-filename %s | %FileCheck %s -check-prefix=CHECK1
29+
// RUN: %target-swift-ide-test -range -pos=11:1 -end-pos 15:20 -source-filename %s | %FileCheck %s -check-prefix=CHECK-KIND
30+
// RUN: %target-swift-ide-test -range -pos=20:1 -end-pos 24:21 -source-filename %s | %FileCheck %s -check-prefix=CHECK-KIND
31+
// RUN: %target-swift-ide-test -range -pos=1:1 -end-pos 15:20 -source-filename %s | %FileCheck %s -check-prefix=CHECK-INVALID
32+
33+
// CHECK1: <Kind>MultiStatement</Kind>
34+
// CHECK1-NEXT: <Content>// some comments
35+
// CHECK1-NEXT: var aaa = 1 + 2
36+
// CHECK1-NEXT: aaa = aaa + 3
37+
// CHECK1-NEXT: if aaa == 3 { aaa = 4 }</Content>
38+
// CHECK1-NEXT: <Type>Void</Type>
39+
// CHECK1-NEXT: <Context>swift_ide_test.(file).foo()</Context>
40+
// CHECK1-NEXT: <Declared>aaa</Declared><OutscopeReference>true</OutscopeReference>
41+
// CHECK1-NEXT: <Referenced>aaa</Referenced><Type>@lvalue Int</Type>
42+
// CHECK1-NEXT: <ASTNodes>3</ASTNodes>
43+
// CHECK1-NEXT: <end>
44+
45+
// CHECK-KIND: <Kind>MultiStatement</Kind>
46+
// CHECK-INVALID: <Kind>Invalid</Kind>

test/SourceKit/RangeInfo/basic.swift

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,9 @@ struct S { func foo() {} }
1717
// RUN: %sourcekitd-test -req=range -pos=4:1 -length 25 %s -- %s | %FileCheck %s -check-prefix=CHECK3
1818
// RUN: %sourcekitd-test -req=range -pos=4:1 -length 26 %s -- %s | %FileCheck %s -check-prefix=CHECK3
1919
// RUN: %sourcekitd-test -req=range -pos=4:1 -length 27 %s -- %s | %FileCheck %s -check-prefix=CHECK3
20-
// RUN: %sourcekitd-test -req=range -pos=4:4 -length 22 %s -- %s | %FileCheck %s -check-prefix=CHECK3
2120

2221
// RUN: %sourcekitd-test -req=range -pos=5:1 -length 12 %s -- %s | %FileCheck %s -check-prefix=CHECK4
2322
// RUN: %sourcekitd-test -req=range -pos=5:2 -length 11 %s -- %s | %FileCheck %s -check-prefix=CHECK4
24-
// RUN: %sourcekitd-test -req=range -pos=5:5 -length 8 %s -- %s | %FileCheck %s -check-prefix=CHECK4
25-
// RUN: %sourcekitd-test -req=range -pos=5:5 -length 9 %s -- %s | %FileCheck %s -check-prefix=CHECK4
2623

2724
// RUN: %sourcekitd-test -req=range -pos=8:1 -length 31 %s -- %s | %FileCheck %s -check-prefix=CHECK5
2825
// RUN: %sourcekitd-test -req=range -pos=9:1 -length 25 %s -- %s | %FileCheck %s -check-prefix=CHECK6

0 commit comments

Comments
 (0)