Skip to content

Commit 40f766c

Browse files
authored
Merge pull request #22952 from nkcsgexi/cherry-pick-ide
[5.1] cherry pick recent sourcekitd/IDE fixes
2 parents 9f3db6d + e4cdc8a commit 40f766c

File tree

28 files changed

+652
-13
lines changed

28 files changed

+652
-13
lines changed

include/swift/AST/DiagnosticsCommon.def

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,12 @@ ERROR(not_implemented,none,
5050
ERROR(error_opening_output,none,
5151
"error opening '%0' for output: %1", (StringRef, StringRef))
5252

53+
ERROR(cannot_find_group_info_file,none,
54+
"cannot find group info file at path: '%0'", (StringRef))
55+
56+
ERROR(cannot_parse_group_info_file,none,
57+
"cannot parse group info file at path: '%0'", (StringRef))
58+
5359
ERROR(error_no_group_info,none,
5460
"no group info found for file: '%0'", (StringRef))
5561

include/swift/Sema/IDETypeChecking.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,28 @@ namespace swift {
161161
bool shouldPrintRequirement(ExtensionDecl *ED, StringRef Req);
162162
bool hasMergeGroup(MergeGroupKind Kind);
163163
};
164+
165+
/// Reported type for an expression. This expression is represented by offset
166+
/// length in the source buffer;
167+
struct ExpressionTypeInfo {
168+
169+
/// The start of the expression;
170+
uint32_t offset;
171+
172+
/// The length of the expression;
173+
uint32_t length;
174+
175+
/// The start of the printed type in a separately given string buffer.
176+
uint32_t typeOffset;
177+
178+
/// The length of the printed type
179+
uint32_t typeLength;
180+
};
181+
182+
/// Collect type information for every expression in \c SF; all types will
183+
/// be printed to \c OS.
184+
ArrayRef<ExpressionTypeInfo> collectExpressionType(SourceFile &SF,
185+
std::vector<ExpressionTypeInfo> &scratch, llvm::raw_ostream &OS);
164186
}
165187

166188
#endif

lib/IDE/IDETypeChecking.cpp

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
#include "swift/AST/NameLookup.h"
2424
#include "swift/AST/ProtocolConformance.h"
2525
#include "swift/Sema/IDETypeChecking.h"
26+
#include "swift/IDE/SourceEntityWalker.h"
27+
#include "swift/Parse/Lexer.h"
2628

2729
using namespace swift;
2830

@@ -577,3 +579,84 @@ collectDefaultImplementationForProtocolMembers(ProtocolDecl *PD,
577579
for (auto *IP : PD->getInheritedProtocols())
578580
HandleMembers(IP->getMembers());
579581
}
582+
583+
/// This walker will traverse the AST and report types for every expression.
584+
class ExpressionTypeCollector: public SourceEntityWalker {
585+
SourceManager &SM;
586+
unsigned int BufferId;
587+
std::vector<ExpressionTypeInfo> &Results;
588+
589+
// This is to where we print all types.
590+
llvm::raw_ostream &OS;
591+
592+
// Map from a printed type to the offset in OS where the type starts.
593+
llvm::StringMap<uint32_t> TypeOffsets;
594+
595+
// This keeps track of whether we have a type reported for a given
596+
// [offset, length].
597+
llvm::DenseMap<unsigned, llvm::DenseSet<unsigned>> AllPrintedTypes;
598+
599+
bool shouldReport(unsigned Offset, unsigned Length, Expr *E) {
600+
// We shouldn't report null types.
601+
if (E->getType().isNull())
602+
return false;
603+
604+
// If we have already reported types for this source range, we shouldn't
605+
// report again. This makes sure we always report the outtermost type of
606+
// several overlapping expressions.
607+
auto &Bucket = AllPrintedTypes[Offset];
608+
return Bucket.find(Length) == Bucket.end();
609+
}
610+
611+
// Find an existing offset in the type buffer otherwise print the type to
612+
// the buffer.
613+
uint32_t getTypeOffsets(StringRef PrintedType) {
614+
auto It = TypeOffsets.find(PrintedType);
615+
if (It == TypeOffsets.end()) {
616+
TypeOffsets[PrintedType] = OS.tell();
617+
OS << PrintedType;
618+
}
619+
return TypeOffsets[PrintedType];
620+
}
621+
622+
public:
623+
ExpressionTypeCollector(SourceFile &SF, std::vector<ExpressionTypeInfo> &Results,
624+
llvm::raw_ostream &OS): SM(SF.getASTContext().SourceMgr),
625+
BufferId(*SF.getBufferID()),
626+
Results(Results), OS(OS) {}
627+
bool walkToExprPre(Expr *E) override {
628+
if (E->getSourceRange().isInvalid())
629+
return true;
630+
CharSourceRange Range =
631+
Lexer::getCharSourceRangeFromSourceRange(SM, E->getSourceRange());
632+
unsigned Offset = SM.getLocOffsetInBuffer(Range.getStart(), BufferId);
633+
unsigned Length = Range.getByteLength();
634+
if (!shouldReport(Offset, Length, E))
635+
return true;
636+
// Print the type to a temporary buffer.
637+
SmallString<64> Buffer;
638+
{
639+
llvm::raw_svector_ostream OS(Buffer);
640+
E->getType()->getRValueType()->reconstituteSugar(true)->print(OS);
641+
// Ensure the end user can directly use the char*
642+
OS << '\0';
643+
}
644+
645+
// Add the type information to the result list.
646+
Results.push_back({Offset, Length, getTypeOffsets(Buffer.str()),
647+
static_cast<uint32_t>(Buffer.size()) - 1});
648+
649+
// Keep track of that we have a type reported for this range.
650+
AllPrintedTypes[Offset].insert(Length);
651+
return true;
652+
}
653+
};
654+
655+
ArrayRef<ExpressionTypeInfo>
656+
swift::collectExpressionType(SourceFile &SF,
657+
std::vector<ExpressionTypeInfo> &Scratch,
658+
llvm::raw_ostream &OS) {
659+
ExpressionTypeCollector Walker(SF, Scratch, OS);
660+
Walker.walk(SF);
661+
return Scratch;
662+
}

lib/IDE/Refactoring.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1909,8 +1909,7 @@ class ExpandableAssignTernaryExprInfo: public ExpandableTernaryExprInfo {
19091909
IfExpr *getIf() {
19101910
if (!Assign)
19111911
return nullptr;
1912-
1913-
return dyn_cast<IfExpr>(Assign->getSrc());
1912+
return dyn_cast_or_null<IfExpr>(Assign->getSrc());
19141913
}
19151914

19161915
SourceRange getNameRange() {

lib/Serialization/SerializeDoc.cpp

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ using pFileNameToGroupNameMap = std::unique_ptr<FileNameToGroupNameMap>;
3838

3939
namespace {
4040
class YamlGroupInputParser {
41+
ASTContext &Ctx;
4142
StringRef RecordPath;
4243
static constexpr const char * const Separator = "/";
4344

@@ -86,12 +87,20 @@ class YamlGroupInputParser {
8687
}
8788

8889
public:
89-
YamlGroupInputParser(StringRef RecordPath): RecordPath(RecordPath) {}
90+
YamlGroupInputParser(ASTContext &Ctx, StringRef RecordPath):
91+
Ctx(Ctx), RecordPath(RecordPath) {}
9092

9193
FileNameToGroupNameMap* getParsedMap() {
9294
return AllMaps[RecordPath].get();
9395
}
9496

97+
bool diagnoseGroupInfoFile(bool FileMissing = false) {
98+
Ctx.Diags.diagnose(SourceLoc(),
99+
FileMissing ? diag::cannot_find_group_info_file:
100+
diag::cannot_parse_group_info_file, RecordPath);
101+
return true;
102+
}
103+
95104
// Parse the Yaml file that contains the group information.
96105
// True on failure; false on success.
97106
bool parse() {
@@ -102,32 +111,31 @@ class YamlGroupInputParser {
102111

103112
auto Buffer = llvm::MemoryBuffer::getFile(RecordPath);
104113
if (!Buffer) {
105-
// The group info file does not exist.
106-
return true;
114+
return diagnoseGroupInfoFile(/*Missing File*/true);
107115
}
108116
llvm::SourceMgr SM;
109117
llvm::yaml::Stream YAMLStream(Buffer.get()->getMemBufferRef(), SM);
110118
llvm::yaml::document_iterator I = YAMLStream.begin();
111119
if (I == YAMLStream.end()) {
112120
// Cannot parse correctly.
113-
return true;
121+
return diagnoseGroupInfoFile();
114122
}
115123
llvm::yaml::Node *Root = I->getRoot();
116124
if (!Root) {
117125
// Cannot parse correctly.
118-
return true;
126+
return diagnoseGroupInfoFile();
119127
}
120128

121129
// The format is a map of ("group0" : ["file1", "file2"]), meaning all
122130
// symbols from file1 and file2 belong to "group0".
123131
auto *Map = dyn_cast<llvm::yaml::MappingNode>(Root);
124132
if (!Map) {
125-
return true;
133+
return diagnoseGroupInfoFile();
126134
}
127135
pFileNameToGroupNameMap pMap(new FileNameToGroupNameMap());
128136
std::string Empty;
129137
if (parseRoot(*pMap, Root, Empty))
130-
return true;
138+
return diagnoseGroupInfoFile();
131139

132140
// Save the parsed map to the owner.
133141
AllMaps[RecordPath] = std::move(pMap);
@@ -168,7 +176,7 @@ class DeclGroupNameContext {
168176
StringRef FullPath =
169177
Ctx.SourceMgr.getIdentifierForBuffer(PathOp.getValue());
170178
if (!pMap) {
171-
YamlGroupInputParser Parser(RecordPath);
179+
YamlGroupInputParser Parser(Ctx, RecordPath);
172180
if (!Parser.parse()) {
173181

174182
// Get the file-name to group map if parsing correctly.

test/IDE/Inputs/ExprType.swift

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
2+
func foo() -> Int { return 1 }
3+
4+
func bar(f: Float) -> Float { return bar(f: 1) }
5+
6+
protocol P {}
7+
8+
func fooP(_ p: P) { fooP(p) }
9+
10+
class C {}
11+
12+
func ArrayC(_ a: [C]) {
13+
_ = a.count
14+
_ = a.description.count.advanced(by: 1).description
15+
}
16+
17+
struct S {
18+
let val = 4
19+
}
20+
func DictS(_ a: [Int: S]) {
21+
_ = a[2]?.val.advanced(by: 1).byteSwapped
22+
}

test/IDE/expr_type.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// RUN: %target-swift-ide-test -print-expr-type -source-filename %S/Inputs/ExprType.swift -swift-version 5 | %FileCheck %s
2+
3+
// CHECK: func foo() -> Int { return <expr type:"Int">1</expr> }
4+
// CHECK: func bar(f: Float) -> Float { return <expr type:"Float"><expr type:"(Float) -> Float">bar</expr><expr type:"(f: Float)">(f: <expr type:"Float">1</expr>)</expr></expr> }
5+
// CHECK: func fooP(_ p: P) { <expr type:"()"><expr type:"(P) -> ()">fooP</expr><expr type:"(P)">(<expr type:"P">p</expr>)</expr></expr> }
6+
// CHECK: <expr type:"()"><expr type:"Int">_</expr> = <expr type:"Int"><expr type:"[C]">a</expr>.count</expr></expr>
7+
// CHECK: <expr type:"()"><expr type:"String">_</expr> = <expr type:"String"><expr type:"Int"><expr type:"(Int) -> Int"><expr type:"Int"><expr type:"String"><expr type:"[C]">a</expr>.description</expr>.count</expr>.<expr type:"(Int) -> (Int) -> Int">advanced</expr></expr><expr type:"(by: Int)">(by: <expr type:"Int">1</expr>)</expr></expr>.description</expr></expr>
8+
// CHECK: <expr type:"()"><expr type:"Int?">_</expr> = <expr type:"Int?"><expr type:"Int"><expr type:"(Int) -> Int"><expr type:"Int"><expr type:"S"><expr type:"S?"><expr type:"[Int : S]">a</expr><expr type:"(Int)">[<expr type:"Int">2</expr>]</expr></expr>?</expr>.val</expr>.<expr type:"(Int) -> (Int) -> Int">advanced</expr></expr><expr type:"(by: Int)">(by: <expr type:"Int">1</expr>)</expr></expr>.byteSwapped</expr></expr>
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
corrupteddata
3+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: not %target-swift-frontend -emit-module %s -module-name HasArray -o %t -module-cache-path %t/mcp -group-info-path %t/nonexist.json -emit-module-doc &> %t/result.txt
3+
// RUN: %FileCheck %s -check-prefix=MISSING < %t/result.txt
4+
// RUN: not %target-swift-frontend -emit-module %s -module-name HasArray -o %t -module-cache-path %t/mcp -group-info-path %S/Inputs/corrupted_group_info.json -emit-module-doc &> %t/result.txt
5+
// RUN: %FileCheck %s -check-prefix=CORRUPTED < %t/result.txt
6+
7+
public protocol P {}
8+
9+
// MISSING: error: cannot find group info file at path
10+
// CORRUPTED: error: cannot parse group info file at path
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
func foo() -> Int { return 1 }
2+
3+
func bar(f: Float) -> Float { return bar(f: 1) }
4+
5+
protocol P {}
6+
7+
func fooP(_ p: P) { fooP(p) }
8+
9+
class C {}
10+
11+
func ArrayC(_ a: [C]) {
12+
_ = a.count
13+
_ = a.description.count.advanced(by: 1).description
14+
}
15+
16+
struct S {
17+
let val = 4
18+
}
19+
func DictS(_ a: [Int: S]) {
20+
_ = a[2]?.val.advanced(by: 1).byteSwapped
21+
}
22+
23+
// RUN: %sourcekitd-test -req=collect-type %s -- %s | %FileCheck %s
24+
// CHECK: (183, 202): Int
25+
// CHECK: (183, 196): String
26+
// CHECK: (183, 184): [C]
27+
// CHECK: (203, 211): (Int) -> (Int) -> Int
28+
// CHECK: (211, 218): (by: Int)
29+
// CHECK: (216, 217): Int
30+
// CHECK: (257, 258): Int
31+
// CHECK: (291, 332): ()
32+
// CHECK: (291, 292): Int?
33+
// CHECK: (295, 332): Int?
34+
// CHECK: (295, 320): Int

tools/SourceKit/docs/Protocol.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -697,7 +697,46 @@ Welcome to SourceKit. Type ':help' for assistance.
697697
}
698698
```
699699

700+
## Expression Type
701+
This request collects the types of all expressions in a source file after type checking.
702+
To fulfill this task, the client must provide the path to the Swift source file under
703+
type checking and the necessary compiler arguments to help resolve all dependencies.
700704

705+
### Request
706+
707+
```
708+
{
709+
<key.request>: (UID) <source.request.expression.type>,
710+
<key.sourcefile>: (string) // Absolute path to the file.
711+
<key.compilerargs>: [string*] // Array of zero or more strings for the compiler arguments,
712+
// e.g ["-sdk", "/path/to/sdk"]. If key.sourcefile is provided,
713+
// these must include the path to that file.
714+
}
715+
```
716+
717+
### Response
718+
```
719+
{
720+
<key.printedtypebuffer>: (string) // A text buffer where all expression types are printed to.
721+
<key.expression_type_list>: (array) [expr-type-info*] // A list of expression and type
722+
}
723+
```
724+
725+
```
726+
expr-type-info ::=
727+
{
728+
<key.expression_offset>: (int64) // Offset of an expression in the source file
729+
<key.expression_length>: (int64) // Length of an expression in the source file
730+
<key.type_offset>: (int64) // Offset of the printed type of the expression in the printed type buffer
731+
<key.type_length>: (int64) // Length of the printed type of the expression in the printed type buffer
732+
}
733+
```
734+
735+
### Testing
736+
737+
```
738+
$ sourcekitd-test -req=collect-type /path/to/file.swift -- /path/to/file.swift
739+
```
701740

702741
# UIDs
703742

tools/SourceKit/include/SourceKit/Core/LangSupport.h

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,17 @@ struct CodeCompletionInfo {
123123
Optional<ArrayRef<ParameterStructure>> parametersStructure;
124124
};
125125

126+
struct ExpressionType {
127+
unsigned ExprOffset;
128+
unsigned ExprLength;
129+
unsigned TypeOffset;
130+
};
131+
132+
struct ExpressionTypesInFile {
133+
std::vector<ExpressionType> Results;
134+
StringRef TypeBuffer;
135+
};
136+
126137
class CodeCompletionConsumer {
127138
virtual void anchor();
128139

@@ -708,6 +719,10 @@ class LangSupport {
708719
ArrayRef<const char*> Args,
709720
CategorizedEditsReceiver Receiver) = 0;
710721

722+
virtual void collectExpressionTypes(StringRef FileName,
723+
ArrayRef<const char *> Args,
724+
std::function<void(const ExpressionTypesInFile&)> Receiver) = 0;
725+
711726
virtual void getDocInfo(llvm::MemoryBuffer *InputBuf,
712727
StringRef ModuleName,
713728
ArrayRef<const char *> Args,
@@ -726,7 +741,6 @@ class LangSupport {
726741

727742
virtual void getStatistics(StatisticsReceiver) = 0;
728743
};
729-
730744
} // namespace SourceKit
731745

732746
#endif

0 commit comments

Comments
 (0)