@sasjs/lint 4.4.0 → 4.6.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/README.md +68 -6
- package/package.json +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/minMacroNameLength.d.ts +13 -0
- package/rules/file/minMacroNameLength.js +132 -0
- package/rules/file/minMacroNameLength.js.map +1 -0
- package/rules/file/noUndeclaredMacros.js +13 -0
- package/rules/file/noUndeclaredMacros.js.map +1 -1
- package/rules/file/noUnusedMacros.js +3 -0
- package/rules/file/noUnusedMacros.js.map +1 -1
- package/rules/line/index.d.ts +1 -0
- package/rules/line/index.js +3 -1
- package/rules/line/index.js.map +1 -1
- package/rules/line/noBannedStrings.d.ts +6 -0
- package/rules/line/noBannedStrings.js +74 -0
- package/rules/line/noBannedStrings.js.map +1 -0
- package/sasjslint-schema.json +54 -1
- package/types/LintConfig.d.ts +2 -0
- package/types/LintConfig.js +46 -0
- package/types/LintConfig.js.map +1 -1
- package/utils/defaultBannedStrings.d.ts +15 -0
- package/utils/defaultBannedStrings.js +19 -0
- package/utils/defaultBannedStrings.js.map +1 -0
- package/utils/getLintConfig.d.ts +3 -0
- package/utils/getLintConfig.js +4 -0
- package/utils/getLintConfig.js.map +1 -1
- package/utils/index.d.ts +1 -0
- package/utils/index.js +1 -0
- package/utils/index.js.map +1 -1
- package/utils/parseMacroCalls.d.ts +4 -0
- package/utils/parseMacroCalls.js +39 -15
- package/utils/parseMacroCalls.js.map +1 -1
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
Our goal is to help SAS developers everywhere spend less time on code reviews, bug fixing and arguing about standards - and more time delivering extraordinary business value.
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
_Note:_ The SASjs project and its repositories are not affiliated with SAS Institute.
|
|
12
12
|
|
|
13
13
|
# Linting
|
|
14
14
|
|
|
@@ -22,6 +22,7 @@ Configuration is via a `.sasjslint` file with the following structure (these are
|
|
|
22
22
|
```json
|
|
23
23
|
{
|
|
24
24
|
"allowedGremlins": [],
|
|
25
|
+
"bannedStrings": ["error ", "error:", "warning ", "warning:"],
|
|
25
26
|
"defaultHeader": "/**{lineEnding} @file{lineEnding} @brief <Your brief here>{lineEnding}{lineEnding} <h4> SAS Macros </h4>{lineEnding}{lineEnding}**/",
|
|
26
27
|
"hasDoxygenHeader": true,
|
|
27
28
|
"hasMacroNameInMend": true,
|
|
@@ -36,6 +37,8 @@ Configuration is via a `.sasjslint` file with the following structure (these are
|
|
|
36
37
|
"maxDataLineLength": 80,
|
|
37
38
|
"maxHeaderLineLength": 80,
|
|
38
39
|
"maxLineLength": 80,
|
|
40
|
+
"minMacroNameLength": 3,
|
|
41
|
+
"noBannedStrings": true,
|
|
39
42
|
"noEncodedPasswords": true,
|
|
40
43
|
"noGremlins": true,
|
|
41
44
|
"noNestedCommentsInHeader": true,
|
|
@@ -65,7 +68,7 @@ Each setting can have three states:
|
|
|
65
68
|
|
|
66
69
|
For more details, and the default state, see the description of each rule below. It is also possible to change whether a rule returns ERROR or WARN using the `severityLevel` object.
|
|
67
70
|
|
|
68
|
-
Configuring a non-zero return code (ERROR) is helpful when running `sasjs lint` as part of a git pre-commit hook.
|
|
71
|
+
Configuring a non-zero return code (ERROR) is helpful when running `sasjs lint` as part of a git pre-commit hook. An example is available [here](https://github.com/sasjs/template_jobs/blob/main/.git-hooks/pre-commit).
|
|
69
72
|
|
|
70
73
|
## Per-file overrides
|
|
71
74
|
|
|
@@ -92,7 +95,7 @@ One exception: `ignoreList` is evaluated before a file is read, so an entry a he
|
|
|
92
95
|
|
|
93
96
|
### allowedGremlins
|
|
94
97
|
|
|
95
|
-
An array of hex codes that represents allowed gremlins (invisible / undesirable characters). To allow all gremlins, you can also set the `noGremlins` rule to `false`.
|
|
98
|
+
An array of hex codes that represents allowed gremlins (invisible / undesirable characters). To allow all gremlins, you can also set the `noGremlins` rule to `false`. The full gremlin list is [here](https://github.com/sasjs/lint/blob/main/src/utils/gremlinCharacters.ts).
|
|
96
99
|
|
|
97
100
|
Example:
|
|
98
101
|
|
|
@@ -103,6 +106,21 @@ Example:
|
|
|
103
106
|
}
|
|
104
107
|
```
|
|
105
108
|
|
|
109
|
+
### bannedStrings
|
|
110
|
+
|
|
111
|
+
An array of strings for the [`noBannedStrings`](#nobannedstrings) rule to report. Matching is case insensitive, and each entry is matched literally rather than as a regular expression, so a trailing space means what it says: `"error "` matches `error ` but not `errors`, `error_flag` or `error:`. Each default entry ends the word with the space or the colon that follows it in a log line.
|
|
112
|
+
|
|
113
|
+
The default list is `["error ", "error:", "warning ", "warning:"]`. An explicit list replaces it, so `"bannedStrings": []` reports nothing.
|
|
114
|
+
|
|
115
|
+
Example:
|
|
116
|
+
|
|
117
|
+
```json
|
|
118
|
+
{
|
|
119
|
+
"noBannedStrings": true,
|
|
120
|
+
"bannedStrings": ["error ", "error:", "warning ", "warning:", "{sas001}"]
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
106
124
|
### defaultHeader
|
|
107
125
|
|
|
108
126
|
This isn't a rule, but a formatting setting, which applies to SAS program that do NOT begin with `/**`. It can be triggered by running `sasjs lint fix` in the SASjs CLI, or by hitting "save" when using the SASjs VS Code extension (with "formatOnSave" in place)
|
|
@@ -168,6 +186,7 @@ This will require macros to have the options listed as "requiredMacroOptions." T
|
|
|
168
186
|
- severity: WARNING
|
|
169
187
|
|
|
170
188
|
Example
|
|
189
|
+
|
|
171
190
|
```json
|
|
172
191
|
{
|
|
173
192
|
"hasRequiredMacroOptions": true,
|
|
@@ -297,6 +316,44 @@ See also:
|
|
|
297
316
|
- [maxDataLineLength](#maxdatalinelength)
|
|
298
317
|
- [maxHeaderLineLength](#maxheaderlinelength)
|
|
299
318
|
|
|
319
|
+
### minMacroNameLength
|
|
320
|
+
|
|
321
|
+
The shortest macro name the linter will treat as a macro name, and the shortest a file may define.
|
|
322
|
+
|
|
323
|
+
It exists because other languages write a percentage sign that reads as a macro call. The default of three is set by what those look like: a conversion is one character (`%s(`, `%d(`, `%x(`), a length modifier makes it two (`%ld(`, `%lu(`, `%Lf(`), and a macro name that anyone documents is longer than either - so three clears the specifiers a format string actually contains and still admits every real name. [`noUndeclaredMacros`](#noundeclaredmacros) applies the floor when it reads invocations, so a short "call" is not reported, and this rule reports a definition that short - so a macro the scanner could not see is caught where it is written rather than silently ignored. [`noUnusedMacros`](#nounusedmacros) deliberately does not apply it: over-counting a use keeps a declaration, while under-counting one has the formatter delete it, so every parsed name counts as a use.
|
|
324
|
+
|
|
325
|
+
The default covers `%s(` and the length-modifier forms such as `%ld(`. A longer conversion - `%lld(` - still reads as a name, and a file that formats numbers that way raises the floor for itself (`@sasjslint {"minMacroNameLength": 4}`), lists those names in [`ignoredMacros`](#ignoredmacros), or switches the rule off for itself. The linter does not try to parse a format string: the residue is a handful of names in a handful of files, and a specifier table would have to track every dialect that writes one.
|
|
326
|
+
|
|
327
|
+
A definition that lives inside a multi-line double-quoted string - a template the file writes out rather than code it runs - is read as a definition, because the macro parser reads lines rather than tracking quotes. A single-quoted template cannot span lines in SAS and is not read that way. That file raises the floor for itself or switches the rule off.
|
|
328
|
+
|
|
329
|
+
Short macro names are legal SAS, but they are not documentable: `%macro s();` reads as a format specifier in every context a reader meets it. To turn the floor off, set the value to 1, which also returns `noUndeclaredMacros` to reading single-character names.
|
|
330
|
+
|
|
331
|
+
- Default: 3
|
|
332
|
+
- Severity: WARNING
|
|
333
|
+
|
|
334
|
+
### noBannedStrings
|
|
335
|
+
|
|
336
|
+
Reports every line that contains one of the strings in [`bannedStrings`](#bannedstrings), anywhere in the line - in a comment, in a string literal, or in code, because the text is read the way a person or a log scanner reads it rather than as SAS tokens.
|
|
337
|
+
|
|
338
|
+
The point is the log. A comment such as `/* returns the error message as text */` contains `error `, and so does a real `ERROR:` line, so a grep of the SAS log - and any log scanner built on one, including the checks people run before they look at a job - returns the comment along with the failure. The same applies to a `%put` that copies a sentence out of the documentation. Keeping those words out of the source means a match in the log is a message that came from somewhere real.
|
|
339
|
+
|
|
340
|
+
Three boundaries are deliberate. The line that carries a `@sasjslint` override is configuration rather than prose, so a file that adds a banned string does not report its own setting. Source data is left alone: a `datalines` payload may hold the text `error ` in a value, and that is data rather than something a developer can reword. And matching is case insensitive, so `Error`, `ERROR` and `error` are the same banned string; the diagnostic quotes the text as written. There is no setting to make it case sensitive: the words being caught are log lookalikes, and the log does not preserve the case an author typed either.
|
|
341
|
+
|
|
342
|
+
The entry is matched as text, so it is not anchored to a word boundary. `saserror = 1` therefore satisfies `"error "`, because the identifier ends with the word and the space before the `=` is the trailing space the entry asks for. Listing `"error "` is what keeps `errors` and `error_flag` out, and every default entry is written the same way for that reason.
|
|
343
|
+
|
|
344
|
+
The rule is a warning by default. A single file can switch it off or downgrade it with a `@sasjslint` block in its header, which is the usual way to keep a legacy program out of scope:
|
|
345
|
+
|
|
346
|
+
```sas
|
|
347
|
+
/**
|
|
348
|
+
@file
|
|
349
|
+
@brief Legacy job, comments left as written
|
|
350
|
+
@sasjslint {"noBannedStrings": false}
|
|
351
|
+
**/
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
- Default: true
|
|
355
|
+
- Severity: WARNING
|
|
356
|
+
|
|
300
357
|
### noEncodedPasswords
|
|
301
358
|
|
|
302
359
|
This rule will highlight any rows that contain a `{sas00X}` type password, or `{sasenc}`. These passwords (especially 001 and 002) are NOT secure, and should NEVER be pushed to source control or saved to the filesystem without special permissions applied.
|
|
@@ -390,6 +447,8 @@ The macros that ship with SAS (`%scan`, `%index`, `%sysfunc` and the other macro
|
|
|
390
447
|
|
|
391
448
|
An invocation written inside the argument of `%str` / `%nrstr` is not reported. A file masks a trigger to print it as often as to defer it, and nothing runs at that point, so a warning there would land on text that may never be code. [`noUnusedMacros`](#nounusedmacros) counts such a name as a use, so the deferred form - `%mp_abort(iftrue=%str(%mf_existds(work.x)=1))` - satisfies the declaration it depends on without demanding one.
|
|
392
449
|
|
|
450
|
+
A name written inside a single-quoted string is reported too, for the same reason as the masking case in reverse: the string is usually a program the file submits, so the macro has to be available when it runs. Text in a string is not always code, though, so two tests keep the noise down. A name shorter than [`minMacroNameLength`](#minmacronamelength) is never reported (`%s(`, `%d(`, `%x(`), and a name inside a string counts only when an opening parenthesis follows it - a generated program calls a macro that way, while a SQL LIKE pattern (`'%abc%'`) or a conversion specifier has nothing after the name. The parenthesis test sits with [`hasMacroParentheses`](#hasmacroparentheses), which requires every definition in the codebase to declare one. A template carrying the macros that its generated program declares for itself goes in [`ignoredMacros`](#ignoredmacros) or has the rule switched off for that file with a `@sasjslint` block.
|
|
451
|
+
|
|
393
452
|
The warning is resolved by adding the macro name to the header. Running `sasjs lint fix` (or saving in the SASjs VS Code extension with `formatOnSave`) adds the missing macros automatically, as a unique list sorted alphabetically.
|
|
394
453
|
|
|
395
454
|
- Default: true
|
|
@@ -447,6 +506,8 @@ A macro can be referenced indirectly - for example by another macro in the depen
|
|
|
447
506
|
|
|
448
507
|
An invocation written inside the argument of `%str` / `%nrstr` counts as a use. That text is data while the masking function runs, but its caller normally unquotes it - `%mp_abort(iftrue=%str(%mf_existds(work.x)=1))` resolves `%mf_existds` when `%mp_abort` evaluates the condition - so the declaration is a dependency of the file, and treating it as unused would have the formatter delete it.
|
|
449
508
|
|
|
509
|
+
An invocation written inside a single-quoted string counts as a use as well. SAS resolves no macro trigger there, so the text is data while the string is built - but code that generates code submits it later: `call execute('%mf_mkdir('!!path!!')')` runs the macro when the DATA step executes, and a program written to a fileref and `%inc`ed runs it then. Counting it as unused would report a declaration the file needs, and the formatter would delete it.
|
|
510
|
+
|
|
450
511
|
Running `sasjs lint fix` (or saving in the SASjs VS Code extension with `formatOnSave`) removes the unused entries from the header. Together with `noUndeclaredMacros`, this leaves the `<h4> SAS Macros </h4>` section listing exactly the macros the file uses. The pass that reads the list also writes each entry back as `@li <macro>.sas` at the section's indentation, in place and without re-spacing the header, so a mistyped tag (`@il`) is repaired even when no entry is removed.
|
|
451
512
|
|
|
452
513
|
- Default: true
|
|
@@ -509,13 +570,14 @@ Contact [Allan Bowe](https://www.linkedin.com/in/allanbowe/) for further details
|
|
|
509
570
|
|
|
510
571
|
# Further resources
|
|
511
572
|
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
573
|
+
- Using the linter on terminal: https://vid.4gl.io/w/vmJspCjcBoc5QtzwZkZRvi
|
|
574
|
+
- Longer intro to sasjs lint: https://vid.4gl.io/w/nDtkQFV1E8rtaa2BuM6U5s
|
|
575
|
+
- CLI docs: https://cli.sasjs.io/lint
|
|
515
576
|
|
|
516
577
|
# Contributors ✨
|
|
517
578
|
|
|
518
579
|
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
|
|
580
|
+
|
|
519
581
|
[](#contributors-)
|
|
520
582
|
<!-- ALL-CONTRIBUTORS-BADGE:END -->
|
|
521
583
|
|
package/package.json
CHANGED
package/rules/file/index.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ export { hasDoxygenHeader } from './hasDoxygenHeader';
|
|
|
2
2
|
export { hasMacroNameInMend } from './hasMacroNameInMend';
|
|
3
3
|
export { hasMacroParentheses } from './hasMacroParentheses';
|
|
4
4
|
export { lineEndings } from './lineEndings';
|
|
5
|
+
export { minMacroNameLength } from './minMacroNameLength';
|
|
5
6
|
export { noNestedCommentsInHeader } from './noNestedCommentsInHeader';
|
|
6
7
|
export { noNestedMacros } from './noNestedMacros';
|
|
7
8
|
export { noUndeclaredMacros } from './noUndeclaredMacros';
|
package/rules/file/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.noSingleAsteriskComments = exports.hasRequiredMacroOptions = exports.strictMacroDefinition = exports.noUnusedLibnames = exports.noUnusedMacros = exports.noUndeclaredMacros = exports.noNestedMacros = exports.noNestedCommentsInHeader = exports.lineEndings = exports.hasMacroParentheses = exports.hasMacroNameInMend = exports.hasDoxygenHeader = void 0;
|
|
3
|
+
exports.noSingleAsteriskComments = exports.hasRequiredMacroOptions = exports.strictMacroDefinition = exports.noUnusedLibnames = exports.noUnusedMacros = exports.noUndeclaredMacros = exports.noNestedMacros = exports.noNestedCommentsInHeader = 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");
|
|
@@ -9,6 +9,8 @@ var hasMacroParentheses_1 = require("./hasMacroParentheses");
|
|
|
9
9
|
Object.defineProperty(exports, "hasMacroParentheses", { enumerable: true, get: function () { return hasMacroParentheses_1.hasMacroParentheses; } });
|
|
10
10
|
var lineEndings_1 = require("./lineEndings");
|
|
11
11
|
Object.defineProperty(exports, "lineEndings", { enumerable: true, get: function () { return lineEndings_1.lineEndings; } });
|
|
12
|
+
var minMacroNameLength_1 = require("./minMacroNameLength");
|
|
13
|
+
Object.defineProperty(exports, "minMacroNameLength", { enumerable: true, get: function () { return minMacroNameLength_1.minMacroNameLength; } });
|
|
12
14
|
var noNestedCommentsInHeader_1 = require("./noNestedCommentsInHeader");
|
|
13
15
|
Object.defineProperty(exports, "noNestedCommentsInHeader", { enumerable: true, get: function () { return noNestedCommentsInHeader_1.noNestedCommentsInHeader; } });
|
|
14
16
|
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,uEAAqE;AAA5D,oIAAA,wBAAwB,OAAA;AACjC,mDAAiD;AAAxC,gHAAA,cAAc,OAAA;AACvB,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"}
|
|
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,uEAAqE;AAA5D,oIAAA,wBAAwB,OAAA;AACjC,mDAAiD;AAAxC,gHAAA,cAAc,OAAA;AACvB,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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { FileLintRule } from '../../types/LintRule';
|
|
2
|
+
/**
|
|
3
|
+
* Reports a macro definition whose name is shorter than the configured floor.
|
|
4
|
+
*
|
|
5
|
+
* The floor exists because other languages write a percentage specifier that
|
|
6
|
+
* reads as a macro call, and the default of three is the width of the ones a
|
|
7
|
+
* format string actually contains: a conversion is one character (`%s(`,
|
|
8
|
+
* `%d(`), a length modifier makes it two (`%ld(`, `%lu(`, `%Lf(`), and a name
|
|
9
|
+
* anyone documents is longer than either. `noUndeclaredMacros` reads
|
|
10
|
+
* invocations through the same floor, so a definition the scanner could not
|
|
11
|
+
* see is reported here instead of being silently ignored.
|
|
12
|
+
*/
|
|
13
|
+
export declare const minMacroNameLength: FileLintRule;
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.minMacroNameLength = void 0;
|
|
4
|
+
const types_1 = require("../../types");
|
|
5
|
+
const LintRuleType_1 = require("../../types/LintRuleType");
|
|
6
|
+
const Severity_1 = require("../../types/Severity");
|
|
7
|
+
const utils_1 = require("../../utils");
|
|
8
|
+
const getColumnNumber_1 = require("../../utils/getColumnNumber");
|
|
9
|
+
const parseMacros_1 = require("../../utils/parseMacros");
|
|
10
|
+
const splitLines_1 = require("../../utils/splitLines");
|
|
11
|
+
const name = 'minMacroNameLength';
|
|
12
|
+
const description = 'Ensure a macro name is long enough not to collide with another language.';
|
|
13
|
+
const message = `Macro name '{macro}' is shorter than {length} characters`;
|
|
14
|
+
/** Escapes a macro name so it can be interpolated into a RegExp literally. */
|
|
15
|
+
const escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
16
|
+
/**
|
|
17
|
+
* The block-comment spans on a line. A `/*` inside a single-quoted string opens
|
|
18
|
+
* nothing - `%put '/*';` writes those characters - so the scan tracks quotes.
|
|
19
|
+
*/
|
|
20
|
+
const commentSpans = (line) => {
|
|
21
|
+
const spans = [];
|
|
22
|
+
let inString = false;
|
|
23
|
+
let start = -1;
|
|
24
|
+
for (let index = 0; index < line.length; index++) {
|
|
25
|
+
const char = line[index];
|
|
26
|
+
if (char === "'") {
|
|
27
|
+
inString = !inString;
|
|
28
|
+
}
|
|
29
|
+
else if (!inString && char === '/' && line[index + 1] === '*') {
|
|
30
|
+
start = index;
|
|
31
|
+
index++;
|
|
32
|
+
}
|
|
33
|
+
else if (!inString &&
|
|
34
|
+
char === '*' &&
|
|
35
|
+
line[index + 1] === '/' &&
|
|
36
|
+
start > -1) {
|
|
37
|
+
spans.push([start, index + 2]);
|
|
38
|
+
start = -1;
|
|
39
|
+
index++;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
if (start > -1)
|
|
43
|
+
spans.push([start, line.length]);
|
|
44
|
+
return spans;
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* True when the offset sits inside a block comment on this line. A comment can
|
|
48
|
+
* carry a declaration that is not the one being reported - `/* %macro a(); *\/`
|
|
49
|
+
* before the real `%macro s();` - and that text takes no position.
|
|
50
|
+
*/
|
|
51
|
+
const inComment = (line, index) => {
|
|
52
|
+
const spans = commentSpans(line);
|
|
53
|
+
return spans.some(([start, end]) => index > start && index < end);
|
|
54
|
+
};
|
|
55
|
+
const test = (value, config) => {
|
|
56
|
+
var _a;
|
|
57
|
+
const severity = (config === null || config === void 0 ? void 0 : config.severityLevel[name]) || Severity_1.Severity.Warning;
|
|
58
|
+
const minimum = (_a = config === null || config === void 0 ? void 0 : config.minMacroNameLength) !== null && _a !== void 0 ? _a : 3;
|
|
59
|
+
// A floor below two characters switches the rule off, the way a zero
|
|
60
|
+
// `maxLineLength` switches its rule off.
|
|
61
|
+
if (minimum < 2)
|
|
62
|
+
return [];
|
|
63
|
+
const lines = (0, splitLines_1.splitLines)(value);
|
|
64
|
+
const cfg = config || new types_1.LintConfig();
|
|
65
|
+
// A data section - `cards` / `parmcards` / `datalines` - holds text that is
|
|
66
|
+
// written somewhere, so a `%macro x();` in it is payload rather than a
|
|
67
|
+
// definition. The macro parser reads the block as code, so the extent comes
|
|
68
|
+
// from the same helper the line rules use, which keeps one definition of a
|
|
69
|
+
// data section in the package.
|
|
70
|
+
const dataSections = (0, utils_1.getDataSectionsDetail)(value, cfg);
|
|
71
|
+
// A name can appear more than once on a line, so each declaration takes the
|
|
72
|
+
// match at its own position rather than the first one.
|
|
73
|
+
const seen = new Map();
|
|
74
|
+
return ((0, parseMacros_1.parseMacros)(value, cfg)
|
|
75
|
+
// An orphan `%mend` yields an entry with no name and no start line, which
|
|
76
|
+
// has no definition to measure and no line to report on.
|
|
77
|
+
.filter((macro) => { var _a; return macro.name && ((_a = macro.startLineNumbers) === null || _a === void 0 ? void 0 : _a.length); })
|
|
78
|
+
.filter((macro) => macro.name.length < minimum)
|
|
79
|
+
.filter((macro) => !(0, utils_1.checkIsDataLine)(dataSections, macro.startLineNumbers[0] - 1))
|
|
80
|
+
.map((macro) => {
|
|
81
|
+
var _a;
|
|
82
|
+
const lineNumber = macro.startLineNumbers[0];
|
|
83
|
+
const line = lines[lineNumber - 1] || '';
|
|
84
|
+
// The diagnostic covers `%macro <name>`. The column comes from the
|
|
85
|
+
// declaration itself rather than the first `%macro` on the line, so a
|
|
86
|
+
// comment that mentions one - `/* %macro a(); */ %macro s();` - does not
|
|
87
|
+
// take the position. The name has to match, which is what tells this
|
|
88
|
+
// declaration apart from an earlier one on the same line.
|
|
89
|
+
const key = `${lineNumber}:${macro.name.toLowerCase()}`;
|
|
90
|
+
const occurrence = (_a = seen.get(key)) !== null && _a !== void 0 ? _a : 0;
|
|
91
|
+
seen.set(key, occurrence + 1);
|
|
92
|
+
const matches = [
|
|
93
|
+
...line.matchAll(new RegExp(`%macro\\s+${escapeRegExp(macro.name)}\\b`, 'gi'))
|
|
94
|
+
].filter((match) => !inComment(line, match.index));
|
|
95
|
+
const declaration = matches[occurrence];
|
|
96
|
+
const startColumnNumber = declaration
|
|
97
|
+
? declaration.index + 1
|
|
98
|
+
: (0, getColumnNumber_1.getColumnNumber)(line, '%macro');
|
|
99
|
+
return {
|
|
100
|
+
message: message
|
|
101
|
+
.replace('{macro}', macro.name)
|
|
102
|
+
.replace('{length}', String(minimum)),
|
|
103
|
+
lineNumber,
|
|
104
|
+
startColumnNumber,
|
|
105
|
+
// When the name is on a later line than `%macro`, the diagnostic
|
|
106
|
+
// covers the keyword itself rather than the rest of the line.
|
|
107
|
+
endColumnNumber: declaration
|
|
108
|
+
? startColumnNumber + declaration[0].length - 1
|
|
109
|
+
: startColumnNumber + '%macro'.length - 1,
|
|
110
|
+
severity
|
|
111
|
+
};
|
|
112
|
+
}));
|
|
113
|
+
};
|
|
114
|
+
/**
|
|
115
|
+
* Reports a macro definition whose name is shorter than the configured floor.
|
|
116
|
+
*
|
|
117
|
+
* The floor exists because other languages write a percentage specifier that
|
|
118
|
+
* reads as a macro call, and the default of three is the width of the ones a
|
|
119
|
+
* format string actually contains: a conversion is one character (`%s(`,
|
|
120
|
+
* `%d(`), a length modifier makes it two (`%ld(`, `%lu(`, `%Lf(`), and a name
|
|
121
|
+
* anyone documents is longer than either. `noUndeclaredMacros` reads
|
|
122
|
+
* invocations through the same floor, so a definition the scanner could not
|
|
123
|
+
* see is reported here instead of being silently ignored.
|
|
124
|
+
*/
|
|
125
|
+
exports.minMacroNameLength = {
|
|
126
|
+
type: LintRuleType_1.LintRuleType.File,
|
|
127
|
+
name,
|
|
128
|
+
description,
|
|
129
|
+
message,
|
|
130
|
+
test
|
|
131
|
+
};
|
|
132
|
+
//# sourceMappingURL=minMacroNameLength.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"minMacroNameLength.js","sourceRoot":"","sources":["../../../src/rules/file/minMacroNameLength.ts"],"names":[],"mappings":"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"}
|
|
@@ -41,10 +41,23 @@ const getUndeclaredCalls = (value, config) => {
|
|
|
41
41
|
const undeclared = [];
|
|
42
42
|
const seen = new Set();
|
|
43
43
|
(0, parseMacroCalls_1.parseMacroCalls)(value, config).forEach((call) => {
|
|
44
|
+
var _a;
|
|
44
45
|
const key = call.name.toLowerCase();
|
|
45
46
|
if (seen.has(key))
|
|
46
47
|
return;
|
|
47
48
|
seen.add(key);
|
|
49
|
+
// A name shorter than the floor is not a macro name: Lua and printf put
|
|
50
|
+
// `%s(` and friends inside strings. The `minMacroNameLength` rule reports
|
|
51
|
+
// a definition that short, so the two agree about what a name is.
|
|
52
|
+
if (call.name.length < ((_a = config.minMacroNameLength) !== null && _a !== void 0 ? _a : 3))
|
|
53
|
+
return;
|
|
54
|
+
// In a string a name is a call when an opening parenthesis follows it: a
|
|
55
|
+
// generated program calls a macro that way, while a SQL LIKE pattern or a
|
|
56
|
+
// conversion specifier has nothing after the name. The test lives here and
|
|
57
|
+
// not in the parser because noUnusedMacros counts every name - a use it
|
|
58
|
+
// misses is a declaration the formatter deletes.
|
|
59
|
+
if (call.quoted && !call.hasParen)
|
|
60
|
+
return;
|
|
48
61
|
if ((0, sasMacros_1.isSasMacro)(call.name))
|
|
49
62
|
return;
|
|
50
63
|
if (ignored.has(key))
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"noUndeclaredMacros.js","sourceRoot":"","sources":["../../../src/rules/file/noUndeclaredMacros.ts"],"names":[],"mappings":";;;AAAA,uCAAoD;AAEpD,2DAAuD;AACvD,mDAA+C;AAC/C,2DAA6E;AAC7E,iEAAwE;AACxE,yDAAqD;AACrD,qDAAkD;AAElD,MAAM,IAAI,GAAG,oBAAoB,CAAA;AACjC,MAAM,WAAW,GAAG,sDAAsD,CAAA;AAC1E,MAAM,OAAO,GAAG,mIAAmI,CAAA;AAEnJ;;;;;;;;;;;;;;GAcG;AACH,MAAM,kBAAkB,GAAG,CAAC,KAAa,EAAE,MAAkB,EAAe,EAAE;IAC5E,IAAI,CAAC,KAAK;QAAE,OAAO,EAAE,CAAA;IAErB,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAU,CAAA;IAElC,IAAA,yBAAW,EAAC,KAAK,EAAE,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE;QAC3C,IAAI,KAAK,CAAC,IAAI;YAAE,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAA;IACxD,CAAC,CAAC,CAAA;IAEF,IAAA,8BAAe,EAAC,KAAK,EAAE,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE;QAC/C,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAA;IACxC,CAAC,CAAC,CAAA;IAEF,MAAM,OAAO,GAAG,IAAI,GAAG,CACrB,MAAM,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,CAChE,CAAA;IAED,MAAM,UAAU,GAAgB,EAAE,CAAA;IAClC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAA;IAE9B,IAAA,iCAAe,EAAC,KAAK,EAAE,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE
|
|
1
|
+
{"version":3,"file":"noUndeclaredMacros.js","sourceRoot":"","sources":["../../../src/rules/file/noUndeclaredMacros.ts"],"names":[],"mappings":"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"}
|
|
@@ -23,6 +23,9 @@ const message = `Macro '{macro}' is declared in the <h4> SAS Macros </h4> sectio
|
|
|
23
23
|
const getUnusedMacros = (value, config) => {
|
|
24
24
|
if (!value)
|
|
25
25
|
return [];
|
|
26
|
+
// Every parsed name counts as a use, including one shorter than
|
|
27
|
+
// `minMacroNameLength`: over-counting a use keeps a declaration, and
|
|
28
|
+
// under-counting one has the formatter delete it.
|
|
26
29
|
const used = new Set((0, parseMacroCalls_1.parseMacroCalls)(value, config, { includeMasked: true }).map((call) => call.name.toLowerCase()));
|
|
27
30
|
const unused = [];
|
|
28
31
|
const reported = new Set();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"noUnusedMacros.js","sourceRoot":"","sources":["../../../src/rules/file/noUnusedMacros.ts"],"names":[],"mappings":";;;AAAA,uCAAoD;AAEpD,2DAAuD;AACvD,mDAA+C;AAC/C,2DAKiC;AACjC,iEAA6D;AAE7D,MAAM,IAAI,GAAG,gBAAgB,CAAA;AAC7B,MAAM,WAAW,GACf,yFAAyF,CAAA;AAC3F,MAAM,OAAO,GAAG,2FAA2F,CAAA;AAE3G;;;;;;;;;;GAUG;AACH,MAAM,eAAe,GAAG,CAAC,KAAa,EAAE,MAAkB,EAAiB,EAAE;IAC3E,IAAI,CAAC,KAAK;QAAE,OAAO,EAAE,CAAA;IAErB,MAAM,IAAI,GAAG,IAAI,GAAG,CAClB,IAAA,iCAAe,EAAC,KAAK,EAAE,MAAM,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CACnE,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CACxB,CACF,CAAA;IAED,MAAM,MAAM,GAAkB,EAAE,CAAA;IAChC,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAU,CAAA;IAElC,IAAA,8BAAe,EAAC,KAAK,EAAE,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE;QAC/C,MAAM,GAAG,GAAG,KAAK,CAAC,IAAI,CAAC,WAAW,EAAE,CAAA;QACpC,IAAI,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,OAAM;QAC7B,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;QACjB,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,OAAM;QAEzB,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;IACpB,CAAC,CAAC,CAAA;IAEF,OAAO,MAAM,CAAA;AACf,CAAC,CAAA;AAED,MAAM,IAAI,GAAG,CAAC,KAAa,EAAE,MAAmB,EAAgB,EAAE;IAChE,MAAM,GAAG,GAAG,MAAM,IAAI,IAAI,kBAAU,EAAE,CAAA;IACtC,MAAM,QAAQ,GAAG,GAAG,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,mBAAQ,CAAC,OAAO,CAAA;IAE5D,OAAO,eAAe,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QACjD,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,SAAS,EAAE,KAAK,CAAC,IAAI,CAAC;QAC/C,UAAU,EAAE,KAAK,CAAC,UAAU;QAC5B,iBAAiB,EAAE,KAAK,CAAC,iBAAiB;QAC1C,eAAe,EAAE,KAAK,CAAC,eAAe;QACtC,QAAQ;KACT,CAAC,CAAC,CAAA;AACL,CAAC,CAAA;AAED,MAAM,GAAG,GAAG,CAAC,KAAa,EAAE,MAAmB,EAAU,EAAE;IACzD,MAAM,GAAG,GAAG,MAAM,IAAI,IAAI,kBAAU,EAAE,CAAA;IACtC,MAAM,MAAM,GAAG,eAAe,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;IAErE,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM;QAC3B,CAAC,CAAC,IAAA,qCAAsB,EAAC,KAAK,EAAE,GAAG,EAAE,MAAM,CAAC;QAC5C,CAAC,CAAC,KAAK,CAAA;IAET,4EAA4E;IAC5E,2EAA2E;IAC3E,2CAA2C;IAC3C,OAAO,IAAA,+BAAgB,EAAC,OAAO,EAAE,GAAG,CAAC,CAAA;AACvC,CAAC,CAAA;AAED;;;;GAIG;AACU,QAAA,cAAc,GAAiB;IAC1C,IAAI,EAAE,2BAAY,CAAC,IAAI;IACvB,IAAI;IACJ,WAAW;IACX,OAAO;IACP,IAAI;IACJ,GAAG;CACJ,CAAA"}
|
|
1
|
+
{"version":3,"file":"noUnusedMacros.js","sourceRoot":"","sources":["../../../src/rules/file/noUnusedMacros.ts"],"names":[],"mappings":"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"}
|
package/rules/line/index.d.ts
CHANGED
package/rules/line/index.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.noTrailingSpaces = exports.noTabs = exports.noEncodedPasswords = exports.maxLineLength = exports.indentationMultiple = exports.noGremlins = void 0;
|
|
3
|
+
exports.noTrailingSpaces = exports.noTabs = exports.noEncodedPasswords = exports.maxLineLength = exports.indentationMultiple = exports.noGremlins = exports.noBannedStrings = void 0;
|
|
4
|
+
var noBannedStrings_1 = require("./noBannedStrings");
|
|
5
|
+
Object.defineProperty(exports, "noBannedStrings", { enumerable: true, get: function () { return noBannedStrings_1.noBannedStrings; } });
|
|
4
6
|
var noGremlins_1 = require("./noGremlins");
|
|
5
7
|
Object.defineProperty(exports, "noGremlins", { enumerable: true, get: function () { return noGremlins_1.noGremlins; } });
|
|
6
8
|
var indentationMultiple_1 = require("./indentationMultiple");
|
package/rules/line/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/rules/line/index.ts"],"names":[],"mappings":";;;AAAA,2CAAyC;AAAhC,wGAAA,UAAU,OAAA;AACnB,6DAA2D;AAAlD,0HAAA,mBAAmB,OAAA;AAC5B,iDAA+C;AAAtC,8GAAA,aAAa,OAAA;AACtB,2DAAyD;AAAhD,wHAAA,kBAAkB,OAAA;AAC3B,mCAAiC;AAAxB,gGAAA,MAAM,OAAA;AACf,uDAAqD;AAA5C,oHAAA,gBAAgB,OAAA"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/rules/line/index.ts"],"names":[],"mappings":";;;AAAA,qDAAmD;AAA1C,kHAAA,eAAe,OAAA;AACxB,2CAAyC;AAAhC,wGAAA,UAAU,OAAA;AACnB,6DAA2D;AAAlD,0HAAA,mBAAmB,OAAA;AAC5B,iDAA+C;AAAtC,8GAAA,aAAa,OAAA;AACtB,2DAAyD;AAAhD,wHAAA,kBAAkB,OAAA;AAC3B,mCAAiC;AAAxB,gGAAA,MAAM,OAAA;AACf,uDAAqD;AAA5C,oHAAA,gBAAgB,OAAA"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.noBannedStrings = void 0;
|
|
4
|
+
const LintRuleType_1 = require("../../types/LintRuleType");
|
|
5
|
+
const Severity_1 = require("../../types/Severity");
|
|
6
|
+
const utils_1 = require("../../utils");
|
|
7
|
+
const name = 'noBannedStrings';
|
|
8
|
+
const description = 'Disallow configured strings, such as words that look like log messages';
|
|
9
|
+
const message = 'Line contains a banned string';
|
|
10
|
+
/**
|
|
11
|
+
* Escapes a configured string so that it is matched literally. The list is
|
|
12
|
+
* user supplied, so `{sas001}`, `(c)` and `[x]` have to survive the trip
|
|
13
|
+
* through the RegExp constructor.
|
|
14
|
+
*/
|
|
15
|
+
const escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
16
|
+
/**
|
|
17
|
+
* The line that carries a per-file override is configuration rather than prose.
|
|
18
|
+
* A file that adds a banned string writes that string in the override, so
|
|
19
|
+
* without this the rule reports its own configuration.
|
|
20
|
+
*/
|
|
21
|
+
const overrideLine = /^[^\w]*@sasjslint\s*\{/;
|
|
22
|
+
const test = (value, lineNumber, config, options) => {
|
|
23
|
+
const diagnostics = [];
|
|
24
|
+
// Source data is data, not code: a `datalines` payload may legitimately hold
|
|
25
|
+
// the text `error ` in a value, and it is not something a developer can edit
|
|
26
|
+
// away without changing the data.
|
|
27
|
+
if (options === null || options === void 0 ? void 0 : options.isDataLine)
|
|
28
|
+
return diagnostics;
|
|
29
|
+
if (overrideLine.test(value))
|
|
30
|
+
return diagnostics;
|
|
31
|
+
const severity = (config === null || config === void 0 ? void 0 : config.severityLevel[name]) || Severity_1.Severity.Warning;
|
|
32
|
+
const bannedStrings = (config === null || config === void 0 ? void 0 : config.bannedStrings) || utils_1.defaultBannedStrings;
|
|
33
|
+
if (!bannedStrings.length)
|
|
34
|
+
return diagnostics;
|
|
35
|
+
// Longest first, so a project that lists both `error:` and `error` reports the
|
|
36
|
+
// more specific match rather than only the prefix of it.
|
|
37
|
+
const patterns = [...bannedStrings].sort((a, b) => b.length - a.length);
|
|
38
|
+
const claimed = new Array(value.length).fill(false);
|
|
39
|
+
for (const bannedString of patterns) {
|
|
40
|
+
// `g` to find every occurrence, `i` so that `Error`, `ERROR` and `error` are
|
|
41
|
+
// the same banned string - case sensitivity is opt out, not opt in.
|
|
42
|
+
const regex = new RegExp(escapeRegExp(bannedString), 'gi');
|
|
43
|
+
let match;
|
|
44
|
+
while ((match = regex.exec(value))) {
|
|
45
|
+
const start = match.index;
|
|
46
|
+
const end = start + match[0].length;
|
|
47
|
+
// A span already reported by a longer banned string is not reported again.
|
|
48
|
+
if (claimed.slice(start, end).some(Boolean))
|
|
49
|
+
continue;
|
|
50
|
+
for (let index = start; index < end; index++)
|
|
51
|
+
claimed[index] = true;
|
|
52
|
+
diagnostics.push({
|
|
53
|
+
message: `${message}: "${match[0]}"`,
|
|
54
|
+
lineNumber,
|
|
55
|
+
startColumnNumber: start + 1,
|
|
56
|
+
endColumnNumber: end + 1,
|
|
57
|
+
severity
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return diagnostics.sort((a, b) => a.startColumnNumber - b.startColumnNumber);
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* Lint rule that checks a given line of text for strings configured in
|
|
65
|
+
* `bannedStrings`.
|
|
66
|
+
*/
|
|
67
|
+
exports.noBannedStrings = {
|
|
68
|
+
type: LintRuleType_1.LintRuleType.Line,
|
|
69
|
+
name,
|
|
70
|
+
description,
|
|
71
|
+
message,
|
|
72
|
+
test
|
|
73
|
+
};
|
|
74
|
+
//# sourceMappingURL=noBannedStrings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"noBannedStrings.js","sourceRoot":"","sources":["../../../src/rules/line/noBannedStrings.ts"],"names":[],"mappings":"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"}
|
package/sasjslint-schema.json
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
"description": "The SASjs Lint Config file provides the settings for customising SAS code style in your project. A SAS file can override these settings for itself with a `@sasjslint` block in its Doxygen header.",
|
|
7
7
|
"default": {
|
|
8
8
|
"allowedGremlins": [],
|
|
9
|
+
"bannedStrings": ["error ", "error:", "warning ", "warning:"],
|
|
9
10
|
"defaultHeader": "/**{lineEnding} @file{lineEnding} @brief <Your brief here>{lineEnding}{lineEnding} <h4> SAS Macros </h4>{lineEnding}{lineEnding}**/",
|
|
10
11
|
"hasDoxygenHeader": true,
|
|
11
12
|
"hasMacroNameInMend": true,
|
|
@@ -20,6 +21,8 @@
|
|
|
20
21
|
"maxDataLineLength": 80,
|
|
21
22
|
"maxHeaderLineLength": 80,
|
|
22
23
|
"maxLineLength": 80,
|
|
24
|
+
"minMacroNameLength": 3,
|
|
25
|
+
"noBannedStrings": true,
|
|
23
26
|
"noEncodedPasswords": true,
|
|
24
27
|
"noGremlins": true,
|
|
25
28
|
"noNestedCommentsInHeader": true,
|
|
@@ -43,6 +46,7 @@
|
|
|
43
46
|
"noSpacesInFileNames": true,
|
|
44
47
|
"lowerCaseFileNames": true,
|
|
45
48
|
"maxLineLength": 80,
|
|
49
|
+
"minMacroNameLength": 3,
|
|
46
50
|
"maxHeaderLineLength": 80,
|
|
47
51
|
"maxDataLineLength": 80,
|
|
48
52
|
"noGremlins": true,
|
|
@@ -65,6 +69,14 @@
|
|
|
65
69
|
}
|
|
66
70
|
],
|
|
67
71
|
"properties": {
|
|
72
|
+
"noBannedStrings": {
|
|
73
|
+
"$id": "#/properties/noBannedStrings",
|
|
74
|
+
"type": "boolean",
|
|
75
|
+
"title": "noBannedStrings",
|
|
76
|
+
"description": "Reports lines that contain a string listed in bannedStrings, such as a word that looks like a log message. Matching is case insensitive. Shows a warning when one is present.",
|
|
77
|
+
"default": true,
|
|
78
|
+
"examples": [true, false]
|
|
79
|
+
},
|
|
68
80
|
"noEncodedPasswords": {
|
|
69
81
|
"$id": "#/properties/noEncodedPasswords",
|
|
70
82
|
"type": "boolean",
|
|
@@ -97,6 +109,21 @@
|
|
|
97
109
|
"default": [true],
|
|
98
110
|
"examples": [true, false]
|
|
99
111
|
},
|
|
112
|
+
"bannedStrings": {
|
|
113
|
+
"$id": "#/properties/bannedStrings",
|
|
114
|
+
"type": "array",
|
|
115
|
+
"items": {
|
|
116
|
+
"type": "string",
|
|
117
|
+
"minLength": 1
|
|
118
|
+
},
|
|
119
|
+
"title": "bannedStrings",
|
|
120
|
+
"description": "An array of strings for the noBannedStrings rule to report. Matching is case insensitive, and each entry is matched literally, so a trailing space means what it says.",
|
|
121
|
+
"default": ["error ", "error:", "warning ", "warning:"],
|
|
122
|
+
"examples": [
|
|
123
|
+
["error ", "error:", "warning ", "warning:"],
|
|
124
|
+
["{sas001}", "TODO"]
|
|
125
|
+
]
|
|
126
|
+
},
|
|
100
127
|
"allowedGremlins": {
|
|
101
128
|
"$id": "#/properties/allowedGremlins",
|
|
102
129
|
"type": "array",
|
|
@@ -165,6 +192,15 @@
|
|
|
165
192
|
"default": 80,
|
|
166
193
|
"examples": [60, 80, 120]
|
|
167
194
|
},
|
|
195
|
+
"minMacroNameLength": {
|
|
196
|
+
"$id": "#/properties/minMacroNameLength",
|
|
197
|
+
"type": "integer",
|
|
198
|
+
"minimum": 1,
|
|
199
|
+
"title": "minMacroNameLength",
|
|
200
|
+
"description": "Sets the shortest macro name the linter treats as a macro name, and the shortest a file may define. A short name cannot be told apart from a format specifier inside a string - %s( is one character, %ld( is two. Shows a warning for a definition shorter than this. Set to 1 to switch the rule off.",
|
|
201
|
+
"default": 3,
|
|
202
|
+
"examples": [3, 4]
|
|
203
|
+
},
|
|
168
204
|
"noNestedCommentsInHeader": {
|
|
169
205
|
"$id": "#/properties/noNestedCommentsInHeader",
|
|
170
206
|
"type": "boolean",
|
|
@@ -260,7 +296,10 @@
|
|
|
260
296
|
"title": "ignoreList",
|
|
261
297
|
"description": "An array of paths or path patterns to ignore when linting. Any files or matching patterns in the .gitignore file will also be ignored.",
|
|
262
298
|
"default": [],
|
|
263
|
-
"examples": [
|
|
299
|
+
"examples": [
|
|
300
|
+
["sasjsbuild/", "sasjsresults/"],
|
|
301
|
+
["sasjs/tests", "tmp/scratch.sas"]
|
|
302
|
+
]
|
|
264
303
|
},
|
|
265
304
|
"ignoredLibnames": {
|
|
266
305
|
"$id": "#/properties/ignoredLibnames",
|
|
@@ -313,6 +352,13 @@
|
|
|
313
352
|
}
|
|
314
353
|
],
|
|
315
354
|
"properties": {
|
|
355
|
+
"noBannedStrings": {
|
|
356
|
+
"$id": "#/properties/severityLevel/noBannedStrings",
|
|
357
|
+
"title": "noBannedStrings",
|
|
358
|
+
"type": "string",
|
|
359
|
+
"enum": ["error", "warn"],
|
|
360
|
+
"default": "warn"
|
|
361
|
+
},
|
|
316
362
|
"noEncodedPasswords": {
|
|
317
363
|
"$id": "#/properties/severityLevel/noEncodedPasswords",
|
|
318
364
|
"title": "noEncodedPasswords",
|
|
@@ -369,6 +415,13 @@
|
|
|
369
415
|
"enum": ["error", "warn"],
|
|
370
416
|
"default": "warn"
|
|
371
417
|
},
|
|
418
|
+
"minMacroNameLength": {
|
|
419
|
+
"$id": "#/properties/severityLevel/minMacroNameLength",
|
|
420
|
+
"title": "minMacroNameLength",
|
|
421
|
+
"type": "string",
|
|
422
|
+
"enum": ["error", "warn"],
|
|
423
|
+
"default": "warn"
|
|
424
|
+
},
|
|
372
425
|
"noNestedCommentsInHeader": {
|
|
373
426
|
"$id": "#/properties/severityLevel/noNestedCommentsInHeader",
|
|
374
427
|
"title": "noNestedCommentsInHeader",
|
package/types/LintConfig.d.ts
CHANGED
|
@@ -20,6 +20,7 @@ export declare class LintConfig {
|
|
|
20
20
|
readonly maxHeaderLineLength: number;
|
|
21
21
|
readonly maxDataLineLength: number;
|
|
22
22
|
readonly indentationMultiple: number;
|
|
23
|
+
readonly minMacroNameLength: number;
|
|
23
24
|
readonly lineEndings: LineEndings;
|
|
24
25
|
readonly defaultHeader: string;
|
|
25
26
|
readonly severityLevel: {
|
|
@@ -28,6 +29,7 @@ export declare class LintConfig {
|
|
|
28
29
|
readonly requiredMacroOptions: string[];
|
|
29
30
|
readonly ignoredLibnames: string[];
|
|
30
31
|
readonly ignoredMacros: string[];
|
|
32
|
+
readonly bannedStrings: string[];
|
|
31
33
|
constructor(json?: any);
|
|
32
34
|
/**
|
|
33
35
|
* Returns a new configuration with an override merged over this one, so a
|
package/types/LintConfig.js
CHANGED
|
@@ -26,12 +26,14 @@ class LintConfig {
|
|
|
26
26
|
this.maxHeaderLineLength = 80;
|
|
27
27
|
this.maxDataLineLength = 80;
|
|
28
28
|
this.indentationMultiple = 2;
|
|
29
|
+
this.minMacroNameLength = 3;
|
|
29
30
|
this.lineEndings = LineEndings_1.LineEndings.LF;
|
|
30
31
|
this.defaultHeader = (0, utils_1.getDefaultHeader)();
|
|
31
32
|
this.severityLevel = {};
|
|
32
33
|
this.requiredMacroOptions = [];
|
|
33
34
|
this.ignoredLibnames = [];
|
|
34
35
|
this.ignoredMacros = [];
|
|
36
|
+
this.bannedStrings = [];
|
|
35
37
|
this.source = json !== null && json !== void 0 ? json : {};
|
|
36
38
|
if (json === null || json === void 0 ? void 0 : json.ignoreList) {
|
|
37
39
|
if (Array.isArray(json.ignoreList)) {
|
|
@@ -70,6 +72,28 @@ class LintConfig {
|
|
|
70
72
|
if ((json === null || json === void 0 ? void 0 : json.noEncodedPasswords) !== false) {
|
|
71
73
|
this.lineLintRules.push(line_1.noEncodedPasswords);
|
|
72
74
|
}
|
|
75
|
+
// The default list is seeded only when the setting is absent, so an empty
|
|
76
|
+
// array is an explicit "report nothing" rather than a fallback to the
|
|
77
|
+
// defaults - the same contract `ignoredMacros` follows.
|
|
78
|
+
if ((json === null || json === void 0 ? void 0 : json.bannedStrings) === undefined) {
|
|
79
|
+
this.bannedStrings.push(...utils_1.defaultBannedStrings);
|
|
80
|
+
}
|
|
81
|
+
else if (!Array.isArray(json.bannedStrings)) {
|
|
82
|
+
throw new Error(`Property "bannedStrings" can only be an array of strings.`);
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
json.bannedStrings.forEach((item) => {
|
|
86
|
+
if (typeof item === 'string' && item.length) {
|
|
87
|
+
this.bannedStrings.push(item);
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
throw new Error(`Property "bannedStrings" has invalid type of values. It can contain only non-empty strings.`);
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
if ((json === null || json === void 0 ? void 0 : json.noBannedStrings) !== false) {
|
|
95
|
+
this.lineLintRules.push(line_1.noBannedStrings);
|
|
96
|
+
}
|
|
73
97
|
this.lineLintRules.push(line_1.noTabs);
|
|
74
98
|
if ((json === null || json === void 0 ? void 0 : json.noTabs) === false || (json === null || json === void 0 ? void 0 : json.noTabIndentation) === false) {
|
|
75
99
|
this.lineLintRules.pop();
|
|
@@ -121,6 +145,25 @@ class LintConfig {
|
|
|
121
145
|
if ((json === null || json === void 0 ? void 0 : json.noNestedMacros) !== false) {
|
|
122
146
|
this.fileLintRules.push(file_1.noNestedMacros);
|
|
123
147
|
}
|
|
148
|
+
// Absence means the documented default, so a project that omits the key
|
|
149
|
+
// still gets the floor. A value below two switches the rule off, the way a
|
|
150
|
+
// zero `maxLineLength` does - and the value is still kept, so the macro
|
|
151
|
+
// rules read one-character names again.
|
|
152
|
+
if ((json === null || json === void 0 ? void 0 : json.minMacroNameLength) !== undefined) {
|
|
153
|
+
// A name length is a whole number of at least one. `isNaN` alone coerces,
|
|
154
|
+
// so it would accept "3" and true, and a fraction or a value below one
|
|
155
|
+
// would store a floor no name can be measured against - 1 is the
|
|
156
|
+
// documented way to switch the rule off.
|
|
157
|
+
if (typeof (json === null || json === void 0 ? void 0 : json.minMacroNameLength) !== 'number' ||
|
|
158
|
+
!Number.isInteger(json.minMacroNameLength) ||
|
|
159
|
+
json.minMacroNameLength < 1) {
|
|
160
|
+
throw new Error(`Property "minMacroNameLength" must be an integer of 1 or more.`);
|
|
161
|
+
}
|
|
162
|
+
this.minMacroNameLength = json.minMacroNameLength;
|
|
163
|
+
}
|
|
164
|
+
if ((json === null || json === void 0 ? void 0 : json.minMacroNameLength) === undefined || json.minMacroNameLength > 1) {
|
|
165
|
+
this.fileLintRules.push(file_1.minMacroNameLength);
|
|
166
|
+
}
|
|
124
167
|
if ((json === null || json === void 0 ? void 0 : json.hasMacroParentheses) !== false) {
|
|
125
168
|
this.fileLintRules.push(file_1.hasMacroParentheses);
|
|
126
169
|
}
|
|
@@ -213,6 +256,9 @@ class LintConfig {
|
|
|
213
256
|
if (source.ignoredMacros === undefined) {
|
|
214
257
|
source.ignoredMacros = [...this.ignoredMacros];
|
|
215
258
|
}
|
|
259
|
+
if (source.bannedStrings === undefined) {
|
|
260
|
+
source.bannedStrings = [...this.bannedStrings];
|
|
261
|
+
}
|
|
216
262
|
return new LintConfig((0, mergeLintConfig_1.mergeLintConfig)(source, json));
|
|
217
263
|
}
|
|
218
264
|
}
|
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,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The strings reported by `noBannedStrings` when a project does not configure
|
|
3
|
+
* its own list.
|
|
4
|
+
*
|
|
5
|
+
* These are words that look like a log message. A comment or a `%put` that
|
|
6
|
+
* holds one turns up in a grep of the SAS log, and in any log scanner built on
|
|
7
|
+
* one, so a real problem is easy to lose among the matches that were only
|
|
8
|
+
* prose.
|
|
9
|
+
*
|
|
10
|
+
* Each entry ends a word: `error ` and `error:` carry the space or the colon
|
|
11
|
+
* that follows the word in a log line. A bare `error` would also match
|
|
12
|
+
* `error_flag`, `errors` and `saserror`, which are ordinary identifiers rather
|
|
13
|
+
* than log lookalikes, so the list is built from the delimited forms.
|
|
14
|
+
*/
|
|
15
|
+
export declare const defaultBannedStrings: string[];
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.defaultBannedStrings = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* The strings reported by `noBannedStrings` when a project does not configure
|
|
6
|
+
* its own list.
|
|
7
|
+
*
|
|
8
|
+
* These are words that look like a log message. A comment or a `%put` that
|
|
9
|
+
* holds one turns up in a grep of the SAS log, and in any log scanner built on
|
|
10
|
+
* one, so a real problem is easy to lose among the matches that were only
|
|
11
|
+
* prose.
|
|
12
|
+
*
|
|
13
|
+
* Each entry ends a word: `error ` and `error:` carry the space or the colon
|
|
14
|
+
* that follows the word in a log line. A bare `error` would also match
|
|
15
|
+
* `error_flag`, `errors` and `saserror`, which are ordinary identifiers rather
|
|
16
|
+
* than log lookalikes, so the list is built from the delimited forms.
|
|
17
|
+
*/
|
|
18
|
+
exports.defaultBannedStrings = ['error ', 'error:', 'warning ', 'warning:'];
|
|
19
|
+
//# sourceMappingURL=defaultBannedStrings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"defaultBannedStrings.js","sourceRoot":"","sources":["../../src/utils/defaultBannedStrings.ts"],"names":[],"mappings":";;;AAAA;;;;;;;;;;;;;GAaG;AACU,QAAA,oBAAoB,GAAG,CAAC,QAAQ,EAAE,QAAQ,EAAE,UAAU,EAAE,UAAU,CAAC,CAAA"}
|
package/utils/getLintConfig.d.ts
CHANGED
|
@@ -11,6 +11,7 @@ export declare const getDefaultHeader: () => string;
|
|
|
11
11
|
*/
|
|
12
12
|
export declare const DefaultLintConfiguration: {
|
|
13
13
|
allowedGremlins: never[];
|
|
14
|
+
bannedStrings: string[];
|
|
14
15
|
defaultHeader: string;
|
|
15
16
|
hasDoxygenHeader: boolean;
|
|
16
17
|
hasMacroNameInMend: boolean;
|
|
@@ -25,6 +26,8 @@ export declare const DefaultLintConfiguration: {
|
|
|
25
26
|
maxDataLineLength: number;
|
|
26
27
|
maxHeaderLineLength: number;
|
|
27
28
|
maxLineLength: number;
|
|
29
|
+
minMacroNameLength: number;
|
|
30
|
+
noBannedStrings: boolean;
|
|
28
31
|
noEncodedPasswords: boolean;
|
|
29
32
|
noGremlins: boolean;
|
|
30
33
|
noNestedCommentsInHeader: boolean;
|
package/utils/getLintConfig.js
CHANGED
|
@@ -20,6 +20,7 @@ const LintConfig_1 = require("../types/LintConfig");
|
|
|
20
20
|
const file_1 = require("@sasjs/utils/file");
|
|
21
21
|
const getProjectRoot_1 = require("./getProjectRoot");
|
|
22
22
|
const LineEndings_1 = require("../types/LineEndings");
|
|
23
|
+
const defaultBannedStrings_1 = require("./defaultBannedStrings");
|
|
23
24
|
const defaultIgnoredMacros_1 = require("./defaultIgnoredMacros");
|
|
24
25
|
const getDefaultHeader = () => `/**{lineEnding} @file{lineEnding} @brief <Your brief here>{lineEnding}{lineEnding} <h4> SAS Macros </h4>{lineEnding}{lineEnding}**/`;
|
|
25
26
|
exports.getDefaultHeader = getDefaultHeader;
|
|
@@ -33,6 +34,7 @@ exports.getDefaultHeader = getDefaultHeader;
|
|
|
33
34
|
*/
|
|
34
35
|
exports.DefaultLintConfiguration = {
|
|
35
36
|
allowedGremlins: [],
|
|
37
|
+
bannedStrings: [...defaultBannedStrings_1.defaultBannedStrings],
|
|
36
38
|
defaultHeader: (0, exports.getDefaultHeader)(),
|
|
37
39
|
hasDoxygenHeader: true,
|
|
38
40
|
hasMacroNameInMend: true,
|
|
@@ -47,6 +49,8 @@ exports.DefaultLintConfiguration = {
|
|
|
47
49
|
maxDataLineLength: 80,
|
|
48
50
|
maxHeaderLineLength: 80,
|
|
49
51
|
maxLineLength: 80,
|
|
52
|
+
minMacroNameLength: 3,
|
|
53
|
+
noBannedStrings: true,
|
|
50
54
|
noEncodedPasswords: true,
|
|
51
55
|
noGremlins: true,
|
|
52
56
|
noNestedCommentsInHeader: true,
|
|
@@ -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":";;;;;;;;;;;;;;;AA4DA,sCASC;AArED,gDAAuB;AACvB,4CAAmB;AACnB,oDAAgD;AAChD,4CAA4C;AAC5C,qDAAiD;AACjD,sDAAkD;AAClD,iEAA6D;AAC7D,iEAA6D;AAEtD,MAAM,gBAAgB,GAAG,GAAG,EAAE,CACnC,wIAAwI,CAAA;AAD7H,QAAA,gBAAgB,oBAC6G;AAE1I;;;;;;;GAOG;AACU,QAAA,wBAAwB,GAAG;IACtC,eAAe,EAAE,EAAE;IACnB,aAAa,EAAE,CAAC,GAAG,2CAAoB,CAAC;IACxC,aAAa,EAAE,IAAA,wBAAgB,GAAE;IACjC,gBAAgB,EAAE,IAAI;IACtB,kBAAkB,EAAE,IAAI;IACxB,mBAAmB,EAAE,IAAI;IACzB,uBAAuB,EAAE,KAAK;IAC9B,UAAU,EAAE,EAAE;IACd,eAAe,EAAE,EAAE;IACnB,aAAa,EAAE,CAAC,GAAG,2CAAoB,CAAC;IACxC,mBAAmB,EAAE,CAAC;IACtB,WAAW,EAAE,yBAAW,CAAC,GAAG;IAC5B,kBAAkB,EAAE,IAAI;IACxB,iBAAiB,EAAE,EAAE;IACrB,mBAAmB,EAAE,EAAE;IACvB,aAAa,EAAE,EAAE;IACjB,kBAAkB,EAAE,CAAC;IACrB,eAAe,EAAE,IAAI;IACrB,kBAAkB,EAAE,IAAI;IACxB,UAAU,EAAE,IAAI;IAChB,wBAAwB,EAAE,IAAI;IAC9B,cAAc,EAAE,IAAI;IACpB,wBAAwB,EAAE,KAAK;IAC/B,mBAAmB,EAAE,IAAI;IACzB,MAAM,EAAE,IAAI;IACZ,gBAAgB,EAAE,IAAI;IACtB,kBAAkB,EAAE,IAAI;IACxB,gBAAgB,EAAE,KAAK;IACvB,cAAc,EAAE,IAAI;IACpB,oBAAoB,EAAE,EAAE;IACxB,aAAa,EAAE,EAAE;IACjB,qBAAqB,EAAE,IAAI;CAC5B,CAAA;AAED;;;;GAIG;AACH,SAAsB,aAAa;;QACjC,MAAM,WAAW,GAAG,MAAM,IAAA,+BAAc,GAAE,CAAA;QAC1C,MAAM,gBAAgB,GAAG,WAAW,IAAI,YAAE,CAAC,OAAO,EAAE,CAAA;QACpD,MAAM,aAAa,GAAG,MAAM,IAAA,eAAQ,EAClC,cAAI,CAAC,IAAI,CAAC,gBAAgB,EAAE,YAAY,CAAC,CAC1C,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE;YACZ,OAAO,IAAI,CAAC,SAAS,CAAC,gCAAwB,CAAC,CAAA;QACjD,CAAC,CAAC,CAAA;QACF,OAAO,IAAI,uBAAU,CAAC,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,CAAA;IAClD,CAAC;CAAA"}
|
package/utils/index.d.ts
CHANGED
|
@@ -10,6 +10,7 @@ export * from './splitLines';
|
|
|
10
10
|
export * from './getIndicesOf';
|
|
11
11
|
export * from './getHeaderLinesCount';
|
|
12
12
|
export * from './getDataSectionsDetail';
|
|
13
|
+
export * from './defaultBannedStrings';
|
|
13
14
|
export * from './defaultIgnoredMacros';
|
|
14
15
|
export * from './headerMacros';
|
|
15
16
|
export * from './mergeLintConfig';
|
package/utils/index.js
CHANGED
|
@@ -26,6 +26,7 @@ __exportStar(require("./splitLines"), exports);
|
|
|
26
26
|
__exportStar(require("./getIndicesOf"), exports);
|
|
27
27
|
__exportStar(require("./getHeaderLinesCount"), exports);
|
|
28
28
|
__exportStar(require("./getDataSectionsDetail"), exports);
|
|
29
|
+
__exportStar(require("./defaultBannedStrings"), exports);
|
|
29
30
|
__exportStar(require("./defaultIgnoredMacros"), exports);
|
|
30
31
|
__exportStar(require("./headerMacros"), exports);
|
|
31
32
|
__exportStar(require("./mergeLintConfig"), exports);
|
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,0DAAuC;AACvC,yDAAsC;AACtC,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,0DAAuC;AACvC,yDAAsC;AACtC,yDAAsC;AACtC,iDAA8B;AAC9B,oDAAiC;AACjC,kDAA+B;AAC/B,sDAAmC;AACnC,oDAAiC;AACjC,8CAA2B"}
|
|
@@ -7,6 +7,10 @@ export interface MacroCall {
|
|
|
7
7
|
lineNumber: number;
|
|
8
8
|
startColumnNumber: number;
|
|
9
9
|
endColumnNumber: number;
|
|
10
|
+
/** True when the token sits inside a single-quoted string. */
|
|
11
|
+
quoted?: boolean;
|
|
12
|
+
/** True when an opening parenthesis follows the name. */
|
|
13
|
+
hasParen?: boolean;
|
|
10
14
|
}
|
|
11
15
|
/**
|
|
12
16
|
* Options for the scan.
|
package/utils/parseMacroCalls.js
CHANGED
|
@@ -13,18 +13,19 @@ const embeddedLanguageProc = /^proc\s+(lua|groovy)\b/i;
|
|
|
13
13
|
const embeddedLanguageEnd = /^endsubmit\s*;/i;
|
|
14
14
|
const embeddedLanguageEndSearch = /endsubmit/i;
|
|
15
15
|
/**
|
|
16
|
-
* Finds the `%name` tokens inside
|
|
16
|
+
* Finds the `%name` tokens inside a region that is text rather than code - the
|
|
17
|
+
* argument of a masking function, or the body of a single-quoted string.
|
|
17
18
|
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
19
|
+
* Since the region is text, a quote or a comment marker in it opens nothing.
|
|
20
|
+
* The scan therefore recognises only the escapes `%(` / `%)` and a `%name`
|
|
21
|
+
* token, and it does not report a `%name:` label.
|
|
21
22
|
*
|
|
22
23
|
* @param {string} text - the whole file.
|
|
23
|
-
* @param {number} from - the offset at which the
|
|
24
|
+
* @param {number} from - the offset at which the region starts.
|
|
24
25
|
* @param {number} to - the offset at which it ends.
|
|
25
26
|
* @returns {{ name: string; offset: number }[]} the tokens, in order.
|
|
26
27
|
*/
|
|
27
|
-
const
|
|
28
|
+
const findNameTokens = (text, from, to) => {
|
|
28
29
|
const tokens = [];
|
|
29
30
|
let index = from;
|
|
30
31
|
while (index < to) {
|
|
@@ -35,8 +36,17 @@ const findMaskedCalls = (text, from, to) => {
|
|
|
35
36
|
}
|
|
36
37
|
const macroName = macroNameStart.exec(text.slice(index, to));
|
|
37
38
|
if (macroName) {
|
|
38
|
-
|
|
39
|
-
|
|
39
|
+
const after = text.slice(index + macroName[0].length, to);
|
|
40
|
+
const isLabel = after.startsWith(':');
|
|
41
|
+
if (!isLabel) {
|
|
42
|
+
// Reported, not filtered: whether a bare name in a string is a call
|
|
43
|
+
// is the reporting rule's decision, and a use that goes unseen costs
|
|
44
|
+
// a declaration.
|
|
45
|
+
tokens.push({
|
|
46
|
+
name: macroName[1],
|
|
47
|
+
offset: index,
|
|
48
|
+
hasParen: /^\s*\(/.test(after)
|
|
49
|
+
});
|
|
40
50
|
}
|
|
41
51
|
index += macroName[0].length;
|
|
42
52
|
continue;
|
|
@@ -108,8 +118,15 @@ const parseMacroCalls = (text, config, options) => {
|
|
|
108
118
|
index++;
|
|
109
119
|
continue;
|
|
110
120
|
}
|
|
111
|
-
// Single-quoted string -
|
|
112
|
-
//
|
|
121
|
+
// Single-quoted string - SAS resolves no macro trigger in it, so it is
|
|
122
|
+
// data. The scan matches straight to the next quote: SAS also treats a
|
|
123
|
+
// doubled quote as an escaped character, but a pair either closes and
|
|
124
|
+
// reopens the string or is skipped, and both leave every character after
|
|
125
|
+
// it in the same state, so modelling the escape changes nothing that can
|
|
126
|
+
// be observed here. An apostrophe inside a double-quoted string is
|
|
127
|
+
// ordinary text. A caller that asks which macros the file uses still wants
|
|
128
|
+
// the invocations in it, because code that generates code submits this
|
|
129
|
+
// text later, so its invocations are reported.
|
|
113
130
|
if (char === "'") {
|
|
114
131
|
if (inDoubleQuote) {
|
|
115
132
|
columnNumber++;
|
|
@@ -119,10 +136,6 @@ const parseMacroCalls = (text, config, options) => {
|
|
|
119
136
|
let end = index + 1;
|
|
120
137
|
while (end < length) {
|
|
121
138
|
if (text[end] === "'") {
|
|
122
|
-
if (text[end + 1] === "'") {
|
|
123
|
-
end += 2;
|
|
124
|
-
continue;
|
|
125
|
-
}
|
|
126
139
|
end++;
|
|
127
140
|
break;
|
|
128
141
|
}
|
|
@@ -130,6 +143,17 @@ const parseMacroCalls = (text, config, options) => {
|
|
|
130
143
|
break;
|
|
131
144
|
end++;
|
|
132
145
|
}
|
|
146
|
+
findNameTokens(text, index + 1, end).forEach((token) => {
|
|
147
|
+
advanceTo(token.offset);
|
|
148
|
+
calls.push({
|
|
149
|
+
name: token.name,
|
|
150
|
+
lineNumber,
|
|
151
|
+
startColumnNumber: columnNumber,
|
|
152
|
+
endColumnNumber: columnNumber + 1 + token.name.length,
|
|
153
|
+
quoted: true,
|
|
154
|
+
hasParen: token.hasParen
|
|
155
|
+
});
|
|
156
|
+
});
|
|
133
157
|
advanceTo(end);
|
|
134
158
|
continue;
|
|
135
159
|
}
|
|
@@ -203,7 +227,7 @@ const parseMacroCalls = (text, config, options) => {
|
|
|
203
227
|
// formatter delete a declaration the file needs.
|
|
204
228
|
const argumentStart = index + maskingFunction[0].length;
|
|
205
229
|
advanceTo(argumentStart);
|
|
206
|
-
|
|
230
|
+
findNameTokens(text, argumentStart, end).forEach((token) => {
|
|
207
231
|
advanceTo(token.offset);
|
|
208
232
|
calls.push({
|
|
209
233
|
name: token.name,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parseMacroCalls.js","sourceRoot":"","sources":["../../src/utils/parseMacroCalls.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"parseMacroCalls.js","sourceRoot":"","sources":["../../src/utils/parseMacroCalls.ts"],"names":[],"mappings":"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"}
|