Skip to content

Commit f2e57d8

Browse files
authored
[SourceKit] Add optional declarations array to interface gen request (#75802)
Introduces the new DeclarationsArrayBuilder and adds it to the EditorConsumer. Declaration info always includes a kind, offset, and length, and includes a USR where applicable. As the USR is already available for editor.open.interface type requests, this doesn't compute any new information, it just exposes more of what's there already.
1 parent ada8a30 commit f2e57d8

File tree

15 files changed

+445
-49
lines changed

15 files changed

+445
-49
lines changed

test/SourceKit/InterfaceGen/gen_swift_module.swift

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,67 @@ func f(s : inout [Int]) {
3333
// RUN: %swift -emit-module -o /dev/null -emit-module-interface-path %t.mod/swift_mod.swiftinterface -O %S/Inputs/swift_mod.swift -parse-as-library -disable-implicit-concurrency-module-import -disable-implicit-string-processing-module-import
3434
// RUN: %sourcekitd-test -req=interface-gen -module swift_mod -- -Xfrontend -disable-implicit-concurrency-module-import -Xfrontend -disable-implicit-string-processing-module-import -I %t.mod -module-cache-path %t.mod/mcp > %t.response
3535
// RUN: %diff -u %s.from_swiftinterface.response %t.response
36+
37+
// Separately test whether the optional declarations array contains the expected information from Inputs/swift_mod.swift
38+
// RUN: %sourcekitd-test -req=interface-gen -req-opts=enabledeclarations=true -module swift_mod -- -Xfrontend -disable-implicit-concurrency-module-import -Xfrontend -disable-implicit-string-processing-module-import -I %t.mod -module-cache-path %t.mod/mcp | %FileCheck -check-prefix=CHECK-DECLARATIONS-ARRAY %s
39+
// check that the other arrays are still there as normal, by checking for a unique identifying member in each
40+
// CHECK-DECLARATIONS-ARRAY-DAG: key.kind:{{.*}}ref.associatedtype
41+
// CHECK-DECLARATIONS-ARRAY-DAG: key.kind:{{.*}}syntaxtype
42+
// CHECK-DECLARATIONS-ARRAY-DAG: key.substructure
43+
44+
// CHECK-DECLARATIONS-ARRAY: {{^}}]{{$}}
45+
46+
// public class MyClass
47+
// CHECK-DECLARATIONS-ARRAY: key.kind:
48+
// CHECK-DECLARATIONS-ARRAY-SAME: decl
49+
// CHECK-DECLARATIONS-ARRAY-SAME: class
50+
// CHECK-DECLARATIONS-ARRAY-NEXT: key.usr:
51+
// could check for parts of the usr, but as the .response files always check for exact matches, do the same here
52+
// CHECK-DECLARATIONS-ARRAY-SAME: s:9swift_mod7MyClassC
53+
// check that offsets are there, but not zero, as that might mean offsets are broken (none of the declaration offsets in this file should be zero)
54+
// CHECK-DECLARATIONS-ARRAY-NOT: key.offset:{{.*}}{{^[0-9]}}0{{^[0-9]}}
55+
// CHECK-DECLARATIONS-ARRAY: key.offset
56+
57+
// public func pub_method
58+
// CHECK-DECLARATIONS-ARRAY: key.kind:
59+
// CHECK-DECLARATIONS-ARRAY-SAME: decl
60+
// CHECK-DECLARATIONS-ARRAY-SAME: method.instance
61+
// CHECK-DECLARATIONS-ARRAY-NEXT: key.usr:
62+
// CHECK-DECLARATIONS-ARRAY-SAME: s:9swift_mod7MyClassC10pub_methodyyF
63+
// CHECK-DECLARATIONS-ARRAY-NOT: key.offset:{{.*}}{{^[0-9]}}0{{^[0-9]}}
64+
// CHECK-DECLARATIONS-ARRAY: key.offset
65+
66+
// CHECK-DECLARATIONS-ARRAY-NOT: int_method
67+
// CHECK-DECLARATIONS-ARRAY-NOT: fp_method
68+
// CHECK-DECLARATIONS-ARRAY-NOT: priv_method
69+
70+
// public protocol MyProto
71+
// CHECK-DECLARATIONS-ARRAY: key.kind:
72+
// CHECK-DECLARATIONS-ARRAY-SAME: decl
73+
// CHECK-DECLARATIONS-ARRAY-SAME: protocol
74+
// CHECK-DECLARATIONS-ARRAY-NEXT: key.usr
75+
// CHECK-DECLARATIONS-ARRAY-SAME: s:9swift_mod7MyProtoP
76+
// CHECK-DECLARATIONS-ARRAY-NOT: key.offset:{{.*}}{{^[0-9]}}0{{^[0-9]}}
77+
// CHECK-DECLARATIONS-ARRAY: key.offset
78+
79+
// associatedtype Assoc
80+
// CHECK-DECLARATIONS-ARRAY: key.kind:
81+
// CHECK-DECLARATIONS-ARRAY-SAME: decl
82+
// CHECK-DECLARATIONS-ARRAY-SAME: associatedtype
83+
// CHECK-DECLARATIONS-ARRAY-NEXT: key.usr
84+
// CHECK-DECLARATIONS-ARRAY-SAME: s:9swift_mod7MyProtoP5AssocQa
85+
// CHECK-DECLARATIONS-ARRAY-NOT: key.offset:{{.*}}{{^[0-9]}}0{{^[0-9]}}
86+
// CHECK-DECLARATIONS-ARRAY: key.offset
87+
88+
// public func pub_function
89+
// CHECK-DECLARATIONS-ARRAY: key.kind:
90+
// CHECK-DECLARATIONS-ARRAY-SAME: decl
91+
// CHECK-DECLARATIONS-ARRAY-SAME: function.free
92+
// CHECK-DECLARATIONS-ARRAY-NEXT: key.usr
93+
// CHECK-DECLARATIONS-ARRAY-SAME: s:9swift_mod12pub_functionSiyF
94+
// CHECK-DECLARATIONS-ARRAY-NOT: key.offset:{{.*}}{{^[0-9]}}0{{^[0-9]}}
95+
// CHECK-DECLARATIONS-ARRAY: key.offset
96+
97+
// CHECK-DECLARATIONS-ARRAY-NOT: int_function
98+
// CHECK-DECLARATIONS-ARRAY-NOT: fp_function
99+
// CHECK-DECLARATIONS-ARRAY-NOT: priv_function

tools/SourceKit/docs/Protocol.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -362,16 +362,19 @@ Welcome to SourceKit. Type ':help' for assistance.
362362
<key.modulename>: (string) // Full module name, e.g. "Foundation.NSArray"
363363
[opt] <key.compilerargs> [string*] // array of zero or more strings for the compiler arguments
364364
// e.g ["-sdk", "/path/to/sdk"]
365+
[opt] <key.enabledeclarations> (int) // 0 by default, 1 to enable the declarations array in the output
365366
}
366367
```
367368

368369
### Response
369370

370371
This will return the Swift interface of the specified module.
371372

372-
- `key.sourcetext`: The pretty-printed module interface in swift source code
373+
- `key.sourcetext`: The pretty-printed module interface in swift source code.
373374
- `key.syntaxmap`: An array of syntactic annotations, same as the one returned for the source.request.editor.open request.
374375
- `key.annotations`: An array of semantic annotations, same as the one returned for the source.request.editor.open request.
376+
- `key.substructure`: An array of dictionaries representing ranges of structural elements in the result description, such as the parameters of a function.
377+
- (optional, only if `key.enabledeclarations: 1`) `key.declarations`: An array of declarations, containing the kind, USR (if available), offset, and length of the declaration.
375378

376379
All SourceKit requests that don't modify the source buffer should work on the
377380
opened document, by passing the associated 'name' for the document.

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,9 @@ class EditorConsumer {
391391
virtual void handleSemanticAnnotation(unsigned Offset, unsigned Length,
392392
UIdent Kind, bool isSystem) = 0;
393393

394+
virtual void handleDeclaration(unsigned Offset, unsigned Length, UIdent Kind,
395+
StringRef USR) = 0;
396+
394397
virtual void beginDocumentSubStructure(unsigned Offset, unsigned Length,
395398
UIdent Kind, UIdent AccessLevel,
396399
UIdent SetterAccessLevel,

tools/SourceKit/lib/SwiftLang/SwiftEditorInterfaceGen.cpp

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ class SwiftInterfaceGenContext::Implementation {
5656
const Decl *Dcl = nullptr;
5757
/// The range in the interface source.
5858
TextRange Range;
59+
/// The USR, if the declaration has one
60+
StringRef USR;
5961

6062
TextDecl(const Decl *D, TextRange Range)
6163
: Dcl(D), Range(Range) {}
@@ -66,6 +68,8 @@ class SwiftInterfaceGenContext::Implementation {
6668
std::string Text;
6769
std::vector<TextReference> References;
6870
std::vector<TextDecl> Decls;
71+
/// Do not clear the map or erase elements,
72+
/// without keeping the Decls vector in sync
6973
llvm::StringMap<TextDecl> USRMap;
7074
};
7175

@@ -155,8 +159,17 @@ class AnnotatingPrinter : public StreamPrinter {
155159
OS << TargetUSR;
156160
}
157161
StringRef USR = OS.str();
158-
Info.USRMap[USR] = Entry;
159162
DeclUSRs.emplace_back(VD, USR);
163+
auto iterator = Info.USRMap.insert_or_assign(USR, Entry).first;
164+
// Set the USR in the declarations to the key in the USRMap, because the
165+
// lifetime of that matches/exceeds the lifetime of Decls. String keys
166+
// in the StringMap are heap allocated and only get destroyed on
167+
// explicit erase() or clear() calls, or on destructor calls (the
168+
// Programmer's Manual description itself also states that StringMap
169+
// "only ever copies a string if a value is inserted").
170+
// Thus this never results in a dangling reference, as the USRMap is
171+
// never cleared and no elements are erased in its lifetime.
172+
Info.Decls.back().USR = iterator->getKey();
160173
}
161174
}
162175
}
@@ -265,6 +278,21 @@ static void reportSemanticAnnotations(const SourceTextInfo &IFaceInfo,
265278
}
266279
}
267280

281+
/// Create the declarations array (sourcekitd::DeclarationsArrayBuilder) from
282+
/// the SourceTextInfo about declarations
283+
static void reportDeclarations(const SourceTextInfo &IFaceInfo,
284+
EditorConsumer &Consumer) {
285+
for (auto &Dcl : IFaceInfo.Decls) {
286+
if (!Dcl.Dcl)
287+
continue;
288+
UIdent Kind = SwiftLangSupport::getUIDForDecl(Dcl.Dcl);
289+
if (Kind.isInvalid())
290+
continue;
291+
Consumer.handleDeclaration(Dcl.Range.Offset, Dcl.Range.Length, Kind,
292+
Dcl.USR);
293+
}
294+
}
295+
268296
namespace {
269297
/// A diagnostic consumer that picks up module loading errors.
270298
class ModuleLoadingErrorConsumer final : public DiagnosticConsumer {
@@ -593,6 +621,8 @@ void SwiftInterfaceGenContext::reportEditorInfo(EditorConsumer &Consumer) const
593621
reportSyntacticAnnotations(Impl.TextCI, Consumer);
594622
reportDocumentStructure(Impl.TextCI, Consumer);
595623
reportSemanticAnnotations(Impl.Info, Consumer);
624+
625+
reportDeclarations(Impl.Info, Consumer);
596626
Consumer.finished();
597627
}
598628

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1069,6 +1069,9 @@ static int handleTestInvocation(TestOptions Opts, TestOptions &InitOpts) {
10691069
Opts.InterestedUSR.c_str());
10701070
if (!Opts.USR.empty())
10711071
sourcekitd_request_dictionary_set_string(Req, KeyUSR, Opts.USR.c_str());
1072+
1073+
// add possible EnableDeclarations in particular
1074+
addRequestOptionsDirect(Req, Opts);
10721075
break;
10731076

10741077
case SourceKitRequest::FindInterfaceDoc:
@@ -2453,6 +2456,12 @@ static void printInterfaceGen(sourcekitd_variant_t Info, bool CheckASCII) {
24532456
sourcekitd_variant_t structure =
24542457
sourcekitd_variant_dictionary_get_value(Info, KeySubStructure);
24552458
printRawVariant(structure);
2459+
sourcekitd_variant_t declarations =
2460+
sourcekitd_variant_dictionary_get_value(Info, KeyDeclarations);
2461+
// only output declarations if there are any (because this might have been
2462+
// disabled in the request itself)
2463+
if (sourcekitd_variant_get_type(declarations) != SOURCEKITD_VARIANT_TYPE_NULL)
2464+
printRawVariant(declarations);
24562465
}
24572466

24582467
static void printRelatedIdents(sourcekitd_variant_t Info, StringRef Filename,
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
//===--- DeclarationsArray.h - ----------------------------------*- C++ -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2024 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
// This is an array used in the response to editor.open.interface requests.
13+
// It contains all declarations, identified by their Kind, Offset, and Length,
14+
// and optionally includes a USR, if the declaration has one.
15+
//===----------------------------------------------------------------------===//
16+
#ifndef LLVM_SOURCEKITD_DECLARATIONS_ARRAY_H
17+
#define LLVM_SOURCEKITD_DECLARATIONS_ARRAY_H
18+
19+
#include "sourcekitd/Internal.h"
20+
21+
namespace sourcekitd {
22+
23+
VariantFunctions *getVariantFunctionsForDeclarationsArray();
24+
25+
/// Builds an array for declarations by kind, offset, length, and optionally USR
26+
class DeclarationsArrayBuilder {
27+
public:
28+
DeclarationsArrayBuilder();
29+
~DeclarationsArrayBuilder();
30+
31+
void add(SourceKit::UIdent Kind, unsigned Offset, unsigned Length,
32+
llvm::StringRef USR);
33+
34+
bool empty() const;
35+
36+
std::unique_ptr<llvm::MemoryBuffer> createBuffer();
37+
38+
private:
39+
struct Implementation;
40+
Implementation &Impl;
41+
};
42+
43+
} // namespace sourcekitd
44+
45+
#endif

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ static const unsigned ProtocolMinorVersion = 0;
4949

5050
enum class CustomBufferKind {
5151
TokenAnnotationsArray,
52+
DeclarationsArray,
5253
DocSupportAnnotationArray,
5354
CodeCompletionResultsArray,
5455
DocStructureArray,

tools/SourceKit/tools/sourcekitd/lib/API/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ add_sourcekit_library(sourcekitdAPI
99
DocSupportAnnotationArray.cpp
1010
RawData.cpp
1111
sourcekitdAPI-Common.cpp
12+
DeclarationsArray.cpp
1213
TokenAnnotationsArray.cpp
1314
ExpressionTypeArray.cpp
1415
VariableTypeArray.cpp

0 commit comments

Comments
 (0)