@sasjs/lint 5.2.0 → 5.3.1
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/README.md +32 -0
- package/package.json +1 -1
- package/rules/file/hasMacroParentheses.js +6 -16
- package/rules/file/hasMacroParentheses.js.map +1 -1
- package/rules/file/index.d.ts +1 -0
- package/rules/file/index.js +3 -1
- package/rules/file/index.js.map +1 -1
- package/rules/file/noMalformedMacroCalls.d.ts +7 -0
- package/rules/file/noMalformedMacroCalls.js +127 -0
- package/rules/file/noMalformedMacroCalls.js.map +1 -0
- package/sasjslint-schema.json +17 -0
- package/types/LintConfig.js +5 -0
- package/types/LintConfig.js.map +1 -1
- package/utils/getIndex.d.ts +12 -0
- package/utils/getIndex.js +32 -0
- package/utils/getIndex.js.map +1 -0
- package/utils/getLintConfig.d.ts +13 -0
- package/utils/getLintConfig.js +32 -5
- package/utils/getLintConfig.js.map +1 -1
- package/utils/index.d.ts +2 -0
- package/utils/index.js +2 -0
- package/utils/index.js.map +1 -1
- package/utils/parseMacroCallArguments.d.ts +58 -0
- package/utils/parseMacroCallArguments.js +176 -0
- package/utils/parseMacroCallArguments.js.map +1 -0
package/README.md
CHANGED
|
@@ -40,6 +40,7 @@ Configuration is via a `.sasjslint` file with the following structure (these are
|
|
|
40
40
|
"noEncodedPasswords": true,
|
|
41
41
|
"noGremlins": true,
|
|
42
42
|
"noMacroIfInOpenCode": false,
|
|
43
|
+
"noMalformedMacroCalls": false,
|
|
43
44
|
"noNestedCommentsInHeader": true,
|
|
44
45
|
"noNestedMacros": true,
|
|
45
46
|
"noSingleAsteriskComments": false,
|
|
@@ -58,6 +59,8 @@ Configuration is via a `.sasjslint` file with the following structure (these are
|
|
|
58
59
|
|
|
59
60
|
Two keys do not have a section of their own. `requiredMacroOptions` is documented with [`hasRequiredMacroOptions`](#hasrequiredmacrooptions), the rule it configures. The deprecated alias `noTabIndentation` is accepted in place of `noTabs`, and is therefore not listed above - it is an alternative spelling rather than a setting of its own.
|
|
60
61
|
|
|
62
|
+
The file is read as strict JSON, so it cannot carry comments: a file that does not parse is reported as an error, naming the file, rather than being treated as though no configuration were present. Add `"$schema": "https://cli.sasjs.io/sasjslint-schema.json"` to get completion, hover text and validation for every setting in an editor that understands JSON schemas - `sasjs lint init` and a SASjs Server's seeded drive-root file both write it.
|
|
63
|
+
|
|
61
64
|
## SAS Lint Settings
|
|
62
65
|
|
|
63
66
|
Each setting can have three states:
|
|
@@ -416,6 +419,35 @@ Two boundaries are deliberate. A definition with no `%mend` reads to the end of
|
|
|
416
419
|
- Default: false
|
|
417
420
|
- Severity: WARNING
|
|
418
421
|
|
|
422
|
+
### noMalformedMacroCalls
|
|
423
|
+
|
|
424
|
+
A macro call is read by the macro tokenizer before the engine runs it, and the tokenizer can read more than the author wrote. An unterminated quote or comment, or a parenthesis that never closes, makes the rest of the file part of the value: the engine reports `ERROR: Macro parameter contains syntax error` at the end of the file, or reports a different error somewhere further down, so the mistake and its symptom sit pages apart. A comma inside an unquoted value splits the parameter list instead, and the engine answers `ERROR: All positional parameters must precede keyword parameters`.
|
|
425
|
+
|
|
426
|
+
This rule reports two shapes, both confirmed on SAS 9.4:
|
|
427
|
+
|
|
428
|
+
- the call never closes - `unclosed quote`, `unclosed parenthesis`, `unterminated comment`;
|
|
429
|
+
- a parameter after the first `name=` one that carries no `name=` - text spilled at a comma inside a value, reported on the spilled text itself. When nothing was spilled (`%foo(a=1, b=2,)`, or a parameter holding only a comment) the message is `an empty parameter follows a comma`, because that is what the engine makes of it: a positional parameter, and an error. A quoted name (`%foo(a=1, "b"=2)`) gets its own message, because the engine names that fault exactly: `ERROR: Invalid macro parameter name "b"`. A masked name (`%foo(a=1, %str(b)=2)`) is refused too - the engine reads the masked text as a positional parameter, not a keyword one - so it has its own message as well.
|
|
430
|
+
|
|
431
|
+
Each reason names its own repair: spill the value inside `%str()` when text was spilled, remove the comma when nothing was spilled, drop the quotes from a quoted name, and drop the mask from a masked one. Masking a parameter _name_ is the one place where `%str()` is the fault rather than the cure.
|
|
432
|
+
|
|
433
|
+
```sas
|
|
434
|
+
%put %kw(a=Check the library's lines, b=2); /* unclosed quote */
|
|
435
|
+
%put %kw(a=1, wider than 80); /* "wider than 80" is not a name=value parameter */
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
The scan follows the macro tokenizer: it tracks single and double quotes, parenthesis depth and block comments, and steps over the whole argument of a masking function (`%str`, `%nrstr`, `%quote`, `%superq`, `%qsysfunc` and the rest of the `%q` family), because a comma or a quote inside one is data. A parameter therefore splits only at a comma that sits at depth zero, outside a quote and outside a mask.
|
|
439
|
+
|
|
440
|
+
The cases this rule stays silent on are worth naming, because each one is accepted by the engine and delivers its values intact: a comma inside a quoted or masked value, a positional call (`%foo(one, two)`), a space-separated list passed as one positional (`%foo(one two three)`), a comment between parameters, a value continued onto the next line, a keyword whose name arrives through a macro trigger (`%foo(a=1, &k=2)` with `%let k=b;`, which the macro processor resolves before it parses the parameter list), and a nested call used as a value.
|
|
441
|
+
|
|
442
|
+
The trigger is the one place where the rule knowingly under-reports. When the macro variable is **not** set, the engine leaves the text as written and refuses the call - `%foo(&k=9)` gives `WARNING: Apparent symbolic reference K not resolved` and `ERROR: More positional parameters found than defined` - but whether a trigger resolves depends on what ran before, and a file cannot be read for that. The rule therefore stays silent on a trigger name, and the engine's own warning is the right place to catch it.
|
|
443
|
+
|
|
444
|
+
A call that closes early - `%foo(a=1), b=2)` - is **not** reported. The closing parenthesis belongs to the call and the leftover text is an ordinary statement error, and a nested call used as a value has exactly the same shape, so there is no way to tell them apart from the text alone.
|
|
445
|
+
|
|
446
|
+
The rule is **off by default**. It models the tokenizer rather than the grammar, so the sensible way to adopt it is to run it over a project once, read the findings, and then turn it on.
|
|
447
|
+
|
|
448
|
+
- Default: false
|
|
449
|
+
- Severity: WARNING
|
|
450
|
+
|
|
419
451
|
### noNestedCommentsInHeader
|
|
420
452
|
|
|
421
453
|
A doxygen header is a SAS block comment, and SAS block comments do not nest: the first close marker ends the comment. A `/* ... */` inside the header therefore closes the header early, and the rest of it - `@param` lines, and any code in a usage example - is compiled as SAS. Header text that holds `/*/`, such as a glob like `src/*/file.sas`, ends the header the same way.
|
package/package.json
CHANGED
|
@@ -4,6 +4,7 @@ exports.hasMacroParentheses = void 0;
|
|
|
4
4
|
const LintRuleType_1 = require("../../types/LintRuleType");
|
|
5
5
|
const Severity_1 = require("../../types/Severity");
|
|
6
6
|
const getColumnNumber_1 = require("../../utils/getColumnNumber");
|
|
7
|
+
const getIndex_1 = require("../../utils/getIndex");
|
|
7
8
|
const parseMacros_1 = require("../../utils/parseMacros");
|
|
8
9
|
const utils_1 = require("../../utils");
|
|
9
10
|
const getSeverity_1 = require("../../utils/getSeverity");
|
|
@@ -20,19 +21,6 @@ const message = 'Macro definition missing parentheses';
|
|
|
20
21
|
const parameterlessDeclaration = (macroName) => new RegExp(`(%macro\\s+${(0, utils_1.escapeRegExp)(macroName)})((?:\\s*/\\*.*?\\*/)?\\s*;)`, 'i');
|
|
21
22
|
/** The line of the declaration that carries the macro name. */
|
|
22
23
|
const nameLine = (macro) => macro.declarationLines.findIndex((declarationLine) => declarationLine.includes(macro.name));
|
|
23
|
-
/**
|
|
24
|
-
* The offset at which each line of the text starts, measured on the text as it
|
|
25
|
-
* stands. Used to edit one line without re-joining every other one, which would
|
|
26
|
-
* rewrite line endings this rule has no business touching.
|
|
27
|
-
*/
|
|
28
|
-
const lineStarts = (text) => {
|
|
29
|
-
const starts = [0];
|
|
30
|
-
for (let index = 0; index < text.length; index++) {
|
|
31
|
-
if (text[index] === '\n')
|
|
32
|
-
starts.push(index + 1);
|
|
33
|
-
}
|
|
34
|
-
return starts;
|
|
35
|
-
};
|
|
36
24
|
const test = (value, config) => {
|
|
37
25
|
const diagnostics = [];
|
|
38
26
|
const macros = (0, parseMacros_1.parseMacros)(value, config);
|
|
@@ -89,9 +77,11 @@ const fix = (value, config) => {
|
|
|
89
77
|
.filter(({ index }) => index !== -1);
|
|
90
78
|
return declarations.reduce((text, { macro, index }) => {
|
|
91
79
|
const lineNumber = macro.startLineNumbers[index];
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
80
|
+
// The offsets of this line and of the next one, measured on the text as it
|
|
81
|
+
// stands. `getIndex` clamps to the end of the file, so the last line ends
|
|
82
|
+
// there.
|
|
83
|
+
const start = (0, getIndex_1.getIndex)(text, lineNumber, 1);
|
|
84
|
+
const end = (0, getIndex_1.getIndex)(text, lineNumber + 1, 1);
|
|
95
85
|
// The line is taken with its terminator and put back with it, so every byte
|
|
96
86
|
// of the file other than the two this fix inserts is left alone.
|
|
97
87
|
const line = text.slice(start, end);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hasMacroParentheses.js","sourceRoot":"","sources":["../../../src/rules/file/hasMacroParentheses.ts"],"names":[],"mappings":";;;AAEA,2DAAuD;AACvD,mDAA+C;AAC/C,iEAA6D;AAC7D,yDAAqD;AAGrD,uCAA0C;AAC1C,yDAAqD;AAErD,MAAM,IAAI,GAAG,qBAAqB,CAAA;AAClC,MAAM,WAAW,GAAG,4DAA4D,CAAA;AAChF,MAAM,OAAO,GAAG,sCAAsC,CAAA;AAEtD;;;;;;GAMG;AACH,MAAM,wBAAwB,GAAG,CAAC,SAAiB,EAAE,EAAE,CACrD,IAAI,MAAM,CACR,cAAc,IAAA,oBAAY,EAAC,SAAS,CAAC,8BAA8B,EACnE,GAAG,CACJ,CAAA;AAEH,+DAA+D;AAC/D,MAAM,QAAQ,GAAG,CAAC,KAAY,EAAU,EAAE,CACxC,KAAK,CAAC,gBAAgB,CAAC,SAAS,CAAC,CAAC,eAAe,EAAE,EAAE,CACnD,eAAe,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CACrC,CAAA;AAEH
|
|
1
|
+
{"version":3,"file":"hasMacroParentheses.js","sourceRoot":"","sources":["../../../src/rules/file/hasMacroParentheses.ts"],"names":[],"mappings":"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"}
|
package/rules/file/index.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ export { hasMacroParentheses } from './hasMacroParentheses';
|
|
|
4
4
|
export { lineEndings } from './lineEndings';
|
|
5
5
|
export { minMacroNameLength } from './minMacroNameLength';
|
|
6
6
|
export { noMacroIfInOpenCode } from './noMacroIfInOpenCode';
|
|
7
|
+
export { noMalformedMacroCalls } from './noMalformedMacroCalls';
|
|
7
8
|
export { noNestedCommentsInHeader } from './noNestedCommentsInHeader';
|
|
8
9
|
export { noNestedMacros } from './noNestedMacros';
|
|
9
10
|
export { noXcmd } from './noXcmd';
|
package/rules/file/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.lowerCaseMacroNames = exports.noSingleAsteriskComments = exports.hasRequiredMacroOptions = exports.strictMacroDefinition = exports.noUnusedLibnames = exports.noUnusedMacros = exports.noUndeclaredMacros = exports.noXcmd = exports.noNestedMacros = exports.noNestedCommentsInHeader = exports.noMacroIfInOpenCode = exports.minMacroNameLength = exports.lineEndings = exports.hasMacroParentheses = exports.hasMacroNameInMend = exports.hasDoxygenHeader = void 0;
|
|
3
|
+
exports.lowerCaseMacroNames = exports.noSingleAsteriskComments = exports.hasRequiredMacroOptions = exports.strictMacroDefinition = exports.noUnusedLibnames = exports.noUnusedMacros = exports.noUndeclaredMacros = exports.noXcmd = exports.noNestedMacros = exports.noNestedCommentsInHeader = exports.noMalformedMacroCalls = exports.noMacroIfInOpenCode = exports.minMacroNameLength = exports.lineEndings = exports.hasMacroParentheses = exports.hasMacroNameInMend = exports.hasDoxygenHeader = void 0;
|
|
4
4
|
var hasDoxygenHeader_1 = require("./hasDoxygenHeader");
|
|
5
5
|
Object.defineProperty(exports, "hasDoxygenHeader", { enumerable: true, get: function () { return hasDoxygenHeader_1.hasDoxygenHeader; } });
|
|
6
6
|
var hasMacroNameInMend_1 = require("./hasMacroNameInMend");
|
|
@@ -13,6 +13,8 @@ var minMacroNameLength_1 = require("./minMacroNameLength");
|
|
|
13
13
|
Object.defineProperty(exports, "minMacroNameLength", { enumerable: true, get: function () { return minMacroNameLength_1.minMacroNameLength; } });
|
|
14
14
|
var noMacroIfInOpenCode_1 = require("./noMacroIfInOpenCode");
|
|
15
15
|
Object.defineProperty(exports, "noMacroIfInOpenCode", { enumerable: true, get: function () { return noMacroIfInOpenCode_1.noMacroIfInOpenCode; } });
|
|
16
|
+
var noMalformedMacroCalls_1 = require("./noMalformedMacroCalls");
|
|
17
|
+
Object.defineProperty(exports, "noMalformedMacroCalls", { enumerable: true, get: function () { return noMalformedMacroCalls_1.noMalformedMacroCalls; } });
|
|
16
18
|
var noNestedCommentsInHeader_1 = require("./noNestedCommentsInHeader");
|
|
17
19
|
Object.defineProperty(exports, "noNestedCommentsInHeader", { enumerable: true, get: function () { return noNestedCommentsInHeader_1.noNestedCommentsInHeader; } });
|
|
18
20
|
var noNestedMacros_1 = require("./noNestedMacros");
|
package/rules/file/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/rules/file/index.ts"],"names":[],"mappings":";;;AAAA,uDAAqD;AAA5C,oHAAA,gBAAgB,OAAA;AACzB,2DAAyD;AAAhD,wHAAA,kBAAkB,OAAA;AAC3B,6DAA2D;AAAlD,0HAAA,mBAAmB,OAAA;AAC5B,6CAA2C;AAAlC,0GAAA,WAAW,OAAA;AACpB,2DAAyD;AAAhD,wHAAA,kBAAkB,OAAA;AAC3B,6DAA2D;AAAlD,0HAAA,mBAAmB,OAAA;AAC5B,uEAAqE;AAA5D,oIAAA,wBAAwB,OAAA;AACjC,mDAAiD;AAAxC,gHAAA,cAAc,OAAA;AACvB,mCAAiC;AAAxB,gGAAA,MAAM,OAAA;AACf,2DAAyD;AAAhD,wHAAA,kBAAkB,OAAA;AAC3B,mDAAiD;AAAxC,gHAAA,cAAc,OAAA;AACvB,uDAAqD;AAA5C,oHAAA,gBAAgB,OAAA;AACzB,iEAA+D;AAAtD,8HAAA,qBAAqB,OAAA;AAC9B,qEAAmE;AAA1D,kIAAA,uBAAuB,OAAA;AAChC,uEAAqE;AAA5D,oIAAA,wBAAwB,OAAA;AACjC,6DAA2D;AAAlD,0HAAA,mBAAmB,OAAA"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/rules/file/index.ts"],"names":[],"mappings":";;;AAAA,uDAAqD;AAA5C,oHAAA,gBAAgB,OAAA;AACzB,2DAAyD;AAAhD,wHAAA,kBAAkB,OAAA;AAC3B,6DAA2D;AAAlD,0HAAA,mBAAmB,OAAA;AAC5B,6CAA2C;AAAlC,0GAAA,WAAW,OAAA;AACpB,2DAAyD;AAAhD,wHAAA,kBAAkB,OAAA;AAC3B,6DAA2D;AAAlD,0HAAA,mBAAmB,OAAA;AAC5B,iEAA+D;AAAtD,8HAAA,qBAAqB,OAAA;AAC9B,uEAAqE;AAA5D,oIAAA,wBAAwB,OAAA;AACjC,mDAAiD;AAAxC,gHAAA,cAAc,OAAA;AACvB,mCAAiC;AAAxB,gGAAA,MAAM,OAAA;AACf,2DAAyD;AAAhD,wHAAA,kBAAkB,OAAA;AAC3B,mDAAiD;AAAxC,gHAAA,cAAc,OAAA;AACvB,uDAAqD;AAA5C,oHAAA,gBAAgB,OAAA;AACzB,iEAA+D;AAAtD,8HAAA,qBAAqB,OAAA;AAC9B,qEAAmE;AAA1D,kIAAA,uBAAuB,OAAA;AAChC,uEAAqE;AAA5D,oIAAA,wBAAwB,OAAA;AACjC,6DAA2D;AAAlD,0HAAA,mBAAmB,OAAA"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { FileLintRule } from '../../types/LintRule';
|
|
2
|
+
/**
|
|
3
|
+
* Lint rule that reports a macro call whose argument list does not parse as
|
|
4
|
+
* written. Off by default: the checks model the macro tokenizer, so a project
|
|
5
|
+
* turns them on once it has read the corpus.
|
|
6
|
+
*/
|
|
7
|
+
export declare const noMalformedMacroCalls: FileLintRule;
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.noMalformedMacroCalls = void 0;
|
|
4
|
+
const LintRuleType_1 = require("../../types/LintRuleType");
|
|
5
|
+
const Severity_1 = require("../../types/Severity");
|
|
6
|
+
const getIndex_1 = require("../../utils/getIndex");
|
|
7
|
+
const getPosition_1 = require("../../utils/getPosition");
|
|
8
|
+
const getSeverity_1 = require("../../utils/getSeverity");
|
|
9
|
+
const sasMacros_1 = require("../../utils/sasMacros");
|
|
10
|
+
const parseMacroCallArguments_1 = require("../../utils/parseMacroCallArguments");
|
|
11
|
+
const parseMacroCalls_1 = require("../../utils/parseMacroCalls");
|
|
12
|
+
const name = 'noMalformedMacroCalls';
|
|
13
|
+
const message = 'Macro call arguments: {reason}';
|
|
14
|
+
const description = 'Reports macro calls whose argument list does not parse as written: a call that never closes, or text spilled after a keyword parameter.';
|
|
15
|
+
/** The words for each way a call can fail, and what the author can do about it. */
|
|
16
|
+
const reasons = {
|
|
17
|
+
quote: 'unclosed quote - the rest of the file is read as part of the value; quote it with %str()',
|
|
18
|
+
parenthesis: 'unclosed parenthesis - the call never closes; quote the value with %str()',
|
|
19
|
+
comment: 'unterminated comment - the rest of the call is read as a comment',
|
|
20
|
+
spill: '"%s" is not a name=value parameter and follows one - a comma inside a value? quote it with %str()',
|
|
21
|
+
empty: 'an empty parameter follows a comma - remove it; the engine reads it as a positional parameter',
|
|
22
|
+
quotedName: 'a parameter name cannot be quoted - the engine answers "Invalid macro parameter name"; drop the quotes',
|
|
23
|
+
maskedName: 'a parameter name cannot be masked - the engine reads it as a positional parameter; drop the %str() and name the parameter directly'
|
|
24
|
+
};
|
|
25
|
+
/** A parameter that opens with a quoted name, `"b"=2`. */
|
|
26
|
+
const quotedParameterName = /^\s*(['"])[^'"]*\1\s*=/;
|
|
27
|
+
/**
|
|
28
|
+
* Lint rule that reports a macro call the macro tokenizer cannot read as the
|
|
29
|
+
* author wrote it.
|
|
30
|
+
*
|
|
31
|
+
* The two shapes it reports are the ones an engine confirmed (SAS 9.4,
|
|
32
|
+
* `sas.4gl.io`, 2026-11-11):
|
|
33
|
+
*
|
|
34
|
+
* - the argument region does not close - an unclosed quote, parenthesis or
|
|
35
|
+
* comment - which either swallows the rest of the file, or raises
|
|
36
|
+
* `Macro parameter contains syntax error` somewhere further down;
|
|
37
|
+
* - a parameter after the first `name=` one that carries no `name=`, which is
|
|
38
|
+
* text spilled at a comma or a stray argument. The engine answers
|
|
39
|
+
* `All positional parameters must precede keyword parameters`.
|
|
40
|
+
*
|
|
41
|
+
* A call that closes early (`%foo(a=1), b=2)`) is NOT reported: the closing
|
|
42
|
+
* parenthesis belongs to the call, the leftover text is an ordinary statement
|
|
43
|
+
* error, and a nested call used as a value (`%foo(a=%bar(x, y), b=2)`) has the
|
|
44
|
+
* same shape, so the check cannot tell them apart.
|
|
45
|
+
*/
|
|
46
|
+
const test = (value, config) => {
|
|
47
|
+
const diagnostics = [];
|
|
48
|
+
const severity = (0, getSeverity_1.getSeverity)(config, name, Severity_1.Severity.Warning);
|
|
49
|
+
const at = (start, end, reason) => {
|
|
50
|
+
const from = (0, getPosition_1.getPosition)(value, start);
|
|
51
|
+
const to = (0, getPosition_1.getPosition)(value, end);
|
|
52
|
+
return {
|
|
53
|
+
message: message.replace('{reason}', reason),
|
|
54
|
+
lineNumber: from.lineNumber,
|
|
55
|
+
startColumnNumber: from.columnNumber,
|
|
56
|
+
endColumnNumber: to.columnNumber,
|
|
57
|
+
severity
|
|
58
|
+
};
|
|
59
|
+
};
|
|
60
|
+
(0, parseMacroCalls_1.parseMacroCalls)(value, config)
|
|
61
|
+
// A name inside a single-quoted string is data that a caller may submit
|
|
62
|
+
// later, and a SAS-shipped name is a function or a statement rather than a
|
|
63
|
+
// call this rule can reason about.
|
|
64
|
+
.filter((call) => !call.quoted && !(0, sasMacros_1.isSasMacro)(call.name))
|
|
65
|
+
.forEach((call) => {
|
|
66
|
+
const args = (0, parseMacroCallArguments_1.parseMacroCallArguments)(value, call);
|
|
67
|
+
if (!args)
|
|
68
|
+
return;
|
|
69
|
+
// The call starts at its `%`, which the scan reported as a position:
|
|
70
|
+
// measuring from the parenthesis would be wrong whenever the call puts
|
|
71
|
+
// whitespace between the name and the argument list.
|
|
72
|
+
const callStart = (0, getIndex_1.getIndex)(value, call.lineNumber, call.startColumnNumber);
|
|
73
|
+
if (args.unclosed) {
|
|
74
|
+
diagnostics.push(at(callStart, callStart + call.name.length + 1, reasons[args.unclosed]));
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
const firstKeyword = args.parameters.findIndex((parameter) => parameter.isKeyword);
|
|
78
|
+
if (firstKeyword === -1)
|
|
79
|
+
return;
|
|
80
|
+
const stray = args.parameters
|
|
81
|
+
.slice(firstKeyword + 1)
|
|
82
|
+
.find((parameter) => !parameter.isKeyword);
|
|
83
|
+
if (stray) {
|
|
84
|
+
// A parameter holding nothing but whitespace or a comment is an empty
|
|
85
|
+
// one: the engine reads `%foo(a=1, b=2,)` and `%foo(a=1, b=2, /*x*/)`
|
|
86
|
+
// as a positional parameter and refuses both.
|
|
87
|
+
const visible = stray.text
|
|
88
|
+
.replace(/\/\*[\s\S]*?\*\//g, ' ')
|
|
89
|
+
.trim()
|
|
90
|
+
.replace(/\s+/g, ' ');
|
|
91
|
+
// Every span here stays within the parameter's own text, trimmed of the
|
|
92
|
+
// whitespace around it: a parameter may run past a newline, and a range
|
|
93
|
+
// measured to `stray.end` would then name a line it does not cover.
|
|
94
|
+
const textStart = stray.start + (stray.text.length - stray.text.trimStart().length);
|
|
95
|
+
const textEnd = stray.end - (stray.text.length - stray.text.trimEnd().length);
|
|
96
|
+
if ((0, parseMacroCallArguments_1.hasMaskedName)(stray.text)) {
|
|
97
|
+
diagnostics.push(at(textStart, textEnd, reasons.maskedName));
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
if (quotedParameterName.test(stray.text)) {
|
|
101
|
+
diagnostics.push(at(textStart, textEnd, reasons.quotedName));
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
if (!visible) {
|
|
105
|
+
// Point at the comma that introduced the parameter - one character,
|
|
106
|
+
// so the range cannot straddle a line break.
|
|
107
|
+
diagnostics.push(at(Math.max(stray.start - 1, 0), stray.start, reasons.empty));
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
diagnostics.push(at(textStart, textEnd, reasons.spill.replace('%s', visible)));
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
return diagnostics;
|
|
114
|
+
};
|
|
115
|
+
/**
|
|
116
|
+
* Lint rule that reports a macro call whose argument list does not parse as
|
|
117
|
+
* written. Off by default: the checks model the macro tokenizer, so a project
|
|
118
|
+
* turns them on once it has read the corpus.
|
|
119
|
+
*/
|
|
120
|
+
exports.noMalformedMacroCalls = {
|
|
121
|
+
type: LintRuleType_1.LintRuleType.File,
|
|
122
|
+
name,
|
|
123
|
+
description,
|
|
124
|
+
message,
|
|
125
|
+
test
|
|
126
|
+
};
|
|
127
|
+
//# sourceMappingURL=noMalformedMacroCalls.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"noMalformedMacroCalls.js","sourceRoot":"","sources":["../../../src/rules/file/noMalformedMacroCalls.ts"],"names":[],"mappings":"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"}
|
package/sasjslint-schema.json
CHANGED
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
"noEncodedPasswords": true,
|
|
28
28
|
"noGremlins": true,
|
|
29
29
|
"noMacroIfInOpenCode": false,
|
|
30
|
+
"noMalformedMacroCalls": false,
|
|
30
31
|
"noNestedCommentsInHeader": true,
|
|
31
32
|
"noNestedMacros": true,
|
|
32
33
|
"noSingleAsteriskComments": false,
|
|
@@ -54,6 +55,7 @@
|
|
|
54
55
|
"maxDataLineLength": 80,
|
|
55
56
|
"noGremlins": true,
|
|
56
57
|
"noMacroIfInOpenCode": false,
|
|
58
|
+
"noMalformedMacroCalls": false,
|
|
57
59
|
"allowedGremlins": ["0x0080", "0x3000"],
|
|
58
60
|
"noTabs": true,
|
|
59
61
|
"indentationMultiple": 4,
|
|
@@ -122,6 +124,14 @@
|
|
|
122
124
|
"default": false,
|
|
123
125
|
"examples": [true, false]
|
|
124
126
|
},
|
|
127
|
+
"noMalformedMacroCalls": {
|
|
128
|
+
"$id": "#/properties/noMalformedMacroCalls",
|
|
129
|
+
"type": "boolean",
|
|
130
|
+
"title": "noMalformedMacroCalls",
|
|
131
|
+
"description": "Reports a macro call whose argument list does not parse as written: a call that never closes (an unclosed quote, parenthesis or comment), or a parameter after the first name= one that carries no name= - text spilled at a comma inside a value. Quoting the value with %str() repairs both. Off by default.",
|
|
132
|
+
"default": false,
|
|
133
|
+
"examples": [true, false]
|
|
134
|
+
},
|
|
125
135
|
"bannedStrings": {
|
|
126
136
|
"$id": "#/properties/bannedStrings",
|
|
127
137
|
"type": "array",
|
|
@@ -565,6 +575,13 @@
|
|
|
565
575
|
"type": "string",
|
|
566
576
|
"enum": ["error", "warn"],
|
|
567
577
|
"default": "warn"
|
|
578
|
+
},
|
|
579
|
+
"noMalformedMacroCalls": {
|
|
580
|
+
"$id": "#/properties/severityLevel/noMalformedMacroCalls",
|
|
581
|
+
"title": "noMalformedMacroCalls",
|
|
582
|
+
"type": "string",
|
|
583
|
+
"enum": ["error", "warn"],
|
|
584
|
+
"default": "warn"
|
|
568
585
|
}
|
|
569
586
|
}
|
|
570
587
|
}
|
package/types/LintConfig.js
CHANGED
|
@@ -142,6 +142,11 @@ class LintConfig {
|
|
|
142
142
|
if ((json === null || json === void 0 ? void 0 : json.hasMacroNameInMend) !== false) {
|
|
143
143
|
this.fileLintRules.push(file_1.hasMacroNameInMend);
|
|
144
144
|
}
|
|
145
|
+
// Off by default: the checks model the macro tokenizer, so a project turns
|
|
146
|
+
// them on after reading them against its own corpus.
|
|
147
|
+
if (json === null || json === void 0 ? void 0 : json.noMalformedMacroCalls) {
|
|
148
|
+
this.fileLintRules.push(file_1.noMalformedMacroCalls);
|
|
149
|
+
}
|
|
145
150
|
if ((json === null || json === void 0 ? void 0 : json.noNestedCommentsInHeader) !== false) {
|
|
146
151
|
this.fileLintRules.push(file_1.noNestedCommentsInHeader);
|
|
147
152
|
}
|
package/types/LintConfig.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LintConfig.js","sourceRoot":"","sources":["../../src/types/LintConfig.ts"],"names":[],"mappings":";;;AAAA,
|
|
1
|
+
{"version":3,"file":"LintConfig.js","sourceRoot":"","sources":["../../src/types/LintConfig.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reports the character index of a one-based line and column.
|
|
3
|
+
*
|
|
4
|
+
* The inverse of `getPosition`, and for the same reason: the lines come from
|
|
5
|
+
* `splitLines`, so a CRLF file yields the index a `\n`-only reader would miss
|
|
6
|
+
* by one per preceding line.
|
|
7
|
+
*
|
|
8
|
+
* @param {string} text - the text the position belongs to.
|
|
9
|
+
* @param {number} lineNumber - one-based line number.
|
|
10
|
+
* @param {number} columnNumber - one-based column number.
|
|
11
|
+
*/
|
|
12
|
+
export declare const getIndex: (text: string, lineNumber: number, columnNumber: number) => number;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getIndex = void 0;
|
|
4
|
+
const splitLines_1 = require("./splitLines");
|
|
5
|
+
/**
|
|
6
|
+
* Reports the character index of a one-based line and column.
|
|
7
|
+
*
|
|
8
|
+
* The inverse of `getPosition`, and for the same reason: the lines come from
|
|
9
|
+
* `splitLines`, so a CRLF file yields the index a `\n`-only reader would miss
|
|
10
|
+
* by one per preceding line.
|
|
11
|
+
*
|
|
12
|
+
* @param {string} text - the text the position belongs to.
|
|
13
|
+
* @param {number} lineNumber - one-based line number.
|
|
14
|
+
* @param {number} columnNumber - one-based column number.
|
|
15
|
+
*/
|
|
16
|
+
const getIndex = (text, lineNumber, columnNumber) => {
|
|
17
|
+
if (lineNumber <= 1)
|
|
18
|
+
return Math.max(columnNumber - 1, 0);
|
|
19
|
+
const lines = (0, splitLines_1.splitLines)(text);
|
|
20
|
+
let index = 0;
|
|
21
|
+
for (let line = 1; line < lineNumber && line <= lines.length; line++) {
|
|
22
|
+
// The terminator is not part of the line, so it is measured here: one
|
|
23
|
+
// character for a LF file, two for a CRLF one.
|
|
24
|
+
const terminator = text.startsWith('\r\n', index + lines[line - 1].length)
|
|
25
|
+
? 2
|
|
26
|
+
: 1;
|
|
27
|
+
index += lines[line - 1].length + terminator;
|
|
28
|
+
}
|
|
29
|
+
return Math.min(index + Math.max(columnNumber - 1, 0), text.length);
|
|
30
|
+
};
|
|
31
|
+
exports.getIndex = getIndex;
|
|
32
|
+
//# sourceMappingURL=getIndex.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getIndex.js","sourceRoot":"","sources":["../../src/utils/getIndex.ts"],"names":[],"mappings":";;;AAAA,6CAAyC;AAEzC;;;;;;;;;;GAUG;AACI,MAAM,QAAQ,GAAG,CACtB,IAAY,EACZ,UAAkB,EAClB,YAAoB,EACZ,EAAE;IACV,IAAI,UAAU,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC,GAAG,CAAC,YAAY,GAAG,CAAC,EAAE,CAAC,CAAC,CAAA;IAEzD,MAAM,KAAK,GAAG,IAAA,uBAAU,EAAC,IAAI,CAAC,CAAA;IAC9B,IAAI,KAAK,GAAG,CAAC,CAAA;IAEb,KAAK,IAAI,IAAI,GAAG,CAAC,EAAE,IAAI,GAAG,UAAU,IAAI,IAAI,IAAI,KAAK,CAAC,MAAM,EAAE,IAAI,EAAE,EAAE,CAAC;QACrE,sEAAsE;QACtE,+CAA+C;QAC/C,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,KAAK,GAAG,KAAK,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC;YACxE,CAAC,CAAC,CAAC;YACH,CAAC,CAAC,CAAC,CAAA;QACL,KAAK,IAAI,KAAK,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,MAAM,GAAG,UAAU,CAAA;IAC9C,CAAC;IAED,OAAO,IAAI,CAAC,GAAG,CAAC,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,YAAY,GAAG,CAAC,EAAE,CAAC,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC,CAAA;AACrE,CAAC,CAAA;AApBY,QAAA,QAAQ,YAoBpB"}
|
package/utils/getLintConfig.d.ts
CHANGED
|
@@ -32,6 +32,7 @@ export declare const DefaultLintConfiguration: {
|
|
|
32
32
|
noEncodedPasswords: boolean;
|
|
33
33
|
noGremlins: boolean;
|
|
34
34
|
noMacroIfInOpenCode: boolean;
|
|
35
|
+
noMalformedMacroCalls: boolean;
|
|
35
36
|
noNestedCommentsInHeader: boolean;
|
|
36
37
|
noNestedMacros: boolean;
|
|
37
38
|
noSingleAsteriskComments: boolean;
|
|
@@ -49,6 +50,18 @@ export declare const DefaultLintConfiguration: {
|
|
|
49
50
|
/**
|
|
50
51
|
* Fetches the config from the .sasjslint file (at project root or home directory) and creates a LintConfig object.
|
|
51
52
|
* Returns the default configuration when a .sasjslint file is unavailable.
|
|
53
|
+
*
|
|
54
|
+
* An absent file means the defaults, but a file that IS there and does not parse
|
|
55
|
+
* is a mistake in the project rather than a missing configuration: reporting it
|
|
56
|
+
* as "no file" applies a different rule set without saying so, which is how a
|
|
57
|
+
* commented-out rule or a stray trailing comma comes to look like it works.
|
|
52
58
|
* @returns {Promise<LintConfig>} resolves with an object representing the current lint configuration.
|
|
53
59
|
*/
|
|
54
60
|
export declare function getLintConfig(): Promise<LintConfig>;
|
|
61
|
+
/**
|
|
62
|
+
* Reads the configuration file, turning a parse failure into a message that
|
|
63
|
+
* names the file and says what to do about it. A raw `JSON.parse` error reads
|
|
64
|
+
* `Unexpected token } in JSON at position 12`, which leaves the reader to work
|
|
65
|
+
* out which file was read and why JSON is involved at all.
|
|
66
|
+
*/
|
|
67
|
+
export declare const parseLintConfig: (configuration: string, filePath: string) => any;
|
package/utils/getLintConfig.js
CHANGED
|
@@ -12,7 +12,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
12
12
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
13
|
};
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
-
exports.DefaultLintConfiguration = exports.getDefaultHeader = void 0;
|
|
15
|
+
exports.parseLintConfig = exports.DefaultLintConfiguration = exports.getDefaultHeader = void 0;
|
|
16
16
|
exports.getLintConfig = getLintConfig;
|
|
17
17
|
const path_1 = __importDefault(require("path"));
|
|
18
18
|
const os_1 = __importDefault(require("os"));
|
|
@@ -55,6 +55,7 @@ exports.DefaultLintConfiguration = {
|
|
|
55
55
|
noEncodedPasswords: true,
|
|
56
56
|
noGremlins: true,
|
|
57
57
|
noMacroIfInOpenCode: false,
|
|
58
|
+
noMalformedMacroCalls: false,
|
|
58
59
|
noNestedCommentsInHeader: true,
|
|
59
60
|
noNestedMacros: true,
|
|
60
61
|
noSingleAsteriskComments: false,
|
|
@@ -72,16 +73,42 @@ exports.DefaultLintConfiguration = {
|
|
|
72
73
|
/**
|
|
73
74
|
* Fetches the config from the .sasjslint file (at project root or home directory) and creates a LintConfig object.
|
|
74
75
|
* Returns the default configuration when a .sasjslint file is unavailable.
|
|
76
|
+
*
|
|
77
|
+
* An absent file means the defaults, but a file that IS there and does not parse
|
|
78
|
+
* is a mistake in the project rather than a missing configuration: reporting it
|
|
79
|
+
* as "no file" applies a different rule set without saying so, which is how a
|
|
80
|
+
* commented-out rule or a stray trailing comma comes to look like it works.
|
|
75
81
|
* @returns {Promise<LintConfig>} resolves with an object representing the current lint configuration.
|
|
76
82
|
*/
|
|
77
83
|
function getLintConfig() {
|
|
78
84
|
return __awaiter(this, void 0, void 0, function* () {
|
|
79
85
|
const projectRoot = yield (0, getProjectRoot_1.getProjectRoot)();
|
|
80
86
|
const lintFileLocation = projectRoot || os_1.default.homedir();
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
87
|
+
const lintFilePath = path_1.default.join(lintFileLocation, '.sasjslint');
|
|
88
|
+
const configuration = yield (0, file_1.readFile)(lintFilePath).catch((_) => null);
|
|
89
|
+
if (configuration === null) {
|
|
90
|
+
return new LintConfig_1.LintConfig(exports.DefaultLintConfiguration);
|
|
91
|
+
}
|
|
92
|
+
return new LintConfig_1.LintConfig((0, exports.parseLintConfig)(configuration, lintFilePath));
|
|
85
93
|
});
|
|
86
94
|
}
|
|
95
|
+
/**
|
|
96
|
+
* Reads the configuration file, turning a parse failure into a message that
|
|
97
|
+
* names the file and says what to do about it. A raw `JSON.parse` error reads
|
|
98
|
+
* `Unexpected token } in JSON at position 12`, which leaves the reader to work
|
|
99
|
+
* out which file was read and why JSON is involved at all.
|
|
100
|
+
*/
|
|
101
|
+
const parseLintConfig = (configuration, filePath) => {
|
|
102
|
+
try {
|
|
103
|
+
return JSON.parse(configuration);
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
107
|
+
throw new Error(`${filePath} is not valid JSON (${reason}).\n` +
|
|
108
|
+
'The file is read as strict JSON, so it cannot carry comments. ' +
|
|
109
|
+
'Every setting, its type and its default are documented in the schema: ' +
|
|
110
|
+
'https://cli.sasjs.io/sasjslint-schema.json');
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
exports.parseLintConfig = parseLintConfig;
|
|
87
114
|
//# sourceMappingURL=getLintConfig.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getLintConfig.js","sourceRoot":"","sources":["../../src/utils/getLintConfig.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"getLintConfig.js","sourceRoot":"","sources":["../../src/utils/getLintConfig.ts"],"names":[],"mappings":"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"}
|
package/utils/index.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ export * from './getPosition';
|
|
|
9
9
|
export * from './splitLines';
|
|
10
10
|
export * from './getIndicesOf';
|
|
11
11
|
export * from './getHeaderLinesCount';
|
|
12
|
+
export * from './getIndex';
|
|
12
13
|
export * from './isDoxygenHeader';
|
|
13
14
|
export * from './getDataSectionsDetail';
|
|
14
15
|
export * from './defaultBannedStrings';
|
|
@@ -19,5 +20,6 @@ export * from './headerMacros';
|
|
|
19
20
|
export * from './mergeLintConfig';
|
|
20
21
|
export * from './parseLibnames';
|
|
21
22
|
export * from './parseLintOverride';
|
|
23
|
+
export * from './parseMacroCallArguments';
|
|
22
24
|
export * from './parseMacroCalls';
|
|
23
25
|
export * from './sasMacros';
|
package/utils/index.js
CHANGED
|
@@ -25,6 +25,7 @@ __exportStar(require("./getPosition"), exports);
|
|
|
25
25
|
__exportStar(require("./splitLines"), exports);
|
|
26
26
|
__exportStar(require("./getIndicesOf"), exports);
|
|
27
27
|
__exportStar(require("./getHeaderLinesCount"), exports);
|
|
28
|
+
__exportStar(require("./getIndex"), exports);
|
|
28
29
|
__exportStar(require("./isDoxygenHeader"), exports);
|
|
29
30
|
__exportStar(require("./getDataSectionsDetail"), exports);
|
|
30
31
|
__exportStar(require("./defaultBannedStrings"), exports);
|
|
@@ -35,6 +36,7 @@ __exportStar(require("./headerMacros"), exports);
|
|
|
35
36
|
__exportStar(require("./mergeLintConfig"), exports);
|
|
36
37
|
__exportStar(require("./parseLibnames"), exports);
|
|
37
38
|
__exportStar(require("./parseLintOverride"), exports);
|
|
39
|
+
__exportStar(require("./parseMacroCallArguments"), exports);
|
|
38
40
|
__exportStar(require("./parseMacroCalls"), exports);
|
|
39
41
|
__exportStar(require("./sasMacros"), exports);
|
|
40
42
|
//# sourceMappingURL=index.js.map
|
package/utils/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,iDAA8B;AAC9B,kDAA+B;AAC/B,mDAAgC;AAChC,sDAAmC;AACnC,8CAA2B;AAC3B,iDAA8B;AAC9B,8CAA2B;AAC3B,gDAA6B;AAC7B,+CAA4B;AAC5B,iDAA8B;AAC9B,wDAAqC;AACrC,oDAAiC;AACjC,0DAAuC;AACvC,yDAAsC;AACtC,yDAAsC;AACtC,iDAA8B;AAC9B,gDAA6B;AAC7B,iDAA8B;AAC9B,oDAAiC;AACjC,kDAA+B;AAC/B,sDAAmC;AACnC,oDAAiC;AACjC,8CAA2B"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,iDAA8B;AAC9B,kDAA+B;AAC/B,mDAAgC;AAChC,sDAAmC;AACnC,8CAA2B;AAC3B,iDAA8B;AAC9B,8CAA2B;AAC3B,gDAA6B;AAC7B,+CAA4B;AAC5B,iDAA8B;AAC9B,wDAAqC;AACrC,6CAA0B;AAC1B,oDAAiC;AACjC,0DAAuC;AACvC,yDAAsC;AACtC,yDAAsC;AACtC,iDAA8B;AAC9B,gDAA6B;AAC7B,iDAA8B;AAC9B,oDAAiC;AACjC,kDAA+B;AAC/B,sDAAmC;AACnC,4DAAyC;AACzC,oDAAiC;AACjC,8CAA2B"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { MacroCall } from './parseMacroCalls';
|
|
2
|
+
/**
|
|
3
|
+
* Why a macro call does not close: the scan ran to the end of the file while
|
|
4
|
+
* inside a quoted string, inside parentheses, or inside a comment.
|
|
5
|
+
*/
|
|
6
|
+
export type MacroCallUnclosed = 'quote' | 'parenthesis' | 'comment';
|
|
7
|
+
/** One comma-separated parameter of a macro call, with its span in the file. */
|
|
8
|
+
export interface MacroCallParameter {
|
|
9
|
+
/** The parameter text, exactly as written. */
|
|
10
|
+
text: string;
|
|
11
|
+
/** True when the parameter opens with `name=`. */
|
|
12
|
+
isKeyword: boolean;
|
|
13
|
+
/** Index of the parameter's first character. */
|
|
14
|
+
start: number;
|
|
15
|
+
/** Index one past the parameter's last character. */
|
|
16
|
+
end: number;
|
|
17
|
+
}
|
|
18
|
+
/** The argument region of one macro call. */
|
|
19
|
+
export interface MacroCallArguments {
|
|
20
|
+
/** Index of the opening parenthesis. */
|
|
21
|
+
openParen: number;
|
|
22
|
+
/** Index of the closing parenthesis, or -1 when the call does not close. */
|
|
23
|
+
closeParen: number;
|
|
24
|
+
/** What the scan was in when it ran out of file, or null when it closed. */
|
|
25
|
+
unclosed: MacroCallUnclosed | null;
|
|
26
|
+
/** The parameters, in order, including empty ones. */
|
|
27
|
+
parameters: MacroCallParameter[];
|
|
28
|
+
/** The text between the closing parenthesis and the end of its line. */
|
|
29
|
+
afterClose: string;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* True when a parameter opens with a masking function whose argument is
|
|
33
|
+
* followed by `=`, as in `%str(b)=2`.
|
|
34
|
+
*
|
|
35
|
+
* That is not a keyword parameter: the engine reads the whole thing as
|
|
36
|
+
* positional text and refuses the call, which is why the rule reports it and
|
|
37
|
+
* tells the reader to drop the mask rather than add one.
|
|
38
|
+
*
|
|
39
|
+
* @param {string} parameter - one parameter's text, as written.
|
|
40
|
+
*/
|
|
41
|
+
export declare const hasMaskedName: (parameter: string) => boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Reads the argument region of a macro call: where it starts and ends, the
|
|
44
|
+
* parameters it contains, and whether it closes at all.
|
|
45
|
+
*
|
|
46
|
+
* The scan follows what the macro tokenizer does - it tracks single and double
|
|
47
|
+
* quotes, parenthesis depth and block comments, and skips the argument of a
|
|
48
|
+
* masking function - because that is what decides where the call ends. A
|
|
49
|
+
* parameter is only split at a comma that sits at depth zero, outside a quote
|
|
50
|
+
* and outside a mask, which is the same rule: `%foo(a="x, y")` and
|
|
51
|
+
* `%foo(a=%str(x, y))` are one parameter each.
|
|
52
|
+
*
|
|
53
|
+
* @param {string} text - the whole file.
|
|
54
|
+
* @param {MacroCall} call - an invocation reported by `parseMacroCalls`.
|
|
55
|
+
* @returns {MacroCallArguments | null} null when the call carries no argument
|
|
56
|
+
* list, so there is nothing to read.
|
|
57
|
+
*/
|
|
58
|
+
export declare const parseMacroCallArguments: (text: string, call: MacroCall) => MacroCallArguments | null;
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseMacroCallArguments = exports.hasMaskedName = void 0;
|
|
4
|
+
const getIndex_1 = require("./getIndex");
|
|
5
|
+
/**
|
|
6
|
+
* The functions whose argument is text rather than code. A comma or a quote
|
|
7
|
+
* inside one belongs to the value, so the scan steps over the whole argument
|
|
8
|
+
* instead of reading it.
|
|
9
|
+
*/
|
|
10
|
+
const maskingFunction = /%(nrstr|str|bquote|quote|superq|qsysfunc|qcmpres|qleft|qlowcase|qscan|qsubstr|qtrim|qupcase|qverify)\s*\(/iy;
|
|
11
|
+
/**
|
|
12
|
+
* A parameter that opens with `name=`, which is what makes it a keyword one.
|
|
13
|
+
*
|
|
14
|
+
* The name may arrive through a macro trigger: the macro processor resolves
|
|
15
|
+
* `&k` before the parameter list is parsed, so `%foo(a=1, &k=2)` with
|
|
16
|
+
* `%let k=b;` passes `b` as a keyword parameter (SAS 9.4, syscc 0).
|
|
17
|
+
*/
|
|
18
|
+
const keywordParameter = /^\s*(?:&+[a-z_][a-z0-9_.]*|[a-z_][a-z0-9_]*)\s*=/i;
|
|
19
|
+
/**
|
|
20
|
+
* A comment at the start of a parameter, which the engine ignores: in
|
|
21
|
+
* `%foo(a=1, [comment] b=2)` the parameter still opens with `b=`, so it is a
|
|
22
|
+
* keyword one.
|
|
23
|
+
*/
|
|
24
|
+
const leadingComment = /^(?:\s*\/\*[\s\S]*?\*\/)*\s*/;
|
|
25
|
+
/** Index one past the `)` that closes a masking function's argument. */
|
|
26
|
+
const skipMasked = (text, openParen) => {
|
|
27
|
+
let depth = 1;
|
|
28
|
+
let index = openParen + 1;
|
|
29
|
+
while (index < text.length && depth) {
|
|
30
|
+
// `%(` and `%)` are literal parenthesis inside a masked argument.
|
|
31
|
+
if (text[index] === '%' &&
|
|
32
|
+
(text[index + 1] === '(' || text[index + 1] === ')')) {
|
|
33
|
+
index += 2;
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
if (text[index] === '(')
|
|
37
|
+
depth++;
|
|
38
|
+
if (text[index] === ')')
|
|
39
|
+
depth--;
|
|
40
|
+
index++;
|
|
41
|
+
}
|
|
42
|
+
return index;
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* True when a parameter opens with a masking function whose argument is
|
|
46
|
+
* followed by `=`, as in `%str(b)=2`.
|
|
47
|
+
*
|
|
48
|
+
* That is not a keyword parameter: the engine reads the whole thing as
|
|
49
|
+
* positional text and refuses the call, which is why the rule reports it and
|
|
50
|
+
* tells the reader to drop the mask rather than add one.
|
|
51
|
+
*
|
|
52
|
+
* @param {string} parameter - one parameter's text, as written.
|
|
53
|
+
*/
|
|
54
|
+
const hasMaskedName = (parameter) => {
|
|
55
|
+
const start = parameter.length - parameter.trimStart().length;
|
|
56
|
+
maskingFunction.lastIndex = start;
|
|
57
|
+
if (!maskingFunction.test(parameter))
|
|
58
|
+
return false;
|
|
59
|
+
const end = skipMasked(parameter, maskingFunction.lastIndex - 1);
|
|
60
|
+
return /^\s*=/.test(parameter.slice(end));
|
|
61
|
+
};
|
|
62
|
+
exports.hasMaskedName = hasMaskedName;
|
|
63
|
+
/**
|
|
64
|
+
* Reads the argument region of a macro call: where it starts and ends, the
|
|
65
|
+
* parameters it contains, and whether it closes at all.
|
|
66
|
+
*
|
|
67
|
+
* The scan follows what the macro tokenizer does - it tracks single and double
|
|
68
|
+
* quotes, parenthesis depth and block comments, and skips the argument of a
|
|
69
|
+
* masking function - because that is what decides where the call ends. A
|
|
70
|
+
* parameter is only split at a comma that sits at depth zero, outside a quote
|
|
71
|
+
* and outside a mask, which is the same rule: `%foo(a="x, y")` and
|
|
72
|
+
* `%foo(a=%str(x, y))` are one parameter each.
|
|
73
|
+
*
|
|
74
|
+
* @param {string} text - the whole file.
|
|
75
|
+
* @param {MacroCall} call - an invocation reported by `parseMacroCalls`.
|
|
76
|
+
* @returns {MacroCallArguments | null} null when the call carries no argument
|
|
77
|
+
* list, so there is nothing to read.
|
|
78
|
+
*/
|
|
79
|
+
const parseMacroCallArguments = (text, call) => {
|
|
80
|
+
if (!text)
|
|
81
|
+
return null;
|
|
82
|
+
// `endColumnNumber` is the column one past the name, so the parenthesis is
|
|
83
|
+
// the first `(` after it. Whitespace between the two is allowed, and a call
|
|
84
|
+
// may put the parenthesis on the next line.
|
|
85
|
+
let openParen = (0, getIndex_1.getIndex)(text, call.lineNumber, call.endColumnNumber);
|
|
86
|
+
while (openParen < text.length && /\s/.test(text[openParen]))
|
|
87
|
+
openParen++;
|
|
88
|
+
if (text[openParen] !== '(')
|
|
89
|
+
return null;
|
|
90
|
+
const parameters = [];
|
|
91
|
+
let depth = 0;
|
|
92
|
+
let quote = null;
|
|
93
|
+
let inComment = false;
|
|
94
|
+
let parameterStart = openParen + 1;
|
|
95
|
+
let index = openParen + 1;
|
|
96
|
+
const closeParameter = (end) => {
|
|
97
|
+
const parameter = text.slice(parameterStart, end);
|
|
98
|
+
parameters.push({
|
|
99
|
+
text: parameter,
|
|
100
|
+
isKeyword: keywordParameter.test(parameter.replace(leadingComment, '')),
|
|
101
|
+
start: parameterStart,
|
|
102
|
+
end
|
|
103
|
+
});
|
|
104
|
+
};
|
|
105
|
+
while (index < text.length) {
|
|
106
|
+
const char = text[index];
|
|
107
|
+
if (inComment) {
|
|
108
|
+
if (text.startsWith('*/', index)) {
|
|
109
|
+
inComment = false;
|
|
110
|
+
index += 2;
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
index++;
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
if (quote) {
|
|
117
|
+
if (char === quote)
|
|
118
|
+
quote = null;
|
|
119
|
+
index++;
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
maskingFunction.lastIndex = index;
|
|
123
|
+
if (maskingFunction.test(text)) {
|
|
124
|
+
index = skipMasked(text, maskingFunction.lastIndex - 1);
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
if (text.startsWith('/*', index)) {
|
|
128
|
+
inComment = true;
|
|
129
|
+
index += 2;
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
if (char === "'" || char === '"') {
|
|
133
|
+
quote = char;
|
|
134
|
+
index++;
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
if (char === '(') {
|
|
138
|
+
depth++;
|
|
139
|
+
index++;
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
if (char === ')') {
|
|
143
|
+
if (depth) {
|
|
144
|
+
depth--;
|
|
145
|
+
index++;
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
closeParameter(index);
|
|
149
|
+
const lineEnd = text.indexOf('\n', index);
|
|
150
|
+
const afterClose = text.slice(index + 1, lineEnd === -1 ? text.length : lineEnd);
|
|
151
|
+
return {
|
|
152
|
+
openParen,
|
|
153
|
+
closeParen: index,
|
|
154
|
+
unclosed: null,
|
|
155
|
+
parameters,
|
|
156
|
+
afterClose
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
if (char === ',' && !depth) {
|
|
160
|
+
closeParameter(index);
|
|
161
|
+
parameterStart = index + 1;
|
|
162
|
+
index++;
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
index++;
|
|
166
|
+
}
|
|
167
|
+
return {
|
|
168
|
+
openParen,
|
|
169
|
+
closeParen: -1,
|
|
170
|
+
unclosed: inComment ? 'comment' : quote ? 'quote' : 'parenthesis',
|
|
171
|
+
parameters,
|
|
172
|
+
afterClose: ''
|
|
173
|
+
};
|
|
174
|
+
};
|
|
175
|
+
exports.parseMacroCallArguments = parseMacroCallArguments;
|
|
176
|
+
//# sourceMappingURL=parseMacroCallArguments.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parseMacroCallArguments.js","sourceRoot":"","sources":["../../src/utils/parseMacroCallArguments.ts"],"names":[],"mappings":";;;AAAA,yCAAqC;AAmCrC;;;;GAIG;AACH,MAAM,eAAe,GACnB,6GAA6G,CAAA;AAE/G;;;;;;GAMG;AACH,MAAM,gBAAgB,GAAG,mDAAmD,CAAA;AAE5E;;;;GAIG;AACH,MAAM,cAAc,GAAG,8BAA8B,CAAA;AAErD,wEAAwE;AACxE,MAAM,UAAU,GAAG,CAAC,IAAY,EAAE,SAAiB,EAAU,EAAE;IAC7D,IAAI,KAAK,GAAG,CAAC,CAAA;IACb,IAAI,KAAK,GAAG,SAAS,GAAG,CAAC,CAAA;IAEzB,OAAO,KAAK,GAAG,IAAI,CAAC,MAAM,IAAI,KAAK,EAAE,CAAC;QACpC,kEAAkE;QAClE,IACE,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG;YACnB,CAAC,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,GAAG,IAAI,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,GAAG,CAAC,EACpD,CAAC;YACD,KAAK,IAAI,CAAC,CAAA;YACV,SAAQ;QACV,CAAC;QACD,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG;YAAE,KAAK,EAAE,CAAA;QAChC,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG;YAAE,KAAK,EAAE,CAAA;QAChC,KAAK,EAAE,CAAA;IACT,CAAC;IAED,OAAO,KAAK,CAAA;AACd,CAAC,CAAA;AAED;;;;;;;;;GASG;AACI,MAAM,aAAa,GAAG,CAAC,SAAiB,EAAW,EAAE;IAC1D,MAAM,KAAK,GAAG,SAAS,CAAC,MAAM,GAAG,SAAS,CAAC,SAAS,EAAE,CAAC,MAAM,CAAA;IAE7D,eAAe,CAAC,SAAS,GAAG,KAAK,CAAA;IACjC,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,SAAS,CAAC;QAAE,OAAO,KAAK,CAAA;IAElD,MAAM,GAAG,GAAG,UAAU,CAAC,SAAS,EAAE,eAAe,CAAC,SAAS,GAAG,CAAC,CAAC,CAAA;IAEhE,OAAO,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAA;AAC3C,CAAC,CAAA;AATY,QAAA,aAAa,iBASzB;AAED;;;;;;;;;;;;;;;GAeG;AACI,MAAM,uBAAuB,GAAG,CACrC,IAAY,EACZ,IAAe,EACY,EAAE;IAC7B,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAA;IAEtB,2EAA2E;IAC3E,4EAA4E;IAC5E,4CAA4C;IAC5C,IAAI,SAAS,GAAG,IAAA,mBAAQ,EAAC,IAAI,EAAE,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,eAAe,CAAC,CAAA;IAErE,OAAO,SAAS,GAAG,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAAE,SAAS,EAAE,CAAA;IAEzE,IAAI,IAAI,CAAC,SAAS,CAAC,KAAK,GAAG;QAAE,OAAO,IAAI,CAAA;IAExC,MAAM,UAAU,GAAyB,EAAE,CAAA;IAC3C,IAAI,KAAK,GAAG,CAAC,CAAA;IACb,IAAI,KAAK,GAAkB,IAAI,CAAA;IAC/B,IAAI,SAAS,GAAG,KAAK,CAAA;IACrB,IAAI,cAAc,GAAG,SAAS,GAAG,CAAC,CAAA;IAClC,IAAI,KAAK,GAAG,SAAS,GAAG,CAAC,CAAA;IAEzB,MAAM,cAAc,GAAG,CAAC,GAAW,EAAE,EAAE;QACrC,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,cAAc,EAAE,GAAG,CAAC,CAAA;QACjD,UAAU,CAAC,IAAI,CAAC;YACd,IAAI,EAAE,SAAS;YACf,SAAS,EAAE,gBAAgB,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC;YACvE,KAAK,EAAE,cAAc;YACrB,GAAG;SACJ,CAAC,CAAA;IACJ,CAAC,CAAA;IAED,OAAO,KAAK,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;QAC3B,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAA;QAExB,IAAI,SAAS,EAAE,CAAC;YACd,IAAI,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,CAAC;gBACjC,SAAS,GAAG,KAAK,CAAA;gBACjB,KAAK,IAAI,CAAC,CAAA;gBACV,SAAQ;YACV,CAAC;YACD,KAAK,EAAE,CAAA;YACP,SAAQ;QACV,CAAC;QAED,IAAI,KAAK,EAAE,CAAC;YACV,IAAI,IAAI,KAAK,KAAK;gBAAE,KAAK,GAAG,IAAI,CAAA;YAChC,KAAK,EAAE,CAAA;YACP,SAAQ;QACV,CAAC;QAED,eAAe,CAAC,SAAS,GAAG,KAAK,CAAA;QACjC,IAAI,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YAC/B,KAAK,GAAG,UAAU,CAAC,IAAI,EAAE,eAAe,CAAC,SAAS,GAAG,CAAC,CAAC,CAAA;YACvD,SAAQ;QACV,CAAC;QAED,IAAI,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,CAAC;YACjC,SAAS,GAAG,IAAI,CAAA;YAChB,KAAK,IAAI,CAAC,CAAA;YACV,SAAQ;QACV,CAAC;QAED,IAAI,IAAI,KAAK,GAAG,IAAI,IAAI,KAAK,GAAG,EAAE,CAAC;YACjC,KAAK,GAAG,IAAI,CAAA;YACZ,KAAK,EAAE,CAAA;YACP,SAAQ;QACV,CAAC;QAED,IAAI,IAAI,KAAK,GAAG,EAAE,CAAC;YACjB,KAAK,EAAE,CAAA;YACP,KAAK,EAAE,CAAA;YACP,SAAQ;QACV,CAAC;QAED,IAAI,IAAI,KAAK,GAAG,EAAE,CAAC;YACjB,IAAI,KAAK,EAAE,CAAC;gBACV,KAAK,EAAE,CAAA;gBACP,KAAK,EAAE,CAAA;gBACP,SAAQ;YACV,CAAC;YAED,cAAc,CAAC,KAAK,CAAC,CAAA;YAErB,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;YACzC,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAC3B,KAAK,GAAG,CAAC,EACT,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CACvC,CAAA;YAED,OAAO;gBACL,SAAS;gBACT,UAAU,EAAE,KAAK;gBACjB,QAAQ,EAAE,IAAI;gBACd,UAAU;gBACV,UAAU;aACX,CAAA;QACH,CAAC;QAED,IAAI,IAAI,KAAK,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC;YAC3B,cAAc,CAAC,KAAK,CAAC,CAAA;YACrB,cAAc,GAAG,KAAK,GAAG,CAAC,CAAA;YAC1B,KAAK,EAAE,CAAA;YACP,SAAQ;QACV,CAAC;QAED,KAAK,EAAE,CAAA;IACT,CAAC;IAED,OAAO;QACL,SAAS;QACT,UAAU,EAAE,CAAC,CAAC;QACd,QAAQ,EAAE,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,aAAa;QACjE,UAAU;QACV,UAAU,EAAE,EAAE;KACf,CAAA;AACH,CAAC,CAAA;AApHY,QAAA,uBAAuB,2BAoHnC"}
|