Skip to content

Commit 2703d08

Browse files
committed
Add CollectVariableType request to SourceKit
- Add VariableTypeCollector This new SourceEntityWalker collects types from variable declarations. - Add SwiftLangSupport::collectVariableTypes - Implement CollectVariableType request - Provide information about explicit types in CollectVarType - Fix HasExplicitType in VariableTypeArray - Fix typo - Implement ranged CollectVariableTypes requests - Use offset/length params for CollectVariableType in sourcekitd-test - Address a bunch of PR suggestions - Remove CanonicalType from VariableTypeCollector This turned out not to be needed (for now). - Improve doc comment on VariableTypeCollector::getTypeOffsets - Remove unused CanonicalTy variable - Remove out-of-date comment - Run clang-format on the CollectVariableType implementation - Fix some minor style issues - Use emplace_back while collecting variable infos - Pass CollectVariableType range to VariableTypeCollector - Use capitalized variable names in VariableTypeArray ...as recommended by the LLVM coding standards - Use PrintOptions for type printing in VariableTypeCollector - Return void for collectVariableType This seems to be cleaner stylistically. - Avoid visiting subranges of invalid range in VariableTypeCollector - Use std::string for type buffer in VariableTypeCollectorASTConsumer - Use plural for PrintedType in VariableTypeArray - Remove unused fields in VariableTypeArrayBuilder - Add suggested doc comments to VariableTypeArray - Remove unused VariableTypeInfo.TypeLength - Fix typo of ostream in VariableTypeCollectorASTConsumer - Fix typo - Document Offset and Length semantics in collectVariableTypes
1 parent 775bff0 commit 2703d08

File tree

17 files changed

+576
-0
lines changed

17 files changed

+576
-0
lines changed

include/swift/Sema/IDETypeChecking.h

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,33 @@ namespace swift {
206206
/// the decl context.
207207
ProtocolDecl *resolveProtocolName(DeclContext *dc, StringRef Name);
208208

209+
/// Reported type of a variable declaration.
210+
struct VariableTypeInfo {
211+
/// The start of the variable identifier.
212+
uint32_t Offset;
213+
214+
/// The length of the variable identifier.
215+
uint32_t Length;
216+
217+
/// Whether the variable has an explicit type annotation.
218+
bool HasExplicitType;
219+
220+
/// The start of the printed type in a separate string buffer.
221+
uint32_t TypeOffset;
222+
223+
VariableTypeInfo(uint32_t Offset, uint32_t Length, bool HasExplicitType,
224+
uint32_t TypeOffset);
225+
};
226+
227+
/// Collect type information for every variable declaration in \c SF
228+
/// within the given range, defined by \c Offset and \c Length.
229+
/// All types will be printed to \c OS and the type offsets of the
230+
/// \c VariableTypeInfos will index into the string that backs this
231+
/// stream.
232+
void collectVariableType(
233+
SourceFile &SF, Optional<unsigned> Offset, Optional<unsigned> Length,
234+
std::vector<VariableTypeInfo> &Scratch, llvm::raw_ostream &OS);
235+
209236
/// FIXME: All of the below goes away once CallExpr directly stores its
210237
/// arguments.
211238

lib/IDE/IDETypeChecking.cpp

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -731,6 +731,95 @@ swift::collectExpressionType(SourceFile &SF,
731731
return Scratch;
732732
}
733733

734+
/// This walker will traverse the AST and report types for every variable
735+
/// declaration.
736+
class VariableTypeCollector : public SourceEntityWalker {
737+
private:
738+
SourceManager &SM;
739+
unsigned int BufferId;
740+
741+
/// The start offset of the range in which variable types are to be collected.
742+
Optional<unsigned> TotalOffset;
743+
744+
/// The length of the range in which variable types are to be collected.
745+
Optional<unsigned> TotalLength;
746+
747+
/// The output vector for VariableTypeInfos emitted during traversal.
748+
std::vector<VariableTypeInfo> &Results;
749+
750+
/// We print all types into a single output stream (e.g. into a string buffer)
751+
/// and provide offsets into this string buffer to describe individual types,
752+
/// i.e. `OS` builds a string that contains all null-terminated printed type
753+
/// strings. When referring to one of these types, we can use the offsets at
754+
/// which it starts in the `OS`.
755+
llvm::raw_ostream &OS;
756+
757+
/// Map from a printed type to the offset in OS where the type starts.
758+
llvm::StringMap<uint32_t> TypeOffsets;
759+
760+
/// Returns the start and end offset of this string in `OS`. If `PrintedType`
761+
/// hasn't been printed to `OS` yet, this function will do so.
762+
std::pair<uint32_t, uint32_t> getTypeOffsets(StringRef PrintedType) {
763+
auto It = TypeOffsets.find(PrintedType);
764+
if (It == TypeOffsets.end()) {
765+
TypeOffsets[PrintedType] = OS.tell();
766+
OS << PrintedType << '\0';
767+
}
768+
return {TypeOffsets[PrintedType], PrintedType.size()};
769+
}
770+
771+
public:
772+
VariableTypeCollector(SourceFile &SF, Optional<unsigned> Offset,
773+
Optional<unsigned> Length,
774+
std::vector<VariableTypeInfo> &Results,
775+
llvm::raw_ostream &OS)
776+
: SM(SF.getASTContext().SourceMgr), BufferId(*SF.getBufferID()),
777+
TotalOffset(Offset), TotalLength(Length), Results(Results), OS(OS) {}
778+
779+
bool walkToDeclPre(Decl *D, CharSourceRange Range) override {
780+
if (Range.isInvalid()) {
781+
return false;
782+
}
783+
unsigned Offset = SM.getLocOffsetInBuffer(Range.getStart(), BufferId);
784+
unsigned Length = Range.getByteLength();
785+
// Skip this declaration and its subtree if outside the range
786+
if (TotalOffset.hasValue() && TotalLength.hasValue() &&
787+
(Offset < *TotalOffset || Offset >= (*TotalOffset + *TotalLength))) {
788+
return false;
789+
}
790+
if (auto VD = dyn_cast<VarDecl>(D)) {
791+
// Print the type to a temporary buffer
792+
SmallString<64> Buffer;
793+
{
794+
llvm::raw_svector_ostream OS(Buffer);
795+
PrintOptions Options;
796+
Options.SynthesizeSugarOnTypes = true;
797+
VD->getType()->print(OS, Options);
798+
}
799+
// Transfer the type to `OS` if needed and get the offsets of this string
800+
// in `OS`.
801+
auto Ty = getTypeOffsets(Buffer.str());
802+
bool HasExplicitType =
803+
VD->getTypeReprOrParentPatternTypeRepr() != nullptr;
804+
// Add the type information to the result list.
805+
Results.emplace_back(Offset, Length, HasExplicitType, Ty.first);
806+
}
807+
return true;
808+
}
809+
};
810+
811+
VariableTypeInfo::VariableTypeInfo(uint32_t Offset, uint32_t Length,
812+
bool HasExplicitType, uint32_t TypeOffset)
813+
: Offset(Offset), Length(Length), HasExplicitType(HasExplicitType),
814+
TypeOffset(TypeOffset) {}
815+
816+
void swift::collectVariableType(
817+
SourceFile &SF, Optional<unsigned> Offset, Optional<unsigned> Length,
818+
std::vector<VariableTypeInfo> &Scratch, llvm::raw_ostream &OS) {
819+
VariableTypeCollector Walker(SF, Offset, Length, Scratch, OS);
820+
Walker.walk(SF);
821+
}
822+
734823
ArrayRef<ValueDecl*> swift::
735824
canDeclProvideDefaultImplementationFor(ValueDecl* VD) {
736825
return evaluateOrDefault(VD->getASTContext().evaluator,

tools/SourceKit/docs/Protocol.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -785,6 +785,49 @@ expr-type-info ::=
785785
$ sourcekitd-test -req=collect-type /path/to/file.swift -- /path/to/file.swift
786786
```
787787

788+
## Variable Type
789+
790+
This request collects the types of all variable declarations in a source file after type checking.
791+
To fulfill this task, the client must provide the path to the Swift source file under
792+
type checking and the necessary compiler arguments to help resolve all dependencies.
793+
794+
### Request
795+
796+
```
797+
{
798+
<key.request>: (UID) <source.request.variable.type>,
799+
<key.sourcefile>: (string) // Absolute path to the file.
800+
<key.compilerargs>: [string*] // Array of zero or more strings for the compiler arguments,
801+
// e.g ["-sdk", "/path/to/sdk"]. If key.sourcefile is provided,
802+
// these must include the path to that file.
803+
[opt] <key.offset>: (int64) // Offset of the requested range. Defaults to zero.
804+
[opt] <key.length>: (int64) // Length of the requested range. Defaults to the entire file.
805+
}
806+
```
807+
808+
### Response
809+
```
810+
{
811+
<key.variable_type_list>: (array) [var-type-info*] // A list of variable declarations and types
812+
}
813+
```
814+
815+
```
816+
var-type-info ::=
817+
{
818+
<key.variable_offset>: (int64) // Offset of a variable identifier in the source file
819+
<key.variable_length>: (int64) // Length of a variable identifier an expression in the source file
820+
<key.variable_type>: (string) // Printed type of the variable declaration
821+
<key.variable_type_explicit> (bool) // Whether the declaration has an explicit type annotation
822+
}
823+
```
824+
825+
### Testing
826+
827+
```
828+
$ sourcekitd-test -req=collect-var-type /path/to/file.swift -- /path/to/file.swift
829+
```
830+
788831
# UIDs
789832

790833
## Keys

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

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,27 @@ struct ExpressionTypesInFile {
138138
StringRef TypeBuffer;
139139
};
140140

141+
struct VariableType {
142+
/// The variable identifier's offset in the file.
143+
unsigned VarOffset;
144+
/// The variable identifier's length.
145+
unsigned VarLength;
146+
/// The offset of the type's string representation inside
147+
/// `VariableTypesInFile.TypeBuffer`.
148+
unsigned TypeOffset;
149+
/// Whether the variable declaration has an explicit type annotation.
150+
bool HasExplicitType;
151+
};
152+
153+
struct VariableTypesInFile {
154+
/// The typed variable declarations in the file.
155+
std::vector<VariableType> Results;
156+
/// A String containing the printed representation of all types in
157+
/// `Results`. Entries in `Results` refer to their types by using
158+
/// an offset into this string.
159+
StringRef TypeBuffer;
160+
};
161+
141162
class CodeCompletionConsumer {
142163
virtual void anchor();
143164

@@ -864,6 +885,15 @@ class LangSupport {
864885
std::function<void(const
865886
RequestResult<ExpressionTypesInFile> &)> Receiver) = 0;
866887

888+
/// Collects variable types for a range defined by `Offset` and `Length` in
889+
/// the source file. If `Offset` or `Length` are empty, variable types for
890+
/// the entire document are collected.
891+
virtual void collectVariableTypes(
892+
StringRef FileName, ArrayRef<const char *> Args,
893+
Optional<unsigned> Offset, Optional<unsigned> Length,
894+
std::function<void(const RequestResult<VariableTypesInFile> &)>
895+
Receiver) = 0;
896+
867897
virtual void getDocInfo(llvm::MemoryBuffer *InputBuf,
868898
StringRef ModuleName,
869899
ArrayRef<const char *> Args,

tools/SourceKit/lib/SwiftLang/SwiftLangSupport.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -603,6 +603,12 @@ class SwiftLangSupport : public LangSupport {
603603
bool CanonicalType,
604604
std::function<void(const RequestResult<ExpressionTypesInFile> &)> Receiver) override;
605605

606+
void collectVariableTypes(
607+
StringRef FileName, ArrayRef<const char *> Args,
608+
Optional<unsigned> Offset, Optional<unsigned> Length,
609+
std::function<void(const RequestResult<VariableTypesInFile> &)> Receiver)
610+
override;
611+
606612
void semanticRefactoring(StringRef Filename, SemanticRefactoringInfo Info,
607613
ArrayRef<const char*> Args,
608614
CategorizedEditsReceiver Receiver) override;

tools/SourceKit/lib/SwiftLang/SwiftSourceDocInfo.cpp

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2368,3 +2368,62 @@ void SwiftLangSupport::collectExpressionTypes(StringRef FileName,
23682368
&OncePerASTToken,
23692369
llvm::vfs::getRealFileSystem());
23702370
}
2371+
2372+
void SwiftLangSupport::collectVariableTypes(
2373+
StringRef FileName, ArrayRef<const char *> Args, Optional<unsigned> Offset,
2374+
Optional<unsigned> Length,
2375+
std::function<void(const RequestResult<VariableTypesInFile> &)> Receiver) {
2376+
std::string Error;
2377+
SwiftInvocationRef Invok = ASTMgr->getInvocation(Args, FileName, Error);
2378+
if (!Invok) {
2379+
LOG_WARN_FUNC("failed to create an ASTInvocation: " << Error);
2380+
Receiver(RequestResult<VariableTypesInFile>::fromError(Error));
2381+
return;
2382+
}
2383+
assert(Invok);
2384+
2385+
class VariableTypeCollectorASTConsumer : public SwiftASTConsumer {
2386+
private:
2387+
std::function<void(const RequestResult<VariableTypesInFile> &)> Receiver;
2388+
Optional<unsigned> Offset;
2389+
Optional<unsigned> Length;
2390+
2391+
public:
2392+
VariableTypeCollectorASTConsumer(
2393+
std::function<void(const RequestResult<VariableTypesInFile> &)>
2394+
Receiver,
2395+
Optional<unsigned> Offset, Optional<unsigned> Length)
2396+
: Receiver(std::move(Receiver)), Offset(Offset), Length(Length) {}
2397+
2398+
void handlePrimaryAST(ASTUnitRef AstUnit) override {
2399+
auto *SF = AstUnit->getCompilerInstance().getPrimarySourceFile();
2400+
std::vector<VariableTypeInfo> Infos;
2401+
std::string TypeBuffer;
2402+
llvm::raw_string_ostream OS(TypeBuffer);
2403+
VariableTypesInFile Result;
2404+
collectVariableType(*SF, Offset, Length, Infos, OS);
2405+
for (auto Info : Infos) {
2406+
Result.Results.push_back({Info.Offset, Info.Length, Info.TypeOffset, Info.HasExplicitType});
2407+
}
2408+
Result.TypeBuffer = OS.str();
2409+
Receiver(RequestResult<VariableTypesInFile>::fromResult(Result));
2410+
}
2411+
2412+
void cancelled() override {
2413+
Receiver(RequestResult<VariableTypesInFile>::cancelled());
2414+
}
2415+
2416+
void failed(StringRef Error) override {
2417+
Receiver(RequestResult<VariableTypesInFile>::fromError(Error));
2418+
}
2419+
};
2420+
2421+
auto Collector = std::make_shared<VariableTypeCollectorASTConsumer>(
2422+
Receiver, Offset, Length);
2423+
/// FIXME: When request cancellation is implemented and Xcode adopts it,
2424+
/// don't use 'OncePerASTToken'.
2425+
static const char OncePerASTToken = 0;
2426+
getASTManager()->processASTAsync(Invok, std::move(Collector),
2427+
&OncePerASTToken,
2428+
llvm::vfs::getRealFileSystem());
2429+
}

tools/SourceKit/tools/sourcekitd-test/TestOptions.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ bool TestOptions::parseArgs(llvm::ArrayRef<const char *> Args) {
147147
.Case("stats", SourceKitRequest::Statistics)
148148
.Case("track-compiles", SourceKitRequest::EnableCompileNotifications)
149149
.Case("collect-type", SourceKitRequest::CollectExpresstionType)
150+
.Case("collect-var-type", SourceKitRequest::CollectVariableType)
150151
.Case("global-config", SourceKitRequest::GlobalConfiguration)
151152
.Case("dependency-updated", SourceKitRequest::DependencyUpdated)
152153
#define SEMANTIC_REFACTORING(KIND, NAME, ID) .Case("refactoring." #ID, SourceKitRequest::KIND)

tools/SourceKit/tools/sourcekitd-test/TestOptions.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ enum class SourceKitRequest {
6464
SyntaxTree,
6565
EnableCompileNotifications,
6666
CollectExpresstionType,
67+
CollectVariableType,
6768
GlobalConfiguration,
6869
DependencyUpdated,
6970
#define SEMANTIC_REFACTORING(KIND, NAME, ID) KIND,

tools/SourceKit/tools/sourcekitd-test/sourcekitd-test.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ static void printNameTranslationInfo(sourcekitd_variant_t Info, llvm::raw_ostrea
6767
static void printRangeInfo(sourcekitd_variant_t Info, StringRef Filename,
6868
llvm::raw_ostream &OS);
6969
static void printExpressionType(sourcekitd_variant_t Info, llvm::raw_ostream &OS);
70+
static void printVariableType(sourcekitd_variant_t Info, llvm::raw_ostream &OS);
7071
static void printDocInfo(sourcekitd_variant_t Info, StringRef Filename);
7172
static void printInterfaceGen(sourcekitd_variant_t Info, bool CheckASCII);
7273
static void printSemanticInfo();
@@ -738,6 +739,17 @@ static int handleTestInvocation(TestOptions Opts, TestOptions &InitOpts) {
738739
break;
739740
}
740741

742+
case SourceKitRequest::CollectVariableType: {
743+
sourcekitd_request_dictionary_set_uid(Req, KeyRequest,
744+
RequestCollectVariableType);
745+
if (Opts.Length) {
746+
sourcekitd_request_dictionary_set_int64(Req, KeyOffset, ByteOffset);
747+
sourcekitd_request_dictionary_set_int64(Req, KeyLength, Opts.Length);
748+
}
749+
addRequestOptionsDirect(Req, Opts);
750+
break;
751+
}
752+
741753
#define SEMANTIC_REFACTORING(KIND, NAME, ID) \
742754
case SourceKitRequest::KIND: \
743755
setRefactoringFields(Req, Opts, KindRefactoring##KIND, SourceBuf.get()); \
@@ -1254,6 +1266,10 @@ static bool handleResponse(sourcekitd_response_t Resp, const TestOptions &Opts,
12541266
printExpressionType(Info, llvm::outs());
12551267
break;
12561268

1269+
case SourceKitRequest::CollectVariableType:
1270+
printVariableType(Info, llvm::outs());
1271+
break;
1272+
12571273
case SourceKitRequest::DocInfo:
12581274
printDocInfo(Info, SourceFile);
12591275
break;
@@ -1945,6 +1961,27 @@ static void printExpressionType(sourcekitd_variant_t Info, llvm::raw_ostream &OS
19451961
OS << "</ExpressionTypes>\n";
19461962
}
19471963

1964+
static void printVariableType(sourcekitd_variant_t Info, llvm::raw_ostream &OS) {
1965+
auto TypeBuffer =
1966+
sourcekitd_variant_dictionary_get_value(Info, KeyVariableTypeList);
1967+
unsigned Count = sourcekitd_variant_array_get_count(TypeBuffer);
1968+
if (!Count) {
1969+
OS << "cannot find variable types in the file\n";
1970+
return;
1971+
}
1972+
OS << "<VariableTypes>\n";
1973+
for (unsigned i = 0; i != Count; ++i) {
1974+
sourcekitd_variant_t Item = sourcekitd_variant_array_get_value(TypeBuffer, i);
1975+
unsigned Offset = sourcekitd_variant_dictionary_get_int64(Item, KeyVariableOffset);
1976+
unsigned Length = sourcekitd_variant_dictionary_get_int64(Item, KeyVariableLength);
1977+
bool HasExplicitType = sourcekitd_variant_dictionary_get_bool(Item, KeyVariableTypeExplicit);
1978+
OS << "(" << Offset << ", " << Offset + Length << "): "
1979+
<< sourcekitd_variant_dictionary_get_string(Item, KeyVariableType)
1980+
<< " (explicit type: " << HasExplicitType << ")\n";
1981+
}
1982+
OS << "</VariableTypes>\n";
1983+
}
1984+
19481985
static void printFoundInterface(sourcekitd_variant_t Info,
19491986
llvm::raw_ostream &OS) {
19501987
const char *Name = sourcekitd_variant_dictionary_get_string(Info,

tools/SourceKit/tools/sourcekitd/include/sourcekitd/Internal.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ enum class CustomBufferKind {
6060
DocStructureElementArray,
6161
AttributesArray,
6262
ExpressionTypeArray,
63+
VariableTypeArray,
6364
RawData
6465
};
6566

0 commit comments

Comments
 (0)