rapydscript-ns 0.8.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.
- package/.agignore +1 -0
- package/.gitattributes +4 -0
- package/.github/workflows/ci.yml +38 -0
- package/.github/workflows/web-repl-page-deploy.yml +42 -0
- package/=template.pyj +5 -0
- package/CHANGELOG.md +456 -0
- package/CONTRIBUTORS +13 -0
- package/HACKING.md +103 -0
- package/LICENSE +24 -0
- package/README.md +2512 -0
- package/TODO.md +327 -0
- package/add-toc-to-readme +2 -0
- package/bin/export +75 -0
- package/bin/rapydscript +70 -0
- package/bin/web-repl-export +102 -0
- package/build +3 -0
- package/package.json +46 -0
- package/publish.py +37 -0
- package/release/baselib-plain-pretty.js +4370 -0
- package/release/baselib-plain-ugly.js +3 -0
- package/release/compiler.js +18394 -0
- package/release/signatures.json +31 -0
- package/session.vim +4 -0
- package/setup.cfg +2 -0
- package/src/ast.pyj +1356 -0
- package/src/baselib-builtins.pyj +279 -0
- package/src/baselib-containers.pyj +723 -0
- package/src/baselib-errors.pyj +37 -0
- package/src/baselib-internal.pyj +421 -0
- package/src/baselib-itertools.pyj +97 -0
- package/src/baselib-str.pyj +798 -0
- package/src/compiler.pyj +36 -0
- package/src/errors.pyj +30 -0
- package/src/lib/aes.pyj +646 -0
- package/src/lib/collections.pyj +695 -0
- package/src/lib/elementmaker.pyj +83 -0
- package/src/lib/encodings.pyj +126 -0
- package/src/lib/functools.pyj +148 -0
- package/src/lib/gettext.pyj +569 -0
- package/src/lib/itertools.pyj +580 -0
- package/src/lib/math.pyj +193 -0
- package/src/lib/numpy.pyj +2101 -0
- package/src/lib/operator.pyj +11 -0
- package/src/lib/pythonize.pyj +20 -0
- package/src/lib/random.pyj +118 -0
- package/src/lib/re.pyj +470 -0
- package/src/lib/traceback.pyj +63 -0
- package/src/lib/uuid.pyj +77 -0
- package/src/monaco-language-service/analyzer.js +526 -0
- package/src/monaco-language-service/builtins.js +543 -0
- package/src/monaco-language-service/completions.js +498 -0
- package/src/monaco-language-service/diagnostics.js +643 -0
- package/src/monaco-language-service/dts.js +550 -0
- package/src/monaco-language-service/hover.js +121 -0
- package/src/monaco-language-service/index.js +386 -0
- package/src/monaco-language-service/scope.js +162 -0
- package/src/monaco-language-service/signature.js +144 -0
- package/src/output/__init__.pyj +0 -0
- package/src/output/classes.pyj +296 -0
- package/src/output/codegen.pyj +492 -0
- package/src/output/comments.pyj +45 -0
- package/src/output/exceptions.pyj +105 -0
- package/src/output/functions.pyj +491 -0
- package/src/output/literals.pyj +109 -0
- package/src/output/loops.pyj +444 -0
- package/src/output/modules.pyj +329 -0
- package/src/output/operators.pyj +429 -0
- package/src/output/statements.pyj +463 -0
- package/src/output/stream.pyj +309 -0
- package/src/output/treeshake.pyj +182 -0
- package/src/output/utils.pyj +72 -0
- package/src/parse.pyj +3106 -0
- package/src/string_interpolation.pyj +72 -0
- package/src/tokenizer.pyj +702 -0
- package/src/unicode_aliases.pyj +576 -0
- package/src/utils.pyj +192 -0
- package/test/_import_one.pyj +37 -0
- package/test/_import_two/__init__.pyj +11 -0
- package/test/_import_two/level2/__init__.pyj +0 -0
- package/test/_import_two/level2/deep.pyj +4 -0
- package/test/_import_two/other.pyj +6 -0
- package/test/_import_two/sub.pyj +13 -0
- package/test/aes_vectors.pyj +421 -0
- package/test/annotations.pyj +80 -0
- package/test/baselib.pyj +319 -0
- package/test/classes.pyj +452 -0
- package/test/collections.pyj +152 -0
- package/test/decorators.pyj +77 -0
- package/test/dict_spread.pyj +76 -0
- package/test/docstrings.pyj +39 -0
- package/test/elementmaker_test.pyj +45 -0
- package/test/ellipsis.pyj +49 -0
- package/test/functions.pyj +151 -0
- package/test/generators.pyj +41 -0
- package/test/generic.pyj +370 -0
- package/test/imports.pyj +72 -0
- package/test/internationalization.pyj +73 -0
- package/test/lint.pyj +164 -0
- package/test/loops.pyj +85 -0
- package/test/numpy.pyj +734 -0
- package/test/omit_function_metadata.pyj +20 -0
- package/test/regexp.pyj +55 -0
- package/test/repl.pyj +121 -0
- package/test/scoped_flags.pyj +76 -0
- package/test/starargs.pyj +506 -0
- package/test/starred_assign.pyj +104 -0
- package/test/str.pyj +198 -0
- package/test/subscript_tuple.pyj +53 -0
- package/test/unit/fixtures/fibonacci_expected.js +46 -0
- package/test/unit/index.js +2989 -0
- package/test/unit/language-service-builtins.js +815 -0
- package/test/unit/language-service-completions.js +1067 -0
- package/test/unit/language-service-dts.js +543 -0
- package/test/unit/language-service-hover.js +455 -0
- package/test/unit/language-service-scope.js +833 -0
- package/test/unit/language-service-signature.js +458 -0
- package/test/unit/language-service.js +705 -0
- package/test/unit/run-language-service.js +41 -0
- package/test/unit/web-repl.js +484 -0
- package/tools/build-language-service.js +190 -0
- package/tools/cli.js +547 -0
- package/tools/compile.js +219 -0
- package/tools/compiler.js +108 -0
- package/tools/completer.js +131 -0
- package/tools/embedded_compiler.js +251 -0
- package/tools/export.js +316 -0
- package/tools/gettext.js +185 -0
- package/tools/ini.js +65 -0
- package/tools/lint.js +705 -0
- package/tools/msgfmt.js +187 -0
- package/tools/repl.js +223 -0
- package/tools/self.js +162 -0
- package/tools/test.js +118 -0
- package/tools/utils.js +128 -0
- package/tools/web_repl.js +95 -0
- package/try +41 -0
- package/web-repl/env.js +74 -0
- package/web-repl/index.html +163 -0
- package/web-repl/language-service.js +4084 -0
- package/web-repl/main.js +254 -0
- package/web-repl/prism.css +139 -0
- package/web-repl/prism.js +113 -0
- package/web-repl/rapydscript.js +435 -0
- package/web-repl/sha1.js +25 -0
|
@@ -0,0 +1,705 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* test/unit/language-service.js
|
|
3
|
+
*
|
|
4
|
+
* Unit tests for src/monaco-language-service/diagnostics.js (Phase 1).
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* node test/unit/language-service.js # run all tests
|
|
8
|
+
* node test/unit/language-service.js <test-name> # run a single test by name
|
|
9
|
+
*/
|
|
10
|
+
"use strict";
|
|
11
|
+
|
|
12
|
+
var assert = require("assert");
|
|
13
|
+
var path = require("path");
|
|
14
|
+
var url = require("url");
|
|
15
|
+
var compiler_module = require("../../tools/compiler");
|
|
16
|
+
var utils = require("../../tools/utils");
|
|
17
|
+
var colored = utils.safe_colored;
|
|
18
|
+
|
|
19
|
+
// Marker severity constants (same as Monaco's, used as defaults when Monaco
|
|
20
|
+
// is absent from the environment).
|
|
21
|
+
var SEV_ERROR = 8;
|
|
22
|
+
var SEV_WARNING = 4;
|
|
23
|
+
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
// Helpers
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
|
|
28
|
+
/** Return all markers with the given ident string in their message. */
|
|
29
|
+
function by_ident(markers, ident) {
|
|
30
|
+
return markers.filter(function (m) { return m.message.indexOf(ident) !== -1; });
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Assert exactly N markers of the given severity exist. */
|
|
34
|
+
function assert_count(markers, severity, n, label) {
|
|
35
|
+
var found = markers.filter(function (m) { return m.severity === severity; });
|
|
36
|
+
assert.strictEqual(found.length, n,
|
|
37
|
+
(label || "") + ": expected " + n + " marker(s) with severity " + severity +
|
|
38
|
+
" but got " + found.length + ": " + JSON.stringify(found.map(function (m) { return m.message; })));
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Assert a marker exists covering a specific 1-indexed line. */
|
|
42
|
+
function assert_line(markers, line, label) {
|
|
43
|
+
var found = markers.filter(function (m) { return m.startLineNumber === line; });
|
|
44
|
+
assert.ok(found.length > 0,
|
|
45
|
+
(label || "") + ": expected a marker at line " + line +
|
|
46
|
+
" but got none. All markers: " + JSON.stringify(markers));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// ---------------------------------------------------------------------------
|
|
50
|
+
// Test definitions
|
|
51
|
+
// ---------------------------------------------------------------------------
|
|
52
|
+
//
|
|
53
|
+
// Each test:
|
|
54
|
+
// name {string} – unique id (used for filtering)
|
|
55
|
+
// description {string} – what is exercised
|
|
56
|
+
// run(d) – receives a Diagnostics instance; throws on failure
|
|
57
|
+
|
|
58
|
+
function make_tests(Diagnostics, RS) {
|
|
59
|
+
|
|
60
|
+
function d(extras) { return new Diagnostics(RS, extras); }
|
|
61
|
+
|
|
62
|
+
var TESTS = [
|
|
63
|
+
|
|
64
|
+
// ── Clean code ────────────────────────────────────────────────────
|
|
65
|
+
|
|
66
|
+
{
|
|
67
|
+
name: "clean_no_markers",
|
|
68
|
+
description: "Valid code with no issues produces an empty marker array",
|
|
69
|
+
run: function () {
|
|
70
|
+
var markers = d().check([
|
|
71
|
+
"def add(a, b):",
|
|
72
|
+
" return a + b",
|
|
73
|
+
"result = add(1, 2)",
|
|
74
|
+
"print(result)",
|
|
75
|
+
].join("\n"));
|
|
76
|
+
assert.deepStrictEqual(markers, [],
|
|
77
|
+
"Expected no markers but got: " + JSON.stringify(markers));
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
|
|
81
|
+
{
|
|
82
|
+
name: "empty_source",
|
|
83
|
+
description: "Empty source produces no markers",
|
|
84
|
+
run: function () {
|
|
85
|
+
var markers = d().check("");
|
|
86
|
+
assert.deepStrictEqual(markers, []);
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
|
|
90
|
+
// ── Syntax errors ─────────────────────────────────────────────────
|
|
91
|
+
|
|
92
|
+
{
|
|
93
|
+
name: "syntax_error_marker",
|
|
94
|
+
description: "A syntax error is reported as a single Error marker",
|
|
95
|
+
run: function () {
|
|
96
|
+
var markers = d().check("def foo(\n");
|
|
97
|
+
assert.strictEqual(markers.length, 1);
|
|
98
|
+
assert.strictEqual(markers[0].severity, SEV_ERROR);
|
|
99
|
+
assert.strictEqual(markers[0].source, "rapydscript");
|
|
100
|
+
assert.ok(markers[0].message.length > 0);
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
|
|
104
|
+
{
|
|
105
|
+
name: "syntax_error_line_number",
|
|
106
|
+
description: "Syntax error marker has the correct line number",
|
|
107
|
+
run: function () {
|
|
108
|
+
// Error is on line 3 (the bad line)
|
|
109
|
+
var markers = d().check([
|
|
110
|
+
"x = 1",
|
|
111
|
+
"y = 2",
|
|
112
|
+
"def (:", // bad syntax
|
|
113
|
+
].join("\n"));
|
|
114
|
+
assert.strictEqual(markers.length, 1);
|
|
115
|
+
assert.strictEqual(markers[0].severity, SEV_ERROR);
|
|
116
|
+
// Line numbers should be >= 1 (exact line may vary by parser)
|
|
117
|
+
assert.ok(markers[0].startLineNumber >= 1);
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
|
|
121
|
+
// ── Undefined symbol ──────────────────────────────────────────────
|
|
122
|
+
|
|
123
|
+
{
|
|
124
|
+
name: "undef_symbol",
|
|
125
|
+
description: "A reference to an undeclared name produces an Error marker",
|
|
126
|
+
run: function () {
|
|
127
|
+
var markers = d().check("print(missing_variable)");
|
|
128
|
+
assert_count(markers, SEV_ERROR, 1, "undef_symbol");
|
|
129
|
+
assert.ok(markers[0].message.indexOf("missing_variable") !== -1,
|
|
130
|
+
"Expected marker message to contain 'missing_variable'");
|
|
131
|
+
},
|
|
132
|
+
},
|
|
133
|
+
|
|
134
|
+
{
|
|
135
|
+
name: "undef_multiple_symbols",
|
|
136
|
+
description: "Multiple undefined names each get their own marker",
|
|
137
|
+
run: function () {
|
|
138
|
+
var markers = d().check("print(aaa)\nprint(bbb)");
|
|
139
|
+
var errors = markers.filter(function (m) { return m.severity === SEV_ERROR; });
|
|
140
|
+
assert.ok(errors.length >= 2, "Expected at least 2 undef errors");
|
|
141
|
+
},
|
|
142
|
+
},
|
|
143
|
+
|
|
144
|
+
{
|
|
145
|
+
name: "undef_suppressed_builtins",
|
|
146
|
+
description: "Built-in names (print, len, range, etc.) are not flagged as undefined",
|
|
147
|
+
run: function () {
|
|
148
|
+
var markers = d().check([
|
|
149
|
+
"print(len([1, 2, 3]))",
|
|
150
|
+
"for i in range(5):",
|
|
151
|
+
" print(i)",
|
|
152
|
+
"x = isinstance(42, int)",
|
|
153
|
+
].join("\n"));
|
|
154
|
+
assert.deepStrictEqual(markers, []);
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
|
|
158
|
+
{
|
|
159
|
+
name: "undef_suppressed_extra_builtins",
|
|
160
|
+
description: "extraBuiltins option prevents a name from being flagged",
|
|
161
|
+
run: function () {
|
|
162
|
+
var inst = new Diagnostics(RS, { MY_GLOBAL: true, ANOTHER: true });
|
|
163
|
+
var markers = inst.check("print(MY_GLOBAL)\nprint(ANOTHER)");
|
|
164
|
+
assert.deepStrictEqual(markers, []);
|
|
165
|
+
},
|
|
166
|
+
},
|
|
167
|
+
|
|
168
|
+
{
|
|
169
|
+
name: "add_globals_method",
|
|
170
|
+
description: "addGlobals() suppresses names added after construction",
|
|
171
|
+
run: function () {
|
|
172
|
+
var inst = new Diagnostics(RS);
|
|
173
|
+
inst.addGlobals(["LATE_GLOBAL"]);
|
|
174
|
+
var markers = inst.check("print(LATE_GLOBAL)");
|
|
175
|
+
assert.deepStrictEqual(markers, []);
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
|
|
179
|
+
// ── Unused bindings ───────────────────────────────────────────────
|
|
180
|
+
|
|
181
|
+
{
|
|
182
|
+
name: "unused_local_variable",
|
|
183
|
+
description: "A local variable that is never used gets an Error marker",
|
|
184
|
+
run: function () {
|
|
185
|
+
var markers = d().check([
|
|
186
|
+
"def foo():",
|
|
187
|
+
" unused_var = 42",
|
|
188
|
+
" return 0",
|
|
189
|
+
"foo()",
|
|
190
|
+
].join("\n"));
|
|
191
|
+
assert_count(markers, SEV_ERROR, 1, "unused_local");
|
|
192
|
+
assert.ok(markers[0].message.indexOf("unused_var") !== -1);
|
|
193
|
+
},
|
|
194
|
+
},
|
|
195
|
+
|
|
196
|
+
{
|
|
197
|
+
name: "used_local_no_marker",
|
|
198
|
+
description: "A local variable that IS used produces no marker",
|
|
199
|
+
run: function () {
|
|
200
|
+
var markers = d().check([
|
|
201
|
+
"def foo():",
|
|
202
|
+
" x = 42",
|
|
203
|
+
" return x",
|
|
204
|
+
"foo()",
|
|
205
|
+
].join("\n"));
|
|
206
|
+
assert.deepStrictEqual(markers, []);
|
|
207
|
+
},
|
|
208
|
+
},
|
|
209
|
+
|
|
210
|
+
// ── Unused import ─────────────────────────────────────────────────
|
|
211
|
+
|
|
212
|
+
{
|
|
213
|
+
name: "unused_import",
|
|
214
|
+
description: "An imported name that is never used gets an Error marker",
|
|
215
|
+
run: function () {
|
|
216
|
+
var markers = d().check(
|
|
217
|
+
"from mymod import foo\nx = 1\nprint(x)",
|
|
218
|
+
{
|
|
219
|
+
virtualFiles: { mymod: "def foo(x): return x" },
|
|
220
|
+
}
|
|
221
|
+
);
|
|
222
|
+
assert_count(markers, SEV_ERROR, 1, "unused_import");
|
|
223
|
+
assert.ok(markers[0].message.indexOf("foo") !== -1);
|
|
224
|
+
},
|
|
225
|
+
},
|
|
226
|
+
|
|
227
|
+
{
|
|
228
|
+
name: "used_import_no_marker",
|
|
229
|
+
description: "An import that is actually used produces no marker",
|
|
230
|
+
run: function () {
|
|
231
|
+
var markers = d().check(
|
|
232
|
+
"from mymod import foo\nprint(foo(2))",
|
|
233
|
+
{
|
|
234
|
+
virtualFiles: { mymod: "def foo(x): return x" },
|
|
235
|
+
}
|
|
236
|
+
);
|
|
237
|
+
assert.deepStrictEqual(markers, []);
|
|
238
|
+
},
|
|
239
|
+
},
|
|
240
|
+
|
|
241
|
+
// ── Import errors ─────────────────────────────────────────────────
|
|
242
|
+
|
|
243
|
+
{
|
|
244
|
+
name: "import_error_caught",
|
|
245
|
+
description: "An unresolvable import is caught and returned as a single Error marker",
|
|
246
|
+
run: function () {
|
|
247
|
+
var markers = d().check("from nonexistent_lib_xyz import something");
|
|
248
|
+
assert.strictEqual(markers.length, 1);
|
|
249
|
+
assert.strictEqual(markers[0].severity, SEV_ERROR);
|
|
250
|
+
},
|
|
251
|
+
},
|
|
252
|
+
|
|
253
|
+
// ── Semicolon warnings ────────────────────────────────────────────
|
|
254
|
+
|
|
255
|
+
{
|
|
256
|
+
name: "eol_semicolon_warning",
|
|
257
|
+
description: "A trailing semicolon at end of a line produces a Warning",
|
|
258
|
+
run: function () {
|
|
259
|
+
var markers = d().check("x = 1;\nprint(x)");
|
|
260
|
+
assert_count(markers, SEV_WARNING, 1, "eol_semicolon");
|
|
261
|
+
},
|
|
262
|
+
},
|
|
263
|
+
|
|
264
|
+
{
|
|
265
|
+
name: "multiple_eol_semicolons",
|
|
266
|
+
description: "Each trailing-semicolon line gets its own Warning",
|
|
267
|
+
run: function () {
|
|
268
|
+
var markers = d().check("x = 1;\ny = 2;\nprint(x + y)");
|
|
269
|
+
var warnings = markers.filter(function (m) { return m.severity === SEV_WARNING; });
|
|
270
|
+
assert.ok(warnings.length >= 2,
|
|
271
|
+
"Expected at least 2 eol-semicolon warnings, got " + warnings.length);
|
|
272
|
+
},
|
|
273
|
+
},
|
|
274
|
+
|
|
275
|
+
// ── Defined-after-use ─────────────────────────────────────────────
|
|
276
|
+
|
|
277
|
+
{
|
|
278
|
+
name: "def_after_use",
|
|
279
|
+
description: "Using a name before assigning it in the same scope is flagged",
|
|
280
|
+
run: function () {
|
|
281
|
+
var markers = d().check([
|
|
282
|
+
"print(ahead)",
|
|
283
|
+
"ahead = 99",
|
|
284
|
+
].join("\n"));
|
|
285
|
+
// Expect a def-after-use error (or at minimum an error touching 'ahead')
|
|
286
|
+
var errors = markers.filter(function (m) { return m.severity === SEV_ERROR; });
|
|
287
|
+
assert.ok(errors.length >= 1, "Expected at least 1 error for def-after-use");
|
|
288
|
+
assert.ok(
|
|
289
|
+
errors.some(function (m) { return m.message.indexOf("ahead") !== -1; }),
|
|
290
|
+
"Expected error message to mention 'ahead'"
|
|
291
|
+
);
|
|
292
|
+
},
|
|
293
|
+
},
|
|
294
|
+
|
|
295
|
+
// ── Marker position accuracy ──────────────────────────────────────
|
|
296
|
+
|
|
297
|
+
{
|
|
298
|
+
name: "marker_positions_valid",
|
|
299
|
+
description: "Marker line/col numbers are 1-indexed positive integers",
|
|
300
|
+
run: function () {
|
|
301
|
+
var markers = d().check("print(undefined_xyz)");
|
|
302
|
+
assert.ok(markers.length > 0, "Expected at least one marker");
|
|
303
|
+
markers.forEach(function (m) {
|
|
304
|
+
assert.ok(m.startLineNumber >= 1, "startLineNumber must be >= 1");
|
|
305
|
+
assert.ok(m.startColumn >= 1, "startColumn must be >= 1");
|
|
306
|
+
assert.ok(m.endLineNumber >= m.startLineNumber,
|
|
307
|
+
"endLineNumber must be >= startLineNumber");
|
|
308
|
+
assert.ok(m.endColumn >= 1, "endColumn must be >= 1");
|
|
309
|
+
});
|
|
310
|
+
},
|
|
311
|
+
},
|
|
312
|
+
|
|
313
|
+
{
|
|
314
|
+
name: "marker_on_correct_line",
|
|
315
|
+
description: "An undefined symbol on line 3 produces a marker on line 3",
|
|
316
|
+
run: function () {
|
|
317
|
+
var markers = d().check([
|
|
318
|
+
"x = 1",
|
|
319
|
+
"y = 2",
|
|
320
|
+
"print(z_undefined)", // line 3
|
|
321
|
+
].join("\n"));
|
|
322
|
+
var errors = markers.filter(function (m) { return m.severity === SEV_ERROR; });
|
|
323
|
+
assert.ok(errors.length >= 1);
|
|
324
|
+
assert_line(errors, 3, "marker_on_correct_line");
|
|
325
|
+
},
|
|
326
|
+
},
|
|
327
|
+
|
|
328
|
+
// ── Duplicate method warning ──────────────────────────────────────
|
|
329
|
+
|
|
330
|
+
{
|
|
331
|
+
name: "dup_method_warning",
|
|
332
|
+
description: "Defining the same method name twice in a class produces a Warning",
|
|
333
|
+
run: function () {
|
|
334
|
+
var markers = d().check([
|
|
335
|
+
"class Foo:",
|
|
336
|
+
" def bar(self):",
|
|
337
|
+
" return 1",
|
|
338
|
+
" def bar(self):", // duplicate
|
|
339
|
+
" return 2",
|
|
340
|
+
"Foo()",
|
|
341
|
+
].join("\n"));
|
|
342
|
+
var warnings = markers.filter(function (m) { return m.severity === SEV_WARNING; });
|
|
343
|
+
assert.ok(warnings.length >= 1,
|
|
344
|
+
"Expected at least 1 dup-method warning, got " + warnings.length);
|
|
345
|
+
assert.ok(
|
|
346
|
+
warnings.some(function (m) { return m.message.indexOf("bar") !== -1; }),
|
|
347
|
+
"Expected warning to mention 'bar'"
|
|
348
|
+
);
|
|
349
|
+
},
|
|
350
|
+
},
|
|
351
|
+
|
|
352
|
+
// ── Loop variable shadowing ───────────────────────────────────────
|
|
353
|
+
|
|
354
|
+
{
|
|
355
|
+
name: "loop_shadowed",
|
|
356
|
+
description: "A for-loop variable that shadows a prior use in the scope is flagged",
|
|
357
|
+
run: function () {
|
|
358
|
+
var markers = d().check([
|
|
359
|
+
"def test():",
|
|
360
|
+
" i = 10",
|
|
361
|
+
" print(i)",
|
|
362
|
+
" for i in range(5):", // shadows previous i
|
|
363
|
+
" print(i)",
|
|
364
|
+
"test()",
|
|
365
|
+
].join("\n"));
|
|
366
|
+
var errors = markers.filter(function (m) { return m.severity === SEV_ERROR; });
|
|
367
|
+
assert.ok(errors.length >= 1,
|
|
368
|
+
"Expected at least 1 loop-shadowed error");
|
|
369
|
+
assert.ok(
|
|
370
|
+
errors.some(function (m) { return m.message.indexOf("i") !== -1; }),
|
|
371
|
+
"Expected error message to mention 'i'"
|
|
372
|
+
);
|
|
373
|
+
},
|
|
374
|
+
},
|
|
375
|
+
|
|
376
|
+
// ── Virtual files ─────────────────────────────────────────────────
|
|
377
|
+
|
|
378
|
+
{
|
|
379
|
+
name: "virtual_file_import_clean",
|
|
380
|
+
description: "Importing from a virtual file and using the symbol produces no markers",
|
|
381
|
+
run: function () {
|
|
382
|
+
var markers = d().check(
|
|
383
|
+
"from helper import greet\nprint(greet('world'))",
|
|
384
|
+
{
|
|
385
|
+
virtualFiles: {
|
|
386
|
+
helper: "def greet(name): return 'hello ' + name",
|
|
387
|
+
},
|
|
388
|
+
}
|
|
389
|
+
);
|
|
390
|
+
assert.deepStrictEqual(markers, []);
|
|
391
|
+
},
|
|
392
|
+
},
|
|
393
|
+
|
|
394
|
+
// ── Severity field ────────────────────────────────────────────────
|
|
395
|
+
|
|
396
|
+
{
|
|
397
|
+
name: "severity_values",
|
|
398
|
+
description: "Error markers have severity 8 and Warning markers have severity 4",
|
|
399
|
+
run: function () {
|
|
400
|
+
// Semicolon → Warning; undef → Error
|
|
401
|
+
var markers = d().check("missing_sym\nx = 1;");
|
|
402
|
+
var errors = markers.filter(function (m) { return m.severity === SEV_ERROR; });
|
|
403
|
+
var warnings = markers.filter(function (m) { return m.severity === SEV_WARNING; });
|
|
404
|
+
assert.ok(errors.length >= 1, "Expected at least one Error marker");
|
|
405
|
+
assert.ok(warnings.length >= 1, "Expected at least one Warning marker");
|
|
406
|
+
},
|
|
407
|
+
},
|
|
408
|
+
|
|
409
|
+
{
|
|
410
|
+
name: "itertools_import_no_errors",
|
|
411
|
+
description: "Importing and using itertools symbols produces no error markers",
|
|
412
|
+
run: function () {
|
|
413
|
+
var markers = d().check([
|
|
414
|
+
"from itertools import chain, islice, count, cycle, repeat",
|
|
415
|
+
"from itertools import accumulate, compress, dropwhile, filterfalse",
|
|
416
|
+
"from itertools import groupby, pairwise, starmap, takewhile, zip_longest",
|
|
417
|
+
"from itertools import product, permutations, combinations",
|
|
418
|
+
"from itertools import combinations_with_replacement",
|
|
419
|
+
"a = list(islice(count(), 3))",
|
|
420
|
+
"b = list(chain([1, 2], [3, 4]))",
|
|
421
|
+
"c = list(islice(cycle([1, 2]), 4))",
|
|
422
|
+
"d2 = list(repeat(5, 3))",
|
|
423
|
+
"e = list(accumulate([1, 2, 3]))",
|
|
424
|
+
"f = list(compress([1,2,3], [1,0,1]))",
|
|
425
|
+
"g = list(dropwhile(lambda x: x < 2, [1,2,3]))",
|
|
426
|
+
"h = list(filterfalse(lambda x: x % 2, [1,2,3,4]))",
|
|
427
|
+
"i2 = list(groupby([1,1,2]))",
|
|
428
|
+
"j = list(pairwise([1,2,3]))",
|
|
429
|
+
"k = list(starmap(lambda a,b: a+b, [[1,2],[3,4]]))",
|
|
430
|
+
"l2 = list(takewhile(lambda x: x < 3, [1,2,3,4]))",
|
|
431
|
+
"m = list(zip_longest([1,2],[3]))",
|
|
432
|
+
"n2 = list(product([1,2],[3,4]))",
|
|
433
|
+
"o = list(permutations([1,2,3], 2))",
|
|
434
|
+
"p = list(combinations([1,2,3], 2))",
|
|
435
|
+
"q = list(combinations_with_replacement([1,2], 2))",
|
|
436
|
+
].join("\n"));
|
|
437
|
+
var errors = markers.filter(function (m) { return m.severity === SEV_ERROR; });
|
|
438
|
+
assert.deepStrictEqual(errors, [],
|
|
439
|
+
"Expected no errors but got: " + JSON.stringify(errors));
|
|
440
|
+
},
|
|
441
|
+
},
|
|
442
|
+
|
|
443
|
+
{
|
|
444
|
+
name: "op_overloading_no_errors",
|
|
445
|
+
description: "Class with arithmetic dunder methods and overload_operators flag produces no errors",
|
|
446
|
+
run: function () {
|
|
447
|
+
var markers = d().check([
|
|
448
|
+
"from __python__ import overload_operators",
|
|
449
|
+
"class Vec:",
|
|
450
|
+
" def __init__(self, x, y):",
|
|
451
|
+
" self.x = x",
|
|
452
|
+
" self.y = y",
|
|
453
|
+
" def __add__(self, other):",
|
|
454
|
+
" return Vec(self.x + other.x, self.y + other.y)",
|
|
455
|
+
" def __neg__(self):",
|
|
456
|
+
" return Vec(-self.x, -self.y)",
|
|
457
|
+
"a = Vec(1, 2)",
|
|
458
|
+
"b = Vec(3, 4)",
|
|
459
|
+
"c = a + b",
|
|
460
|
+
"d2 = -a",
|
|
461
|
+
].join("\n"));
|
|
462
|
+
var errors = markers.filter(function (m) { return m.severity === SEV_ERROR; });
|
|
463
|
+
assert.deepStrictEqual(errors, [],
|
|
464
|
+
"Expected no errors but got: " + JSON.stringify(errors));
|
|
465
|
+
},
|
|
466
|
+
},
|
|
467
|
+
|
|
468
|
+
{
|
|
469
|
+
name: "class_dunder_no_errors",
|
|
470
|
+
description: "__name__, __qualname__, __module__, __class__ access on classes/instances produces no errors",
|
|
471
|
+
run: function () {
|
|
472
|
+
var markers = d().check([
|
|
473
|
+
"class Foo:",
|
|
474
|
+
" def __init__(self):",
|
|
475
|
+
" pass",
|
|
476
|
+
"name = Foo.__name__",
|
|
477
|
+
"qual = Foo.__qualname__",
|
|
478
|
+
"mod = Foo.__module__",
|
|
479
|
+
"obj = Foo()",
|
|
480
|
+
"cls = obj.__class__",
|
|
481
|
+
].join("\n"));
|
|
482
|
+
var errors = markers.filter(function (m) { return m.severity === SEV_ERROR; });
|
|
483
|
+
assert.deepStrictEqual(errors, [],
|
|
484
|
+
"Expected no errors but got: " + JSON.stringify(errors));
|
|
485
|
+
},
|
|
486
|
+
},
|
|
487
|
+
|
|
488
|
+
{
|
|
489
|
+
name: "classmethod_no_errors",
|
|
490
|
+
description: "@classmethod decorator produces no error markers; cls param is recognized",
|
|
491
|
+
run: function () {
|
|
492
|
+
var markers = d().check([
|
|
493
|
+
"class Factory:",
|
|
494
|
+
" @classmethod",
|
|
495
|
+
" def create(cls, value):",
|
|
496
|
+
" obj = cls()",
|
|
497
|
+
" obj.value = value",
|
|
498
|
+
" return obj",
|
|
499
|
+
" @classmethod",
|
|
500
|
+
" def from_string(cls, s):",
|
|
501
|
+
" return cls.create(int(s))",
|
|
502
|
+
" def __init__(self):",
|
|
503
|
+
" self.value = 0",
|
|
504
|
+
"f = Factory.create(42)",
|
|
505
|
+
"g = Factory.from_string('7')",
|
|
506
|
+
].join("\n"));
|
|
507
|
+
var errors = markers.filter(function (m) { return m.severity === SEV_ERROR; });
|
|
508
|
+
assert.deepStrictEqual(errors, [],
|
|
509
|
+
"Expected no errors but got: " + JSON.stringify(errors));
|
|
510
|
+
},
|
|
511
|
+
},
|
|
512
|
+
|
|
513
|
+
{
|
|
514
|
+
name: "classmethod_classvar_no_errors",
|
|
515
|
+
description: "cls.classvar in @classmethod body produces no error markers",
|
|
516
|
+
run: function () {
|
|
517
|
+
var markers = d().check([
|
|
518
|
+
"class Counter:",
|
|
519
|
+
" count = 0",
|
|
520
|
+
" @classmethod",
|
|
521
|
+
" def increment(cls):",
|
|
522
|
+
" cls.count += 1",
|
|
523
|
+
" @classmethod",
|
|
524
|
+
" def get_count(cls):",
|
|
525
|
+
" return cls.count",
|
|
526
|
+
"Counter.increment()",
|
|
527
|
+
"x = Counter.get_count()",
|
|
528
|
+
].join("\n"));
|
|
529
|
+
var errors = markers.filter(function (m) { return m.severity === SEV_ERROR; });
|
|
530
|
+
assert.deepStrictEqual(errors, [],
|
|
531
|
+
"Expected no errors but got: " + JSON.stringify(errors));
|
|
532
|
+
},
|
|
533
|
+
},
|
|
534
|
+
|
|
535
|
+
{
|
|
536
|
+
name: "nested_comprehension_no_errors",
|
|
537
|
+
description: "Nested comprehensions produce no error markers",
|
|
538
|
+
run: function () {
|
|
539
|
+
var markers = d().check([
|
|
540
|
+
"flat = [x for row in [[1,2],[3,4]] for x in row]",
|
|
541
|
+
"evens = [x for row in [[1,2,3],[4,5,6]] for x in row if x % 2 == 0]",
|
|
542
|
+
"pairs = [[i, j] for i in range(3) for j in range(i)]",
|
|
543
|
+
"s = {x + y for x in range(3) for y in range(3)}",
|
|
544
|
+
].join("\n"));
|
|
545
|
+
var errors = markers.filter(function (m) { return m.severity === SEV_ERROR; });
|
|
546
|
+
assert.deepStrictEqual(errors, [],
|
|
547
|
+
"Expected no errors but got: " + JSON.stringify(errors));
|
|
548
|
+
},
|
|
549
|
+
},
|
|
550
|
+
|
|
551
|
+
// ── pythonFlags option ─────────────────────────────────────────────
|
|
552
|
+
|
|
553
|
+
{
|
|
554
|
+
name: "python_flags_no_errors_when_flag_active",
|
|
555
|
+
description: "pythonFlags constructor option activates flags without inline import",
|
|
556
|
+
run: function () {
|
|
557
|
+
// Without pythonFlags, using overload_operators syntax requires
|
|
558
|
+
// `from __python__ import overload_operators` in the source.
|
|
559
|
+
// With pythonFlags, it should be active globally.
|
|
560
|
+
var d_with_flags = new Diagnostics(RS, null, "overload_operators,overload_getitem");
|
|
561
|
+
var markers = d_with_flags.check([
|
|
562
|
+
"class Vec:",
|
|
563
|
+
" def __init__(self, x):",
|
|
564
|
+
" self.x = x",
|
|
565
|
+
" def __add__(self, other):",
|
|
566
|
+
" return Vec(self.x + other.x)",
|
|
567
|
+
"a = Vec(1)",
|
|
568
|
+
"b = Vec(2)",
|
|
569
|
+
"c = a + b",
|
|
570
|
+
].join("\n"));
|
|
571
|
+
var errors = markers.filter(function (m) { return m.severity === SEV_ERROR; });
|
|
572
|
+
assert.deepStrictEqual(errors, [],
|
|
573
|
+
"Expected no errors with pythonFlags active, got: " + JSON.stringify(errors));
|
|
574
|
+
},
|
|
575
|
+
},
|
|
576
|
+
|
|
577
|
+
{
|
|
578
|
+
name: "python_flags_constructor_same_as_inline_import",
|
|
579
|
+
description: "pythonFlags constructor option produces same parse result as inline from __python__ import",
|
|
580
|
+
run: function () {
|
|
581
|
+
// Code using overload_operators — no inline import
|
|
582
|
+
var src_no_import = [
|
|
583
|
+
"class N:",
|
|
584
|
+
" def __init__(self, v): self.v = v",
|
|
585
|
+
" def __add__(self, o): return N(self.v + o.v)",
|
|
586
|
+
"a = N(1)",
|
|
587
|
+
"b = N(2)",
|
|
588
|
+
"x = a + b",
|
|
589
|
+
].join("\n");
|
|
590
|
+
|
|
591
|
+
// With inline import
|
|
592
|
+
var src_with_import = [
|
|
593
|
+
"from __python__ import overload_operators",
|
|
594
|
+
].concat(src_no_import.split("\n")).join("\n");
|
|
595
|
+
|
|
596
|
+
// Both should parse without error when the flag is active
|
|
597
|
+
var d_flags = new Diagnostics(RS, null, "overload_operators");
|
|
598
|
+
var d_inline = new Diagnostics(RS, null, null);
|
|
599
|
+
|
|
600
|
+
var m_flags = d_flags.check(src_no_import);
|
|
601
|
+
var m_inline = d_inline.check(src_with_import);
|
|
602
|
+
|
|
603
|
+
var err_flags = m_flags.filter(function (m) { return m.severity === SEV_ERROR; });
|
|
604
|
+
var err_inline = m_inline.filter(function (m) { return m.severity === SEV_ERROR; });
|
|
605
|
+
|
|
606
|
+
assert.deepStrictEqual(err_flags, [],
|
|
607
|
+
"Flags: expected no errors, got: " + JSON.stringify(err_flags));
|
|
608
|
+
assert.deepStrictEqual(err_inline, [],
|
|
609
|
+
"Inline: expected no errors, got: " + JSON.stringify(err_inline));
|
|
610
|
+
},
|
|
611
|
+
},
|
|
612
|
+
|
|
613
|
+
{
|
|
614
|
+
name: "dict_spread_no_errors",
|
|
615
|
+
description: "Dict merge literal {**d1, **d2} produces no error markers",
|
|
616
|
+
run: function () {
|
|
617
|
+
var markers = d().check([
|
|
618
|
+
"d1 = {'a': 1}",
|
|
619
|
+
"d2 = {'b': 2}",
|
|
620
|
+
"merged = {**d1, **d2}",
|
|
621
|
+
"mixed = {**d1, 'c': 3}",
|
|
622
|
+
"single = {**d1}",
|
|
623
|
+
].join("\n"));
|
|
624
|
+
var errors = markers.filter(function (m) { return m.severity === SEV_ERROR; });
|
|
625
|
+
assert.deepStrictEqual(errors, [],
|
|
626
|
+
"Expected no errors for dict spread, got: " + JSON.stringify(errors));
|
|
627
|
+
},
|
|
628
|
+
},
|
|
629
|
+
|
|
630
|
+
{
|
|
631
|
+
name: "dict_spread_no_dup_key_false_positive",
|
|
632
|
+
description: "Spread items do not trigger dup-key warnings alongside real keys",
|
|
633
|
+
run: function () {
|
|
634
|
+
var markers = d().check([
|
|
635
|
+
"d1 = {'a': 1}",
|
|
636
|
+
"result = {**d1, 'b': 2, 'c': 3}",
|
|
637
|
+
].join("\n"));
|
|
638
|
+
var dup_key = markers.filter(function (m) { return m.message.indexOf("dup") !== -1; });
|
|
639
|
+
assert.deepStrictEqual(dup_key, [],
|
|
640
|
+
"Expected no dup-key warnings for spread, got: " + JSON.stringify(dup_key));
|
|
641
|
+
},
|
|
642
|
+
},
|
|
643
|
+
|
|
644
|
+
];
|
|
645
|
+
|
|
646
|
+
return TESTS;
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
// ---------------------------------------------------------------------------
|
|
650
|
+
// Runner (mirrors test/unit/index.js style)
|
|
651
|
+
// ---------------------------------------------------------------------------
|
|
652
|
+
|
|
653
|
+
function run_tests(TESTS, filter) {
|
|
654
|
+
var tests = filter
|
|
655
|
+
? TESTS.filter(function (t) { return t.name === filter; })
|
|
656
|
+
: TESTS;
|
|
657
|
+
|
|
658
|
+
if (tests.length === 0) {
|
|
659
|
+
console.error(colored("No test found: " + filter, "red"));
|
|
660
|
+
process.exit(1);
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
var failures = [];
|
|
664
|
+
|
|
665
|
+
tests.forEach(function (test) {
|
|
666
|
+
try {
|
|
667
|
+
test.run();
|
|
668
|
+
console.log(colored("PASS " + test.name, "green") +
|
|
669
|
+
" – " + test.description);
|
|
670
|
+
} catch (e) {
|
|
671
|
+
failures.push(test.name);
|
|
672
|
+
var msg = e.message || String(e);
|
|
673
|
+
console.log(colored("FAIL " + test.name, "red") +
|
|
674
|
+
"\n " + msg + "\n");
|
|
675
|
+
}
|
|
676
|
+
});
|
|
677
|
+
|
|
678
|
+
console.log("");
|
|
679
|
+
if (failures.length) {
|
|
680
|
+
console.log(colored(failures.length + " test(s) failed.", "red"));
|
|
681
|
+
} else {
|
|
682
|
+
console.log(colored("All " + tests.length + " language-service tests passed!", "green"));
|
|
683
|
+
}
|
|
684
|
+
process.exit(failures.length ? 1 : 0);
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
// ---------------------------------------------------------------------------
|
|
688
|
+
// Entry point — use dynamic import() to load the ES module
|
|
689
|
+
// ---------------------------------------------------------------------------
|
|
690
|
+
|
|
691
|
+
var diagnostics_path = url.pathToFileURL(
|
|
692
|
+
path.join(__dirname, "../../src/monaco-language-service/diagnostics.js")
|
|
693
|
+
).href;
|
|
694
|
+
|
|
695
|
+
var filter = process.argv[2] || null;
|
|
696
|
+
|
|
697
|
+
import(diagnostics_path).then(function (mod) {
|
|
698
|
+
var Diagnostics = mod.Diagnostics;
|
|
699
|
+
var RS = compiler_module.create_compiler();
|
|
700
|
+
var TESTS = make_tests(Diagnostics, RS);
|
|
701
|
+
run_tests(TESTS, filter);
|
|
702
|
+
}).catch(function (e) {
|
|
703
|
+
console.error(colored("Failed to load language service module:", "red"), e);
|
|
704
|
+
process.exit(1);
|
|
705
|
+
});
|