rbs 4.0.3 → 4.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.dockerignore +37 -0
- data/.github/dependabot.yml +1 -1
- data/.github/workflows/bundle-update.yml +2 -2
- data/.github/workflows/c-check.yml +14 -6
- data/.github/workflows/comments.yml +2 -2
- data/.github/workflows/dependabot.yml +2 -2
- data/.github/workflows/jruby.yml +74 -0
- data/.github/workflows/release-gems.yml +235 -0
- data/.github/workflows/ruby.yml +43 -3
- data/.github/workflows/rust.yml +13 -10
- data/.github/workflows/truffleruby.yml +54 -0
- data/.github/workflows/typecheck.yml +5 -2
- data/.github/workflows/wasm.yml +55 -0
- data/.github/workflows/windows.yml +5 -2
- data/.gitignore +8 -0
- data/CHANGELOG.md +113 -0
- data/Dockerfile.jruby +53 -0
- data/README.md +4 -4
- data/Rakefile +512 -82
- data/Steepfile +9 -0
- data/config.yml +26 -0
- data/core/array.rbs +243 -180
- data/core/builtin.rbs +7 -6
- data/core/class.rbs +5 -3
- data/core/enumerable.rbs +109 -109
- data/core/enumerator/product.rbs +5 -5
- data/core/enumerator.rbs +28 -28
- data/core/file.rbs +24 -1018
- data/core/file_constants.rbs +463 -0
- data/core/file_stat.rbs +534 -0
- data/core/float.rbs +0 -24
- data/core/hash.rbs +117 -101
- data/core/integer.rbs +21 -58
- data/core/io.rbs +25 -7
- data/core/kernel.rbs +26 -11
- data/core/match_data.rbs +1 -1
- data/core/module.rbs +88 -74
- data/core/numeric.rbs +3 -0
- data/core/object_space/weak_key_map.rbs +7 -7
- data/core/pathname.rbs +0 -10
- data/core/ractor.rbs +0 -10
- data/core/range.rbs +23 -23
- data/core/rbs/ops.rbs +154 -0
- data/core/rbs/unnamed/argf.rbs +3 -3
- data/core/ruby_vm.rbs +40 -0
- data/core/rubygems/errors.rbs +4 -1
- data/core/rubygems/requirement.rbs +0 -10
- data/core/rubygems/rubygems.rbs +4 -1
- data/core/rubygems/specification.rbs +8 -0
- data/core/rubygems/version.rbs +0 -160
- data/core/set.rbs +3 -3
- data/core/struct.rbs +16 -16
- data/core/thread.rbs +9 -14
- data/docs/CONTRIBUTING.md +2 -1
- data/docs/inline.md +65 -7
- data/docs/rbs_by_example.md +20 -20
- data/docs/release.md +247 -0
- data/docs/syntax.md +2 -2
- data/docs/wasm_serialization.md +80 -0
- data/ext/rbs_extension/ast_translation.c +1298 -956
- data/ext/rbs_extension/ast_translation.h +4 -0
- data/ext/rbs_extension/class_constants.c +2 -0
- data/ext/rbs_extension/class_constants.h +1 -0
- data/ext/rbs_extension/extconf.rb +1 -0
- data/ext/rbs_extension/legacy_location.c +11 -6
- data/ext/rbs_extension/main.c +139 -4
- data/include/rbs/ast.h +323 -298
- data/include/rbs/defines.h +18 -0
- data/include/rbs/lexer.h +1 -0
- data/include/rbs/serialize.h +39 -0
- data/lib/rbs/annotate/rdoc_annotator.rb +27 -31
- data/lib/rbs/ast/ruby/annotations.rb +42 -0
- data/lib/rbs/ast/ruby/comment_block.rb +6 -4
- data/lib/rbs/ast/ruby/declarations.rb +11 -1
- data/lib/rbs/ast/ruby/helpers/location_helper.rb +1 -1
- data/lib/rbs/ast/ruby/members.rb +40 -1
- data/lib/rbs/buffer.rb +48 -11
- data/lib/rbs/cli.rb +3 -5
- data/lib/rbs/collection/config/lockfile_generator.rb +14 -1
- data/lib/rbs/collection/sources/git.rb +6 -0
- data/lib/rbs/definition_builder/method_builder.rb +12 -6
- data/lib/rbs/environment.rb +10 -3
- data/lib/rbs/inline_parser.rb +54 -28
- data/lib/rbs/namespace.rb +47 -11
- data/lib/rbs/parser_aux.rb +4 -2
- data/lib/rbs/prototype/rbi.rb +193 -25
- data/lib/rbs/prototype/runtime.rb +2 -0
- data/lib/rbs/resolver/type_name_resolver.rb +12 -14
- data/lib/rbs/rewriter.rb +70 -0
- data/lib/rbs/test/type_check.rb +6 -1
- data/lib/rbs/type_name.rb +33 -13
- data/lib/rbs/unit_test/type_assertions.rb +14 -5
- data/lib/rbs/version.rb +1 -1
- data/lib/rbs/wasm/deserializer.rb +213 -0
- data/lib/rbs/wasm/location.rb +61 -0
- data/lib/rbs/wasm/parser.rb +137 -0
- data/lib/rbs/wasm/runtime.rb +196 -0
- data/lib/rbs/wasm/serialization_schema.rb +110 -0
- data/lib/rbs.rb +14 -2
- data/lib/rbs_jars.rb +39 -0
- data/lib/rdoc_plugin/parser.rb +5 -0
- data/rbs.gemspec +32 -2
- data/sig/annotate/rdoc_annotater.rbs +12 -9
- data/sig/ast/ruby/annotations.rbs +49 -0
- data/sig/ast/ruby/members.rbs +21 -1
- data/sig/buffer.rbs +19 -1
- data/sig/collection/config/lockfile_generator.rbs +2 -0
- data/sig/inline_parser.rbs +2 -0
- data/sig/manifest.yaml +0 -1
- data/sig/namespace.rbs +20 -0
- data/sig/parser.rbs +10 -0
- data/sig/prototype/rbi.rbs +33 -4
- data/sig/resolver/type_name_resolver.rbs +2 -4
- data/sig/rewriter.rbs +45 -0
- data/sig/typename.rbs +15 -0
- data/sig/unit_test/type_assertions.rbs +6 -2
- data/sig/wasm/deserializer.rbs +66 -0
- data/sig/wasm/serialization_schema.rbs +13 -0
- data/src/ast.c +186 -162
- data/src/lexer.c +220 -193
- data/src/lexer.re +2 -1
- data/src/lexstate.c +10 -1
- data/src/parser.c +61 -8
- data/src/serialize.c +958 -0
- data/src/util/rbs_allocator.c +14 -8
- data/stdlib/abbrev/0/array.rbs +1 -1
- data/stdlib/csv/0/csv.rbs +5 -5
- data/stdlib/delegate/0/delegator.rbs +2 -1
- data/stdlib/digest/0/digest.rbs +11 -5
- data/stdlib/erb/0/erb.rbs +1 -1
- data/stdlib/etc/0/etc.rbs +18 -4
- data/stdlib/fileutils/0/fileutils.rbs +21 -21
- data/stdlib/ipaddr/0/ipaddr.rbs +0 -5
- data/stdlib/json/0/json.rbs +6 -6
- data/stdlib/monitor/0/monitor.rbs +2 -2
- data/stdlib/openssl/0/openssl.rbs +46 -40
- data/stdlib/resolv/0/resolv.rbs +1 -1
- data/stdlib/shellwords/0/shellwords.rbs +1 -1
- data/stdlib/stringio/0/stringio.rbs +32 -10
- data/stdlib/strscan/0/string_scanner.rbs +74 -55
- data/stdlib/tempfile/0/manifest.yaml +3 -0
- data/stdlib/timeout/0/timeout.rbs +0 -5
- data/stdlib/tsort/0/cyclic.rbs +1 -1
- data/stdlib/tsort/0/interfaces.rbs +8 -8
- data/stdlib/tsort/0/tsort.rbs +9 -9
- data/stdlib/uri/0/generic.rbs +0 -5
- data/stdlib/zlib/0/gzip_reader.rbs +2 -2
- data/stdlib/zlib/0/zstream.rbs +0 -1
- data/wasm/README.md +93 -0
- data/wasm/rbs_wasm.c +423 -0
- metadata +29 -6
- data/.github/workflows/milestone.yml +0 -80
- data/.vscode/extensions.json +0 -5
- data/.vscode/settings.json +0 -19
data/wasm/rbs_wasm.c
ADDED
|
@@ -0,0 +1,423 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file rbs_wasm.c
|
|
3
|
+
*
|
|
4
|
+
* WebAssembly entry points for the RBS parser.
|
|
5
|
+
*
|
|
6
|
+
* The parser in `src/` is plain, self-contained C with no dependency on the
|
|
7
|
+
* Ruby C API, so it compiles to WebAssembly as-is. This file exposes a small,
|
|
8
|
+
* stable ABI so the parser can be driven from a WebAssembly host (a JVM-based
|
|
9
|
+
* runtime running under JRuby).
|
|
10
|
+
*
|
|
11
|
+
* The flow is: the host writes a UTF-8 source string into linear memory
|
|
12
|
+
* (`rbs_wasm_alloc`), calls one of the `rbs_wasm_parse_*` functions, and reads
|
|
13
|
+
* the result back out (`rbs_wasm_result_ptr` / `rbs_wasm_result_len`). On
|
|
14
|
+
* success the result is the serialized AST (see `rbs_serialize_node` and
|
|
15
|
+
* `docs/wasm_serialization.md`); on a parse error it is an error blob (see
|
|
16
|
+
* `set_error_result`). `RBS::WASM` on the Ruby side decodes both.
|
|
17
|
+
*
|
|
18
|
+
* Built as a "reactor": no `main`, and the host calls `_initialize` once before
|
|
19
|
+
* invoking any export.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
#include <stdint.h>
|
|
23
|
+
#include <stdlib.h>
|
|
24
|
+
#include <string.h>
|
|
25
|
+
|
|
26
|
+
#include "rbs/parser.h"
|
|
27
|
+
#include "rbs/serialize.h"
|
|
28
|
+
#include "rbs/string.h"
|
|
29
|
+
#include "rbs/util/rbs_buffer.h"
|
|
30
|
+
#include "rbs/util/rbs_encoding.h"
|
|
31
|
+
|
|
32
|
+
// The result of the most recent parse, living in linear memory until the next
|
|
33
|
+
// call replaces it. WebAssembly is little-endian, so the multi-byte integers
|
|
34
|
+
// written below match the little-endian format the Ruby decoder expects.
|
|
35
|
+
static char *result_buffer = NULL;
|
|
36
|
+
static int32_t result_length = 0;
|
|
37
|
+
|
|
38
|
+
// Replace the current result with a fresh `length`-byte buffer and return a
|
|
39
|
+
// pointer to it for the caller to fill in.
|
|
40
|
+
static char *allocate_result(size_t length) {
|
|
41
|
+
free(result_buffer);
|
|
42
|
+
result_buffer = (char *) malloc(length == 0 ? 1 : length);
|
|
43
|
+
result_length = (int32_t) length;
|
|
44
|
+
return result_buffer;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Allocate `size` bytes in linear memory and return the offset. The host uses
|
|
49
|
+
* this to reserve space for an input string before calling a parse function.
|
|
50
|
+
*/
|
|
51
|
+
__attribute__((export_name("rbs_wasm_alloc"))) void *rbs_wasm_alloc(size_t size) {
|
|
52
|
+
return malloc(size);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Free a region previously returned by `rbs_wasm_alloc`.
|
|
57
|
+
*/
|
|
58
|
+
__attribute__((export_name("rbs_wasm_free"))) void rbs_wasm_free(void *ptr) {
|
|
59
|
+
free(ptr);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Offset of the most recent parse result in linear memory.
|
|
64
|
+
*/
|
|
65
|
+
__attribute__((export_name("rbs_wasm_result_ptr")))
|
|
66
|
+
int32_t
|
|
67
|
+
rbs_wasm_result_ptr(void) {
|
|
68
|
+
return (int32_t) (intptr_t) result_buffer;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Length, in bytes, of the most recent parse result.
|
|
73
|
+
*/
|
|
74
|
+
__attribute__((export_name("rbs_wasm_result_len")))
|
|
75
|
+
int32_t
|
|
76
|
+
rbs_wasm_result_len(void) {
|
|
77
|
+
return result_length;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Encode the parser's error into the result buffer:
|
|
81
|
+
//
|
|
82
|
+
// [i32 start_char][i32 end_char][u8 syntax_error]
|
|
83
|
+
// [u32 token_type_len][token_type bytes][u32 message_len][message bytes]
|
|
84
|
+
//
|
|
85
|
+
// Always returns 0, the failure status for the parse functions.
|
|
86
|
+
static int set_error_result(rbs_parser_t *parser) {
|
|
87
|
+
rbs_error_t *error = parser->error;
|
|
88
|
+
const char *token_type = rbs_token_type_str(error->token.type);
|
|
89
|
+
const char *message = error->message;
|
|
90
|
+
uint32_t token_type_len = (uint32_t) strlen(token_type);
|
|
91
|
+
uint32_t message_len = (uint32_t) strlen(message);
|
|
92
|
+
|
|
93
|
+
int32_t start_char = error->token.range.start.char_pos;
|
|
94
|
+
int32_t end_char = error->token.range.end.char_pos;
|
|
95
|
+
uint8_t syntax_error = error->syntax_error ? 1 : 0;
|
|
96
|
+
|
|
97
|
+
size_t total = 4 + 4 + 1 + 4 + token_type_len + 4 + message_len;
|
|
98
|
+
char *p = allocate_result(total);
|
|
99
|
+
|
|
100
|
+
memcpy(p, &start_char, 4);
|
|
101
|
+
p += 4;
|
|
102
|
+
memcpy(p, &end_char, 4);
|
|
103
|
+
p += 4;
|
|
104
|
+
*p++ = (char) syntax_error;
|
|
105
|
+
memcpy(p, &token_type_len, 4);
|
|
106
|
+
p += 4;
|
|
107
|
+
memcpy(p, token_type, token_type_len);
|
|
108
|
+
p += token_type_len;
|
|
109
|
+
memcpy(p, &message_len, 4);
|
|
110
|
+
p += 4;
|
|
111
|
+
memcpy(p, message, message_len);
|
|
112
|
+
|
|
113
|
+
return 0;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
static int set_serialized_result(rbs_parser_t *parser, rbs_node_t *node) {
|
|
117
|
+
rbs_string_t bytes = rbs_serialize_node(parser->allocator, &parser->constant_pool, node);
|
|
118
|
+
size_t length = rbs_string_len(bytes);
|
|
119
|
+
memcpy(allocate_result(length), bytes.start, length);
|
|
120
|
+
return 1;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// A reversed or out-of-bounds range would make the lexer loop forever, which
|
|
124
|
+
// would hang the whole host. Hosts are expected to validate too (RBS::Parser
|
|
125
|
+
// raises on bad ranges), but guard here so a stray caller can never wedge the VM.
|
|
126
|
+
static bool range_is_valid(int start_pos, int end_pos, int length) {
|
|
127
|
+
return start_pos >= 0 && end_pos >= 0 && start_pos <= end_pos && end_pos <= length;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Resolve a Ruby encoding name (e.g. "UTF-8", "EUC-JP") to an rbs encoding,
|
|
131
|
+
// falling back to UTF-8 when none is given or the name is not recognised.
|
|
132
|
+
static const rbs_encoding_t *resolve_encoding(const char *name, int name_length) {
|
|
133
|
+
if (name_length > 0) {
|
|
134
|
+
const rbs_encoding_t *encoding = rbs_encoding_find((const uint8_t *) name, (const uint8_t *) (name + name_length));
|
|
135
|
+
if (encoding != NULL) return encoding;
|
|
136
|
+
}
|
|
137
|
+
return RBS_ENCODING_UTF_8_ENTRY;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Declare type variables from a buffer of newline-separated names. A negative
|
|
141
|
+
// length means "no variables given" (the parser keeps its default table).
|
|
142
|
+
static void declare_variables(rbs_parser_t *parser, const char *variables, int variables_length) {
|
|
143
|
+
if (variables_length < 0) return;
|
|
144
|
+
|
|
145
|
+
rbs_parser_push_typevar_table(parser, true);
|
|
146
|
+
|
|
147
|
+
const char *cursor = variables;
|
|
148
|
+
const char *end = variables + variables_length;
|
|
149
|
+
const char *name_start = cursor;
|
|
150
|
+
|
|
151
|
+
while (cursor <= end) {
|
|
152
|
+
if (cursor == end || *cursor == '\n') {
|
|
153
|
+
size_t name_length = (size_t) (cursor - name_start);
|
|
154
|
+
if (name_length > 0) {
|
|
155
|
+
uint8_t *copied = (uint8_t *) malloc(name_length);
|
|
156
|
+
memcpy(copied, name_start, name_length);
|
|
157
|
+
rbs_constant_id_t id = rbs_constant_pool_insert_owned(&parser->constant_pool, copied, name_length);
|
|
158
|
+
(void) rbs_parser_insert_typevar(parser, id);
|
|
159
|
+
}
|
|
160
|
+
name_start = cursor + 1;
|
|
161
|
+
}
|
|
162
|
+
cursor++;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Parse an RBS signature from a source buffer.
|
|
168
|
+
*
|
|
169
|
+
* `source`/`length` is the whole buffer content; `encoding`/`encoding_length` is
|
|
170
|
+
* its Ruby encoding name; `start_pos`/`end_pos` are the character range within it
|
|
171
|
+
* to parse, so reported locations are absolute (this mirrors
|
|
172
|
+
* RBS::Parser._parse_signature).
|
|
173
|
+
*
|
|
174
|
+
* @return 1 on success (result is the serialized AST), 0 on a parse error
|
|
175
|
+
* (result is an error blob).
|
|
176
|
+
*/
|
|
177
|
+
__attribute__((export_name("rbs_wasm_parse_signature"))) int rbs_wasm_parse_signature(const char *source, int length, const char *encoding, int encoding_length, int start_pos, int end_pos) {
|
|
178
|
+
if (!range_is_valid(start_pos, end_pos, length)) {
|
|
179
|
+
allocate_result(0);
|
|
180
|
+
return 0;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
rbs_string_t string = rbs_string_new(source, source + length);
|
|
184
|
+
rbs_parser_t *parser = rbs_parser_new(string, resolve_encoding(encoding, encoding_length), start_pos, end_pos);
|
|
185
|
+
|
|
186
|
+
rbs_signature_t *signature = NULL;
|
|
187
|
+
rbs_parse_signature(parser, &signature);
|
|
188
|
+
|
|
189
|
+
int status;
|
|
190
|
+
if (parser->error == NULL) {
|
|
191
|
+
status = set_serialized_result(parser, (rbs_node_t *) signature);
|
|
192
|
+
} else {
|
|
193
|
+
status = set_error_result(parser);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
rbs_parser_free(parser);
|
|
197
|
+
return status;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Parse a single RBS type.
|
|
202
|
+
*
|
|
203
|
+
* @param variables Newline-separated type variable names (length < 0 for none).
|
|
204
|
+
* @return 1 on success, 0 on a parse error. On success with an empty result
|
|
205
|
+
* (`rbs_wasm_result_len` == 0), the input was empty (`nil`).
|
|
206
|
+
*/
|
|
207
|
+
__attribute__((export_name("rbs_wasm_parse_type"))) int rbs_wasm_parse_type(const char *source, int length, const char *encoding, int encoding_length, int start_pos, int end_pos, const char *variables, int variables_length, int require_eof, int void_allowed, int self_allowed, int classish_allowed) {
|
|
208
|
+
if (!range_is_valid(start_pos, end_pos, length)) {
|
|
209
|
+
allocate_result(0);
|
|
210
|
+
return 0;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
rbs_string_t string = rbs_string_new(source, source + length);
|
|
214
|
+
rbs_parser_t *parser = rbs_parser_new(string, resolve_encoding(encoding, encoding_length), start_pos, end_pos);
|
|
215
|
+
declare_variables(parser, variables, variables_length);
|
|
216
|
+
|
|
217
|
+
int status;
|
|
218
|
+
if (parser->next_token.type == pEOF) {
|
|
219
|
+
allocate_result(0);
|
|
220
|
+
status = 1;
|
|
221
|
+
} else {
|
|
222
|
+
rbs_node_t *type = NULL;
|
|
223
|
+
rbs_parse_type(parser, &type, void_allowed != 0, self_allowed != 0, classish_allowed != 0);
|
|
224
|
+
|
|
225
|
+
if (parser->error == NULL && require_eof) {
|
|
226
|
+
rbs_parser_advance(parser);
|
|
227
|
+
if (parser->current_token.type != pEOF) {
|
|
228
|
+
rbs_parser_set_error(parser, parser->current_token, true, "expected a token `%s`", rbs_token_type_str(pEOF));
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
status = parser->error == NULL ? set_serialized_result(parser, type) : set_error_result(parser);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
rbs_parser_free(parser);
|
|
236
|
+
return status;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Parse a single RBS method type.
|
|
241
|
+
*
|
|
242
|
+
* @param variables Newline-separated type variable names (length < 0 for none).
|
|
243
|
+
* @return 1 on success, 0 on a parse error. On success with an empty result,
|
|
244
|
+
* the input was empty (`nil`).
|
|
245
|
+
*/
|
|
246
|
+
__attribute__((export_name("rbs_wasm_parse_method_type"))) int rbs_wasm_parse_method_type(const char *source, int length, const char *encoding, int encoding_length, int start_pos, int end_pos, const char *variables, int variables_length, int require_eof) {
|
|
247
|
+
if (!range_is_valid(start_pos, end_pos, length)) {
|
|
248
|
+
allocate_result(0);
|
|
249
|
+
return 0;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
rbs_string_t string = rbs_string_new(source, source + length);
|
|
253
|
+
rbs_parser_t *parser = rbs_parser_new(string, resolve_encoding(encoding, encoding_length), start_pos, end_pos);
|
|
254
|
+
declare_variables(parser, variables, variables_length);
|
|
255
|
+
|
|
256
|
+
int status;
|
|
257
|
+
if (parser->next_token.type == pEOF) {
|
|
258
|
+
allocate_result(0);
|
|
259
|
+
status = 1;
|
|
260
|
+
} else {
|
|
261
|
+
rbs_method_type_t *method_type = NULL;
|
|
262
|
+
rbs_parse_method_type(parser, &method_type, require_eof != 0, true);
|
|
263
|
+
|
|
264
|
+
status = parser->error == NULL ? set_serialized_result(parser, (rbs_node_t *) method_type) : set_error_result(parser);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
rbs_parser_free(parser);
|
|
268
|
+
return status;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Parse a type parameter list (e.g. `[T < Comparable]`). On success the result
|
|
273
|
+
* is a serialized node list; an empty result means the input was empty (`nil`).
|
|
274
|
+
*/
|
|
275
|
+
__attribute__((export_name("rbs_wasm_parse_type_params"))) int rbs_wasm_parse_type_params(const char *source, int length, const char *encoding, int encoding_length, int start_pos, int end_pos, int module_type_params) {
|
|
276
|
+
if (!range_is_valid(start_pos, end_pos, length)) {
|
|
277
|
+
allocate_result(0);
|
|
278
|
+
return 0;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
rbs_string_t string = rbs_string_new(source, source + length);
|
|
282
|
+
rbs_parser_t *parser = rbs_parser_new(string, resolve_encoding(encoding, encoding_length), start_pos, end_pos);
|
|
283
|
+
|
|
284
|
+
int status;
|
|
285
|
+
if (parser->next_token.type == pEOF) {
|
|
286
|
+
allocate_result(0);
|
|
287
|
+
status = 1;
|
|
288
|
+
} else {
|
|
289
|
+
rbs_node_list_t *params = NULL;
|
|
290
|
+
rbs_parse_type_params(parser, module_type_params != 0, ¶ms);
|
|
291
|
+
|
|
292
|
+
if (parser->error == NULL) {
|
|
293
|
+
rbs_string_t bytes = rbs_serialize_node_list(parser->allocator, &parser->constant_pool, params);
|
|
294
|
+
size_t n = rbs_string_len(bytes);
|
|
295
|
+
memcpy(allocate_result(n), bytes.start, n);
|
|
296
|
+
status = 1;
|
|
297
|
+
} else {
|
|
298
|
+
status = set_error_result(parser);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
rbs_parser_free(parser);
|
|
303
|
+
return status;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// Shared body for the leading/trailing inline annotation parsers.
|
|
307
|
+
static int parse_inline_annotation(const char *source, int length, const char *encoding, int encoding_length, int start_pos, int end_pos, const char *variables, int variables_length, bool leading) {
|
|
308
|
+
if (!range_is_valid(start_pos, end_pos, length)) {
|
|
309
|
+
allocate_result(0);
|
|
310
|
+
return 0;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
rbs_string_t string = rbs_string_new(source, source + length);
|
|
314
|
+
rbs_parser_t *parser = rbs_parser_new(string, resolve_encoding(encoding, encoding_length), start_pos, end_pos);
|
|
315
|
+
declare_variables(parser, variables, variables_length);
|
|
316
|
+
|
|
317
|
+
rbs_ast_ruby_annotations_t *annotation = NULL;
|
|
318
|
+
bool success = leading ? rbs_parse_inline_leading_annotation(parser, &annotation) : rbs_parse_inline_trailing_annotation(parser, &annotation);
|
|
319
|
+
|
|
320
|
+
int status;
|
|
321
|
+
if (parser->error != NULL) {
|
|
322
|
+
status = set_error_result(parser);
|
|
323
|
+
} else if (!success || annotation == NULL) {
|
|
324
|
+
allocate_result(0);
|
|
325
|
+
status = 1;
|
|
326
|
+
} else {
|
|
327
|
+
status = set_serialized_result(parser, (rbs_node_t *) annotation);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
rbs_parser_free(parser);
|
|
331
|
+
return status;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Parse an inline leading annotation. On success the result is a serialized
|
|
336
|
+
* node; an empty result means there was no annotation (`nil`).
|
|
337
|
+
*/
|
|
338
|
+
__attribute__((export_name("rbs_wasm_parse_inline_leading_annotation"))) int rbs_wasm_parse_inline_leading_annotation(const char *source, int length, const char *encoding, int encoding_length, int start_pos, int end_pos, const char *variables, int variables_length) {
|
|
339
|
+
return parse_inline_annotation(source, length, encoding, encoding_length, start_pos, end_pos, variables, variables_length, true);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Parse an inline trailing annotation. See rbs_wasm_parse_inline_leading_annotation.
|
|
344
|
+
*/
|
|
345
|
+
__attribute__((export_name("rbs_wasm_parse_inline_trailing_annotation"))) int rbs_wasm_parse_inline_trailing_annotation(const char *source, int length, const char *encoding, int encoding_length, int start_pos, int end_pos, const char *variables, int variables_length) {
|
|
346
|
+
return parse_inline_annotation(source, length, encoding, encoding_length, start_pos, end_pos, variables, variables_length, false);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
static void w_lex_u32(rbs_allocator_t *allocator, rbs_buffer_t *buffer, uint32_t value) {
|
|
350
|
+
unsigned char bytes[4] = {
|
|
351
|
+
(unsigned char) (value & 0xff),
|
|
352
|
+
(unsigned char) ((value >> 8) & 0xff),
|
|
353
|
+
(unsigned char) ((value >> 16) & 0xff),
|
|
354
|
+
(unsigned char) ((value >> 24) & 0xff),
|
|
355
|
+
};
|
|
356
|
+
rbs_buffer_append_string(allocator, buffer, (const char *) bytes, 4);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Lex the source into tokens. The result is a sequence of records, with no
|
|
361
|
+
* leading count (the host reads until the buffer is exhausted):
|
|
362
|
+
*
|
|
363
|
+
* [u32 type_name_len][type_name bytes][i32 start_char][i32 end_char]
|
|
364
|
+
*
|
|
365
|
+
* The final token is always pEOF, mirroring RBS::Parser._lex.
|
|
366
|
+
*
|
|
367
|
+
* @return 1 always (lexing does not report parse errors here).
|
|
368
|
+
*/
|
|
369
|
+
__attribute__((export_name("rbs_wasm_lex"))) int rbs_wasm_lex(const char *source, int length, const char *encoding, int encoding_length, int end_pos) {
|
|
370
|
+
rbs_allocator_t *allocator = rbs_allocator_init();
|
|
371
|
+
rbs_lexer_t *lexer = rbs_lexer_new(allocator, rbs_string_new(source, source + length), resolve_encoding(encoding, encoding_length), 0, end_pos);
|
|
372
|
+
|
|
373
|
+
rbs_buffer_t buffer;
|
|
374
|
+
rbs_buffer_init(allocator, &buffer);
|
|
375
|
+
|
|
376
|
+
rbs_token_t token = NullToken;
|
|
377
|
+
while (token.type != pEOF) {
|
|
378
|
+
token = rbs_lexer_next_token(lexer);
|
|
379
|
+
|
|
380
|
+
const char *type_name = rbs_token_type_str(token.type);
|
|
381
|
+
uint32_t type_name_length = (uint32_t) strlen(type_name);
|
|
382
|
+
w_lex_u32(allocator, &buffer, type_name_length);
|
|
383
|
+
rbs_buffer_append_string(allocator, &buffer, type_name, type_name_length);
|
|
384
|
+
w_lex_u32(allocator, &buffer, (uint32_t) token.range.start.char_pos);
|
|
385
|
+
w_lex_u32(allocator, &buffer, (uint32_t) token.range.end.char_pos);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
rbs_string_t bytes = rbs_buffer_to_string(&buffer);
|
|
389
|
+
size_t n = rbs_string_len(bytes);
|
|
390
|
+
memcpy(allocate_result(n), bytes.start, n);
|
|
391
|
+
|
|
392
|
+
rbs_allocator_free(allocator);
|
|
393
|
+
return 1;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* Parse a small, fixed RBS document, used as a build smoke test
|
|
398
|
+
* (`wasmtime run --invoke rbs_wasm_selftest rbs_parser.wasm`).
|
|
399
|
+
*
|
|
400
|
+
* @return 1 if the sample parsed successfully, 0 otherwise.
|
|
401
|
+
*/
|
|
402
|
+
// Internal: defined in rbs_allocator.c, not declared in the public header.
|
|
403
|
+
extern size_t rbs_allocator_normalize_page_size(long raw);
|
|
404
|
+
|
|
405
|
+
__attribute__((export_name("rbs_wasm_selftest"))) int rbs_wasm_selftest(void) {
|
|
406
|
+
// Regression test: normalize_page_size must return a safe value
|
|
407
|
+
// (>= sizeof(rbs_allocator_page_t)) for inputs that would underflow
|
|
408
|
+
// payload_size. On WASI in a Rust cdylib, sysconf(_SC_PAGESIZE)
|
|
409
|
+
// returns 0; the normalization must catch that.
|
|
410
|
+
if (rbs_allocator_normalize_page_size(-1) != 4096) return 0;
|
|
411
|
+
if (rbs_allocator_normalize_page_size(0) != 4096) return 0;
|
|
412
|
+
if (rbs_allocator_normalize_page_size(1) != 4096) return 0;
|
|
413
|
+
if (rbs_allocator_normalize_page_size(65536) != 65536) return 0;
|
|
414
|
+
|
|
415
|
+
static const char source[] =
|
|
416
|
+
"class User\n"
|
|
417
|
+
" attr_reader name: String\n"
|
|
418
|
+
" def initialize: (String name) -> void\n"
|
|
419
|
+
"end\n";
|
|
420
|
+
|
|
421
|
+
int length = (int) (sizeof(source) - 1);
|
|
422
|
+
return rbs_wasm_parse_signature(source, length, "UTF-8", 5, 0, length);
|
|
423
|
+
}
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rbs
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 4.
|
|
4
|
+
version: 4.1.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Soutaro Matsumoto
|
|
8
8
|
bindir: exe
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 1980-01-
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: logger
|
|
@@ -63,23 +63,26 @@ extra_rdoc_files: []
|
|
|
63
63
|
files:
|
|
64
64
|
- ".clang-format"
|
|
65
65
|
- ".clangd"
|
|
66
|
+
- ".dockerignore"
|
|
66
67
|
- ".github/dependabot.yml"
|
|
67
68
|
- ".github/workflows/bundle-update.yml"
|
|
68
69
|
- ".github/workflows/c-check.yml"
|
|
69
70
|
- ".github/workflows/comments.yml"
|
|
70
71
|
- ".github/workflows/dependabot.yml"
|
|
71
|
-
- ".github/workflows/
|
|
72
|
+
- ".github/workflows/jruby.yml"
|
|
73
|
+
- ".github/workflows/release-gems.yml"
|
|
72
74
|
- ".github/workflows/ruby.yml"
|
|
73
75
|
- ".github/workflows/rust.yml"
|
|
76
|
+
- ".github/workflows/truffleruby.yml"
|
|
74
77
|
- ".github/workflows/typecheck.yml"
|
|
78
|
+
- ".github/workflows/wasm.yml"
|
|
75
79
|
- ".github/workflows/windows.yml"
|
|
76
80
|
- ".gitignore"
|
|
77
81
|
- ".rubocop.yml"
|
|
78
|
-
- ".vscode/extensions.json"
|
|
79
|
-
- ".vscode/settings.json"
|
|
80
82
|
- BSDL
|
|
81
83
|
- CHANGELOG.md
|
|
82
84
|
- COPYING
|
|
85
|
+
- Dockerfile.jruby
|
|
83
86
|
- README.md
|
|
84
87
|
- Rakefile
|
|
85
88
|
- Steepfile
|
|
@@ -107,6 +110,8 @@ files:
|
|
|
107
110
|
- core/fiber.rbs
|
|
108
111
|
- core/fiber_error.rbs
|
|
109
112
|
- core/file.rbs
|
|
113
|
+
- core/file_constants.rbs
|
|
114
|
+
- core/file_stat.rbs
|
|
110
115
|
- core/file_test.rbs
|
|
111
116
|
- core/float.rbs
|
|
112
117
|
- core/gc.rbs
|
|
@@ -135,6 +140,7 @@ files:
|
|
|
135
140
|
- core/range.rbs
|
|
136
141
|
- core/rational.rbs
|
|
137
142
|
- core/rb_config.rbs
|
|
143
|
+
- core/rbs/ops.rbs
|
|
138
144
|
- core/rbs/unnamed/argf.rbs
|
|
139
145
|
- core/rbs/unnamed/env_class.rbs
|
|
140
146
|
- core/rbs/unnamed/main_class.rbs
|
|
@@ -180,6 +186,7 @@ files:
|
|
|
180
186
|
- docs/gem.md
|
|
181
187
|
- docs/inline.md
|
|
182
188
|
- docs/rbs_by_example.md
|
|
189
|
+
- docs/release.md
|
|
183
190
|
- docs/repo.md
|
|
184
191
|
- docs/rust.md
|
|
185
192
|
- docs/sigs.md
|
|
@@ -187,6 +194,7 @@ files:
|
|
|
187
194
|
- docs/syntax.md
|
|
188
195
|
- docs/tools.md
|
|
189
196
|
- docs/type_fingerprint.md
|
|
197
|
+
- docs/wasm_serialization.md
|
|
190
198
|
- exe/rbs
|
|
191
199
|
- ext/rbs_extension/ast_translation.c
|
|
192
200
|
- ext/rbs_extension/ast_translation.h
|
|
@@ -207,6 +215,7 @@ files:
|
|
|
207
215
|
- include/rbs/lexer.h
|
|
208
216
|
- include/rbs/location.h
|
|
209
217
|
- include/rbs/parser.h
|
|
218
|
+
- include/rbs/serialize.h
|
|
210
219
|
- include/rbs/string.h
|
|
211
220
|
- include/rbs/util/rbs_allocator.h
|
|
212
221
|
- include/rbs/util/rbs_assert.h
|
|
@@ -287,6 +296,7 @@ files:
|
|
|
287
296
|
- lib/rbs/repository.rb
|
|
288
297
|
- lib/rbs/resolver/constant_resolver.rb
|
|
289
298
|
- lib/rbs/resolver/type_name_resolver.rb
|
|
299
|
+
- lib/rbs/rewriter.rb
|
|
290
300
|
- lib/rbs/sorter.rb
|
|
291
301
|
- lib/rbs/source.rb
|
|
292
302
|
- lib/rbs/substitution.rb
|
|
@@ -313,7 +323,13 @@ files:
|
|
|
313
323
|
- lib/rbs/variance_calculator.rb
|
|
314
324
|
- lib/rbs/vendorer.rb
|
|
315
325
|
- lib/rbs/version.rb
|
|
326
|
+
- lib/rbs/wasm/deserializer.rb
|
|
327
|
+
- lib/rbs/wasm/location.rb
|
|
328
|
+
- lib/rbs/wasm/parser.rb
|
|
329
|
+
- lib/rbs/wasm/runtime.rb
|
|
330
|
+
- lib/rbs/wasm/serialization_schema.rb
|
|
316
331
|
- lib/rbs/writer.rb
|
|
332
|
+
- lib/rbs_jars.rb
|
|
317
333
|
- lib/rdoc/discover.rb
|
|
318
334
|
- lib/rdoc_plugin/parser.rb
|
|
319
335
|
- rbs.gemspec
|
|
@@ -388,6 +404,7 @@ files:
|
|
|
388
404
|
- sig/resolver/constant_resolver.rbs
|
|
389
405
|
- sig/resolver/context.rbs
|
|
390
406
|
- sig/resolver/type_name_resolver.rbs
|
|
407
|
+
- sig/rewriter.rbs
|
|
391
408
|
- sig/shims/bundler.rbs
|
|
392
409
|
- sig/shims/enumerable.rbs
|
|
393
410
|
- sig/shims/rubygems.rbs
|
|
@@ -415,6 +432,8 @@ files:
|
|
|
415
432
|
- sig/vendorer.rbs
|
|
416
433
|
- sig/version.rbs
|
|
417
434
|
- sig/visitor.rbs
|
|
435
|
+
- sig/wasm/deserializer.rbs
|
|
436
|
+
- sig/wasm/serialization_schema.rbs
|
|
418
437
|
- sig/writer.rbs
|
|
419
438
|
- src/ast.c
|
|
420
439
|
- src/lexer.c
|
|
@@ -422,6 +441,7 @@ files:
|
|
|
422
441
|
- src/lexstate.c
|
|
423
442
|
- src/location.c
|
|
424
443
|
- src/parser.c
|
|
444
|
+
- src/serialize.c
|
|
425
445
|
- src/string.c
|
|
426
446
|
- src/util/rbs_allocator.c
|
|
427
447
|
- src/util/rbs_assert.c
|
|
@@ -563,6 +583,7 @@ files:
|
|
|
563
583
|
- stdlib/socket/0/unix_socket.rbs
|
|
564
584
|
- stdlib/stringio/0/stringio.rbs
|
|
565
585
|
- stdlib/strscan/0/string_scanner.rbs
|
|
586
|
+
- stdlib/tempfile/0/manifest.yaml
|
|
566
587
|
- stdlib/tempfile/0/tempfile.rbs
|
|
567
588
|
- stdlib/time/0/time.rbs
|
|
568
589
|
- stdlib/timeout/0/timeout.rbs
|
|
@@ -604,6 +625,8 @@ files:
|
|
|
604
625
|
- stdlib/zlib/0/version_error.rbs
|
|
605
626
|
- stdlib/zlib/0/zlib.rbs
|
|
606
627
|
- stdlib/zlib/0/zstream.rbs
|
|
628
|
+
- wasm/README.md
|
|
629
|
+
- wasm/rbs_wasm.c
|
|
607
630
|
homepage: https://github.com/ruby/rbs
|
|
608
631
|
licenses:
|
|
609
632
|
- BSD-2-Clause
|
|
@@ -626,7 +649,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
626
649
|
- !ruby/object:Gem::Version
|
|
627
650
|
version: '0'
|
|
628
651
|
requirements: []
|
|
629
|
-
rubygems_version: 4.0.
|
|
652
|
+
rubygems_version: 4.0.18
|
|
630
653
|
specification_version: 4
|
|
631
654
|
summary: Type signature for Ruby.
|
|
632
655
|
test_files: []
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
name: Check milestone
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
pull_request:
|
|
5
|
-
types: [opened, edited, labeled, unlabeled, milestoned, demilestoned, synchronize]
|
|
6
|
-
|
|
7
|
-
jobs:
|
|
8
|
-
check:
|
|
9
|
-
runs-on: ubuntu-latest
|
|
10
|
-
|
|
11
|
-
steps:
|
|
12
|
-
- uses: actions/checkout@v6
|
|
13
|
-
|
|
14
|
-
- name: Extract RBS::Version
|
|
15
|
-
id: version
|
|
16
|
-
run: |
|
|
17
|
-
# Extract version string from lib/rbs/version.rb
|
|
18
|
-
version=$(ruby -e 'load "lib/rbs/version.rb"; print RBS::VERSION')
|
|
19
|
-
echo "version=$version" >> "$GITHUB_OUTPUT"
|
|
20
|
-
|
|
21
|
-
# Parse major.minor.patch
|
|
22
|
-
IFS='.' read -r major minor patch _rest <<< "$version"
|
|
23
|
-
echo "major=$major" >> "$GITHUB_OUTPUT"
|
|
24
|
-
echo "minor=$minor" >> "$GITHUB_OUTPUT"
|
|
25
|
-
echo "patch=$patch" >> "$GITHUB_OUTPUT"
|
|
26
|
-
|
|
27
|
-
echo "RBS::VERSION = $version (major=$major, minor=$minor, patch=$patch)"
|
|
28
|
-
|
|
29
|
-
- name: Check milestone
|
|
30
|
-
uses: actions/github-script@v8
|
|
31
|
-
with:
|
|
32
|
-
script: |
|
|
33
|
-
const pr = context.payload.pull_request;
|
|
34
|
-
const milestone = pr.milestone;
|
|
35
|
-
const labels = pr.labels.map(l => l.name);
|
|
36
|
-
|
|
37
|
-
const version = '${{ steps.version.outputs.version }}';
|
|
38
|
-
const major = '${{ steps.version.outputs.major }}';
|
|
39
|
-
const minor = '${{ steps.version.outputs.minor }}';
|
|
40
|
-
const patch = parseInt('${{ steps.version.outputs.patch }}', 10);
|
|
41
|
-
|
|
42
|
-
if (!milestone) {
|
|
43
|
-
if (labels.includes('no-milestone')) {
|
|
44
|
-
core.info('No milestone set, but no-milestone label is present — OK');
|
|
45
|
-
return;
|
|
46
|
-
}
|
|
47
|
-
core.setFailed(
|
|
48
|
-
'No milestone set. Add a milestone or add the "no-milestone" label.'
|
|
49
|
-
);
|
|
50
|
-
return;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
if (labels.includes('no-milestone')) {
|
|
54
|
-
core.setFailed(
|
|
55
|
-
'Milestone is set but "no-milestone" label is present. Remove the label or the milestone.'
|
|
56
|
-
);
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
const milestoneName = milestone.title;
|
|
61
|
-
core.info(`Milestone: "${milestoneName}", RBS::VERSION: ${version}`);
|
|
62
|
-
|
|
63
|
-
// Expected milestone based on version:
|
|
64
|
-
// patch == 0 → "RBS major.minor" (e.g. "RBS 4.0")
|
|
65
|
-
// patch >= 1 → "RBS major.minor.x" (e.g. "RBS 4.0.x")
|
|
66
|
-
let expectedMilestone;
|
|
67
|
-
if (patch === 0) {
|
|
68
|
-
expectedMilestone = `RBS ${major}.${minor}`;
|
|
69
|
-
} else {
|
|
70
|
-
expectedMilestone = `RBS ${major}.${minor}.x`;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
if (milestoneName !== expectedMilestone) {
|
|
74
|
-
core.setFailed(
|
|
75
|
-
`Milestone "${milestoneName}" does not match RBS::VERSION ${version}. ` +
|
|
76
|
-
`Expected milestone: "${expectedMilestone}"`
|
|
77
|
-
);
|
|
78
|
-
} else {
|
|
79
|
-
core.info(`Milestone "${milestoneName}" matches RBS::VERSION ${version}`);
|
|
80
|
-
}
|
data/.vscode/extensions.json
DELETED
data/.vscode/settings.json
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"clangd.onConfigChanged": "restart",
|
|
3
|
-
"[c]": {
|
|
4
|
-
"editor.formatOnSave": true,
|
|
5
|
-
"editor.defaultFormatter": "llvm-vs-code-extensions.vscode-clangd"
|
|
6
|
-
},
|
|
7
|
-
"[cpp]": {
|
|
8
|
-
"editor.formatOnSave": true,
|
|
9
|
-
"editor.defaultFormatter": "llvm-vs-code-extensions.vscode-clangd"
|
|
10
|
-
},
|
|
11
|
-
"[h]": {
|
|
12
|
-
"editor.formatOnSave": true,
|
|
13
|
-
"editor.defaultFormatter": "llvm-vs-code-extensions.vscode-clangd"
|
|
14
|
-
},
|
|
15
|
-
"[hpp]": {
|
|
16
|
-
"editor.formatOnSave": true,
|
|
17
|
-
"editor.defaultFormatter": "llvm-vs-code-extensions.vscode-clangd"
|
|
18
|
-
}
|
|
19
|
-
}
|