prism 1.0.0 → 1.1.0
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/CHANGELOG.md +26 -1
- data/README.md +1 -0
- data/config.yml +257 -20
- data/docs/parsing_rules.md +4 -1
- data/ext/prism/extension.c +63 -26
- data/ext/prism/extension.h +1 -1
- data/include/prism/ast.h +559 -327
- data/include/prism/defines.h +27 -0
- data/include/prism/diagnostic.h +5 -1
- data/include/prism/options.h +39 -2
- data/include/prism/parser.h +7 -0
- data/include/prism/util/pm_string.h +27 -4
- data/include/prism/version.h +2 -2
- data/lib/prism/dot_visitor.rb +2 -0
- data/lib/prism/dsl.rb +10 -8
- data/lib/prism/ffi.rb +37 -3
- data/lib/prism/inspect_visitor.rb +1 -1
- data/lib/prism/lex_compat.rb +1 -1
- data/lib/prism/node.rb +132 -89
- data/lib/prism/parse_result.rb +1 -1
- data/lib/prism/reflection.rb +1 -1
- data/lib/prism/serialize.rb +6 -2
- data/lib/prism/translation/parser/lexer.rb +25 -3
- data/lib/prism/translation/ruby_parser.rb +7 -1
- data/prism.gemspec +1 -2
- data/rbi/prism/dsl.rbi +32 -32
- data/rbi/prism/node.rbi +69 -59
- data/rbi/prism.rbi +34 -34
- data/sig/prism/dsl.rbs +24 -24
- data/sig/prism/node.rbs +113 -105
- data/sig/prism.rbs +90 -72
- data/src/diagnostic.c +15 -7
- data/src/node.c +10 -0
- data/src/options.c +58 -27
- data/src/prettyprint.c +10 -0
- data/src/prism.c +588 -385
- data/src/util/pm_string.c +123 -65
- metadata +2 -3
- data/lib/prism/translation/parser/rubocop.rb +0 -73
data/src/util/pm_string.c
CHANGED
@@ -47,6 +47,62 @@ pm_string_constant_init(pm_string_t *string, const char *source, size_t length)
|
|
47
47
|
};
|
48
48
|
}
|
49
49
|
|
50
|
+
#ifdef _WIN32
|
51
|
+
/**
|
52
|
+
* Represents a file handle on Windows, where the path will need to be freed
|
53
|
+
* when the file is closed.
|
54
|
+
*/
|
55
|
+
typedef struct {
|
56
|
+
/** The path to the file, which will become allocated memory. */
|
57
|
+
WCHAR *path;
|
58
|
+
|
59
|
+
/** The handle to the file, which will start as uninitialized memory. */
|
60
|
+
HANDLE file;
|
61
|
+
} pm_string_file_handle_t;
|
62
|
+
|
63
|
+
/**
|
64
|
+
* Open the file indicated by the filepath parameter for reading on Windows.
|
65
|
+
* Perform any kind of normalization that needs to happen on the filepath.
|
66
|
+
*/
|
67
|
+
static pm_string_init_result_t
|
68
|
+
pm_string_file_handle_open(pm_string_file_handle_t *handle, const char *filepath) {
|
69
|
+
int length = MultiByteToWideChar(CP_UTF8, 0, filepath, -1, NULL, 0);
|
70
|
+
if (length == 0) return PM_STRING_INIT_ERROR_GENERIC;
|
71
|
+
|
72
|
+
handle->path = xmalloc(sizeof(WCHAR) * ((size_t) length));
|
73
|
+
if ((handle->path == NULL) || (MultiByteToWideChar(CP_UTF8, 0, filepath, -1, handle->path, length) == 0)) {
|
74
|
+
xfree(handle->path);
|
75
|
+
return PM_STRING_INIT_ERROR_GENERIC;
|
76
|
+
}
|
77
|
+
|
78
|
+
handle->file = CreateFileW(handle->path, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL);
|
79
|
+
if (handle->file == INVALID_HANDLE_VALUE) {
|
80
|
+
pm_string_init_result_t result = PM_STRING_INIT_ERROR_GENERIC;
|
81
|
+
|
82
|
+
if (GetLastError() == ERROR_ACCESS_DENIED) {
|
83
|
+
DWORD attributes = GetFileAttributesW(handle->path);
|
84
|
+
if ((attributes != INVALID_FILE_ATTRIBUTES) && (attributes & FILE_ATTRIBUTE_DIRECTORY)) {
|
85
|
+
result = PM_STRING_INIT_ERROR_DIRECTORY;
|
86
|
+
}
|
87
|
+
}
|
88
|
+
|
89
|
+
xfree(handle->path);
|
90
|
+
return result;
|
91
|
+
}
|
92
|
+
|
93
|
+
return PM_STRING_INIT_SUCCESS;
|
94
|
+
}
|
95
|
+
|
96
|
+
/**
|
97
|
+
* Close the file handle and free the path.
|
98
|
+
*/
|
99
|
+
static void
|
100
|
+
pm_string_file_handle_close(pm_string_file_handle_t *handle) {
|
101
|
+
xfree(handle->path);
|
102
|
+
CloseHandle(handle->file);
|
103
|
+
}
|
104
|
+
#endif
|
105
|
+
|
50
106
|
/**
|
51
107
|
* Read the file indicated by the filepath parameter into source and load its
|
52
108
|
* contents and size into the given `pm_string_t`. The given `pm_string_t`
|
@@ -58,69 +114,66 @@ pm_string_constant_init(pm_string_t *string, const char *source, size_t length)
|
|
58
114
|
* `MapViewOfFile`, on POSIX systems that have access to `mmap` we'll use
|
59
115
|
* `mmap`, and on other POSIX systems we'll use `read`.
|
60
116
|
*/
|
61
|
-
PRISM_EXPORTED_FUNCTION
|
117
|
+
PRISM_EXPORTED_FUNCTION pm_string_init_result_t
|
62
118
|
pm_string_mapped_init(pm_string_t *string, const char *filepath) {
|
63
119
|
#ifdef _WIN32
|
64
120
|
// Open the file for reading.
|
65
|
-
|
66
|
-
|
67
|
-
if (
|
68
|
-
return false;
|
69
|
-
}
|
121
|
+
pm_string_file_handle_t handle;
|
122
|
+
pm_string_init_result_t result = pm_string_file_handle_open(&handle, filepath);
|
123
|
+
if (result != PM_STRING_INIT_SUCCESS) return result;
|
70
124
|
|
71
125
|
// Get the file size.
|
72
|
-
DWORD file_size = GetFileSize(file, NULL);
|
126
|
+
DWORD file_size = GetFileSize(handle.file, NULL);
|
73
127
|
if (file_size == INVALID_FILE_SIZE) {
|
74
|
-
|
75
|
-
return
|
128
|
+
pm_string_file_handle_close(&handle);
|
129
|
+
return PM_STRING_INIT_ERROR_GENERIC;
|
76
130
|
}
|
77
131
|
|
78
132
|
// If the file is empty, then we don't need to do anything else, we'll set
|
79
133
|
// the source to a constant empty string and return.
|
80
134
|
if (file_size == 0) {
|
81
|
-
|
135
|
+
pm_string_file_handle_close(&handle);
|
82
136
|
const uint8_t source[] = "";
|
83
137
|
*string = (pm_string_t) { .type = PM_STRING_CONSTANT, .source = source, .length = 0 };
|
84
|
-
return
|
138
|
+
return PM_STRING_INIT_SUCCESS;
|
85
139
|
}
|
86
140
|
|
87
141
|
// Create a mapping of the file.
|
88
|
-
HANDLE mapping = CreateFileMapping(file, NULL, PAGE_READONLY, 0, 0, NULL);
|
142
|
+
HANDLE mapping = CreateFileMapping(handle.file, NULL, PAGE_READONLY, 0, 0, NULL);
|
89
143
|
if (mapping == NULL) {
|
90
|
-
|
91
|
-
return
|
144
|
+
pm_string_file_handle_close(&handle);
|
145
|
+
return PM_STRING_INIT_ERROR_GENERIC;
|
92
146
|
}
|
93
147
|
|
94
148
|
// Map the file into memory.
|
95
149
|
uint8_t *source = (uint8_t *) MapViewOfFile(mapping, FILE_MAP_READ, 0, 0, 0);
|
96
150
|
CloseHandle(mapping);
|
97
|
-
|
151
|
+
pm_string_file_handle_close(&handle);
|
98
152
|
|
99
153
|
if (source == NULL) {
|
100
|
-
return
|
154
|
+
return PM_STRING_INIT_ERROR_GENERIC;
|
101
155
|
}
|
102
156
|
|
103
157
|
*string = (pm_string_t) { .type = PM_STRING_MAPPED, .source = source, .length = (size_t) file_size };
|
104
|
-
return
|
158
|
+
return PM_STRING_INIT_SUCCESS;
|
105
159
|
#elif defined(_POSIX_MAPPED_FILES)
|
106
160
|
// Open the file for reading
|
107
161
|
int fd = open(filepath, O_RDONLY);
|
108
162
|
if (fd == -1) {
|
109
|
-
return
|
163
|
+
return PM_STRING_INIT_ERROR_GENERIC;
|
110
164
|
}
|
111
165
|
|
112
166
|
// Stat the file to get the file size
|
113
167
|
struct stat sb;
|
114
168
|
if (fstat(fd, &sb) == -1) {
|
115
169
|
close(fd);
|
116
|
-
return
|
170
|
+
return PM_STRING_INIT_ERROR_GENERIC;
|
117
171
|
}
|
118
172
|
|
119
173
|
// Ensure it is a file and not a directory
|
120
174
|
if (S_ISDIR(sb.st_mode)) {
|
121
|
-
errno = EISDIR;
|
122
175
|
close(fd);
|
123
|
-
return
|
176
|
+
return PM_STRING_INIT_ERROR_DIRECTORY;
|
124
177
|
}
|
125
178
|
|
126
179
|
// mmap the file descriptor to virtually get the contents
|
@@ -131,17 +184,17 @@ pm_string_mapped_init(pm_string_t *string, const char *filepath) {
|
|
131
184
|
close(fd);
|
132
185
|
const uint8_t source[] = "";
|
133
186
|
*string = (pm_string_t) { .type = PM_STRING_CONSTANT, .source = source, .length = 0 };
|
134
|
-
return
|
187
|
+
return PM_STRING_INIT_SUCCESS;
|
135
188
|
}
|
136
189
|
|
137
190
|
source = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
|
138
191
|
if (source == MAP_FAILED) {
|
139
|
-
return
|
192
|
+
return PM_STRING_INIT_ERROR_GENERIC;
|
140
193
|
}
|
141
194
|
|
142
195
|
close(fd);
|
143
196
|
*string = (pm_string_t) { .type = PM_STRING_MAPPED, .source = source, .length = size };
|
144
|
-
return
|
197
|
+
return PM_STRING_INIT_SUCCESS;
|
145
198
|
#else
|
146
199
|
return pm_string_file_init(string, filepath);
|
147
200
|
#endif
|
@@ -152,100 +205,105 @@ pm_string_mapped_init(pm_string_t *string, const char *filepath) {
|
|
152
205
|
* contents and size into the given `pm_string_t`. The given `pm_string_t`
|
153
206
|
* should be freed using `pm_string_free` when it is no longer used.
|
154
207
|
*/
|
155
|
-
PRISM_EXPORTED_FUNCTION
|
208
|
+
PRISM_EXPORTED_FUNCTION pm_string_init_result_t
|
156
209
|
pm_string_file_init(pm_string_t *string, const char *filepath) {
|
157
210
|
#ifdef _WIN32
|
158
211
|
// Open the file for reading.
|
159
|
-
|
160
|
-
|
161
|
-
if (
|
162
|
-
return false;
|
163
|
-
}
|
212
|
+
pm_string_file_handle_t handle;
|
213
|
+
pm_string_init_result_t result = pm_string_file_handle_open(&handle, filepath);
|
214
|
+
if (result != PM_STRING_INIT_SUCCESS) return result;
|
164
215
|
|
165
216
|
// Get the file size.
|
166
|
-
DWORD file_size = GetFileSize(file, NULL);
|
217
|
+
DWORD file_size = GetFileSize(handle.file, NULL);
|
167
218
|
if (file_size == INVALID_FILE_SIZE) {
|
168
|
-
|
169
|
-
return
|
219
|
+
pm_string_file_handle_close(&handle);
|
220
|
+
return PM_STRING_INIT_ERROR_GENERIC;
|
170
221
|
}
|
171
222
|
|
172
223
|
// If the file is empty, then we don't need to do anything else, we'll set
|
173
224
|
// the source to a constant empty string and return.
|
174
225
|
if (file_size == 0) {
|
175
|
-
|
226
|
+
pm_string_file_handle_close(&handle);
|
176
227
|
const uint8_t source[] = "";
|
177
228
|
*string = (pm_string_t) { .type = PM_STRING_CONSTANT, .source = source, .length = 0 };
|
178
|
-
return
|
229
|
+
return PM_STRING_INIT_SUCCESS;
|
179
230
|
}
|
180
231
|
|
181
232
|
// Create a buffer to read the file into.
|
182
233
|
uint8_t *source = xmalloc(file_size);
|
183
234
|
if (source == NULL) {
|
184
|
-
|
185
|
-
return
|
235
|
+
pm_string_file_handle_close(&handle);
|
236
|
+
return PM_STRING_INIT_ERROR_GENERIC;
|
186
237
|
}
|
187
238
|
|
188
239
|
// Read the contents of the file
|
189
240
|
DWORD bytes_read;
|
190
|
-
if (!ReadFile(file, source, file_size, &bytes_read, NULL)) {
|
191
|
-
|
192
|
-
return
|
241
|
+
if (!ReadFile(handle.file, source, file_size, &bytes_read, NULL)) {
|
242
|
+
pm_string_file_handle_close(&handle);
|
243
|
+
return PM_STRING_INIT_ERROR_GENERIC;
|
193
244
|
}
|
194
245
|
|
195
246
|
// Check the number of bytes read
|
196
247
|
if (bytes_read != file_size) {
|
197
248
|
xfree(source);
|
198
|
-
|
199
|
-
return
|
249
|
+
pm_string_file_handle_close(&handle);
|
250
|
+
return PM_STRING_INIT_ERROR_GENERIC;
|
200
251
|
}
|
201
252
|
|
202
|
-
|
253
|
+
pm_string_file_handle_close(&handle);
|
203
254
|
*string = (pm_string_t) { .type = PM_STRING_OWNED, .source = source, .length = (size_t) file_size };
|
204
|
-
return
|
255
|
+
return PM_STRING_INIT_SUCCESS;
|
205
256
|
#elif defined(PRISM_HAS_FILESYSTEM)
|
206
|
-
|
207
|
-
|
208
|
-
|
257
|
+
// Open the file for reading
|
258
|
+
int fd = open(filepath, O_RDONLY);
|
259
|
+
if (fd == -1) {
|
260
|
+
return PM_STRING_INIT_ERROR_GENERIC;
|
209
261
|
}
|
210
262
|
|
211
|
-
|
212
|
-
|
263
|
+
// Stat the file to get the file size
|
264
|
+
struct stat sb;
|
265
|
+
if (fstat(fd, &sb) == -1) {
|
266
|
+
close(fd);
|
267
|
+
return PM_STRING_INIT_ERROR_GENERIC;
|
268
|
+
}
|
213
269
|
|
214
|
-
|
215
|
-
|
216
|
-
|
270
|
+
// Ensure it is a file and not a directory
|
271
|
+
if (S_ISDIR(sb.st_mode)) {
|
272
|
+
close(fd);
|
273
|
+
return PM_STRING_INIT_ERROR_DIRECTORY;
|
217
274
|
}
|
218
275
|
|
219
|
-
|
220
|
-
|
276
|
+
// Check the size to see if it's empty
|
277
|
+
size_t size = (size_t) sb.st_size;
|
278
|
+
if (size == 0) {
|
279
|
+
close(fd);
|
221
280
|
const uint8_t source[] = "";
|
222
281
|
*string = (pm_string_t) { .type = PM_STRING_CONSTANT, .source = source, .length = 0 };
|
223
|
-
return
|
282
|
+
return PM_STRING_INIT_SUCCESS;
|
224
283
|
}
|
225
284
|
|
226
|
-
size_t length = (size_t)
|
285
|
+
size_t length = (size_t) size;
|
227
286
|
uint8_t *source = xmalloc(length);
|
228
287
|
if (source == NULL) {
|
229
|
-
|
230
|
-
return
|
288
|
+
close(fd);
|
289
|
+
return PM_STRING_INIT_ERROR_GENERIC;
|
231
290
|
}
|
232
291
|
|
233
|
-
|
234
|
-
|
235
|
-
fclose(file);
|
292
|
+
long bytes_read = (long) read(fd, source, length);
|
293
|
+
close(fd);
|
236
294
|
|
237
|
-
if (bytes_read
|
295
|
+
if (bytes_read == -1) {
|
238
296
|
xfree(source);
|
239
|
-
return
|
297
|
+
return PM_STRING_INIT_ERROR_GENERIC;
|
240
298
|
}
|
241
299
|
|
242
300
|
*string = (pm_string_t) { .type = PM_STRING_OWNED, .source = source, .length = length };
|
243
|
-
return
|
301
|
+
return PM_STRING_INIT_SUCCESS;
|
244
302
|
#else
|
245
303
|
(void) string;
|
246
304
|
(void) filepath;
|
247
305
|
perror("pm_string_file_init is not implemented for this platform");
|
248
|
-
return
|
306
|
+
return PM_STRING_INIT_ERROR_GENERIC;
|
249
307
|
#endif
|
250
308
|
}
|
251
309
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: prism
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shopify
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-10-02 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description:
|
14
14
|
email:
|
@@ -98,7 +98,6 @@ files:
|
|
98
98
|
- lib/prism/translation/parser.rb
|
99
99
|
- lib/prism/translation/parser/compiler.rb
|
100
100
|
- lib/prism/translation/parser/lexer.rb
|
101
|
-
- lib/prism/translation/parser/rubocop.rb
|
102
101
|
- lib/prism/translation/parser33.rb
|
103
102
|
- lib/prism/translation/parser34.rb
|
104
103
|
- lib/prism/translation/ripper.rb
|
@@ -1,73 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
# typed: ignore
|
3
|
-
|
4
|
-
warn "WARN: Prism is directly supported since RuboCop 1.62. The `prism/translation/parser/rubocop` file is deprecated."
|
5
|
-
|
6
|
-
require "parser"
|
7
|
-
require "rubocop"
|
8
|
-
|
9
|
-
require_relative "../../prism"
|
10
|
-
require_relative "../parser"
|
11
|
-
|
12
|
-
module Prism
|
13
|
-
module Translation
|
14
|
-
class Parser
|
15
|
-
# This is the special version numbers that should be used in RuboCop
|
16
|
-
# configuration files to trigger using prism.
|
17
|
-
|
18
|
-
# For Ruby 3.3
|
19
|
-
VERSION_3_3 = 80_82_73_83_77.33
|
20
|
-
|
21
|
-
# For Ruby 3.4
|
22
|
-
VERSION_3_4 = 80_82_73_83_77.34
|
23
|
-
|
24
|
-
# This module gets prepended into RuboCop::AST::ProcessedSource.
|
25
|
-
module ProcessedSource
|
26
|
-
# This condition is compatible with rubocop-ast versions up to 1.30.0.
|
27
|
-
if RuboCop::AST::ProcessedSource.instance_method(:parser_class).arity == 1
|
28
|
-
# Redefine parser_class so that we can inject the prism parser into the
|
29
|
-
# list of known parsers.
|
30
|
-
def parser_class(ruby_version)
|
31
|
-
if ruby_version == Prism::Translation::Parser::VERSION_3_3
|
32
|
-
warn "WARN: Setting `TargetRubyVersion: 80_82_73_83_77.33` is deprecated. " \
|
33
|
-
"Set to `ParserEngine: parser_prism` and `TargetRubyVersion: 3.3` instead."
|
34
|
-
require_relative "../parser33"
|
35
|
-
Prism::Translation::Parser33
|
36
|
-
elsif ruby_version == Prism::Translation::Parser::VERSION_3_4
|
37
|
-
warn "WARN: Setting `TargetRubyVersion: 80_82_73_83_77.34` is deprecated. " \
|
38
|
-
"Set to `ParserEngine: parser_prism` and `TargetRubyVersion: 3.4` instead."
|
39
|
-
require_relative "../parser34"
|
40
|
-
Prism::Translation::Parser34
|
41
|
-
else
|
42
|
-
super
|
43
|
-
end
|
44
|
-
end
|
45
|
-
else
|
46
|
-
# Redefine parser_class so that we can inject the prism parser into the
|
47
|
-
# list of known parsers.
|
48
|
-
def parser_class(ruby_version, _parser_engine)
|
49
|
-
if ruby_version == Prism::Translation::Parser::VERSION_3_3
|
50
|
-
warn "WARN: Setting `TargetRubyVersion: 80_82_73_83_77.33` is deprecated. " \
|
51
|
-
"Set to `ParserEngine: parser_prism` and `TargetRubyVersion: 3.3` instead."
|
52
|
-
require_relative "../parser33"
|
53
|
-
Prism::Translation::Parser33
|
54
|
-
elsif ruby_version == Prism::Translation::Parser::VERSION_3_4
|
55
|
-
warn "WARN: Setting `TargetRubyVersion: 80_82_73_83_77.34` is deprecated. " \
|
56
|
-
"Set to `ParserEngine: parser_prism` and `TargetRubyVersion: 3.4` instead."
|
57
|
-
require_relative "../parser34"
|
58
|
-
Prism::Translation::Parser34
|
59
|
-
else
|
60
|
-
super
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
# :stopdoc:
|
70
|
-
RuboCop::AST::ProcessedSource.prepend(Prism::Translation::Parser::ProcessedSource)
|
71
|
-
known_rubies = RuboCop::TargetRuby.const_get(:KNOWN_RUBIES)
|
72
|
-
RuboCop::TargetRuby.send(:remove_const, :KNOWN_RUBIES)
|
73
|
-
RuboCop::TargetRuby::KNOWN_RUBIES = [*known_rubies, Prism::Translation::Parser::VERSION_3_3].freeze
|