Enables noMacroIfInOpenCode and promotes it to error - the last rule that was off. It found 19 sites in 9 files, and fixing them is a real change rather than a config flip, because open-code %if is a SAS 9.4M5 feature: the service bodies were relying on a version the requirements page does not claim (it says 9.4M3).
What was done with each site
Six blocks become a small macro called unconditionally, which is where the conditional then lives:
drop_cas_tables - services/admin/demodata.sas
add_derived_cols - services/editors/getdata.sas
build_sasdata4 - services/editors/getdata.test.sas (test 4's two blocks)
The two makedata STPs get the same treatment: check_outfmt, check_keepme, emit_response, self_destruct. Their call symputx writes are now explicit about the global symbol table ('G'), because a %let - or a plain symput - inside a wrapper is local to it, and &keepme / &OUTFMT are read further down the program.
Two guards are removed rather than wrapped. stagedata and getrawdata ran proc sql ... select name into: derived_droplist only when there were derived variables; the variable is %let blank just above, and with no derived variables the select returns no rows, so the guard did nothing. Running it unconditionally is equivalent. The drop that consumes the list keeps its conditional, now inside a macro and tested with %trim, because a zero-row select into can leave a single blank in the variable - %length would then read 1 and emit a bare drop ;.
validatefilter's two %let branches become one data step with call symputx, so targetds_val lands in the global table that the following %mp_filterstore reads (a %let inside a wrapper would not have reached it).
Tests on sas.4gl.io
$ sasjs cbd -t server-lint
✔ Services have been successfully deployed to https://sas.4gl.io
$ sasjs request services/admin/makedata -d deploy/makeDataServer.json -t server-lint
[{'STATUS': 'SUCCESS', 'MESSAGE': 'Data Controller Config created by sasjssrv', ...}]
$ sasjs test -t server-lint
ℹ Tests provided results: 64/64 (100%)
Tests that pass: 64/64 (100%)
Scratch appLoc, seeded with makedata (which itself exercises the emit_response and check_keepme wrappers). The derived-variable paths these wrappers touch are covered by the suite: getdata test 4, stagedata tests 3 and 4, validatefilter, getcolvals test 1 and the mpe_derived test all configure derived variables and all pass.
Verification
npm run lint:sas: 0 warnings, 0 errors, All matched files use @sasjs/lint code style!
sasjs compile: succeeds.
Full SAS suite on sas.4gl.io: 64/64.
AGENTS.md now records the pattern (wrap the block, or resolve it in a data step) and the call symputx(...,'G') trap, since the next conditional added to a service body will hit it.
Enables `noMacroIfInOpenCode` and promotes it to `error` - the last rule that was off. It found 19 sites in 9 files, and fixing them is a real change rather than a config flip, because open-code `%if` is a SAS 9.4M5 feature: the service bodies were relying on a version the requirements page does not claim (it says 9.4M3).
## What was done with each site
- **Six blocks become a small macro called unconditionally**, which is where the conditional then lives:
- `drop_cas_tables` - services/admin/demodata.sas
- `add_derived_cols` - services/editors/getdata.sas
- `build_sasdata4` - services/editors/getdata.test.sas (test 4's two blocks)
- `drop_derived` - services/editors/stagedata.sas
- `build_staged` - services/public/getrawdata.sas
- `apply_view_limit` / `apply_edit_limit` / `apply_cell_limit` - db/migrations/20261003_row_and_cell_limits.sas
- **The two makedata STPs** get the same treatment: `check_outfmt`, `check_keepme`, `emit_response`, `self_destruct`. Their `call symputx` writes are now explicit about the global symbol table (`'G'`), because a `%let` - or a plain symput - inside a wrapper is local to it, and `&keepme` / `&OUTFMT` are read further down the program.
- **Two guards are removed rather than wrapped.** stagedata and getrawdata ran `proc sql ... select name into: derived_droplist` only when there were derived variables; the variable is `%let` blank just above, and with no derived variables the select returns no rows, so the guard did nothing. Running it unconditionally is equivalent. The `drop` that consumes the list keeps its conditional, now inside a macro and tested with `%trim`, because a zero-row `select into` can leave a single blank in the variable - `%length` would then read 1 and emit a bare `drop ;`.
- **validatefilter's two `%let` branches** become one data step with `call symputx`, so `targetds_val` lands in the global table that the following `%mp_filterstore` reads (a `%let` inside a wrapper would not have reached it).
## Tests on sas.4gl.io
```
$ sasjs cbd -t server-lint
✔ Services have been successfully deployed to https://sas.4gl.io
$ sasjs request services/admin/makedata -d deploy/makeDataServer.json -t server-lint
[{'STATUS': 'SUCCESS', 'MESSAGE': 'Data Controller Config created by sasjssrv', ...}]
$ sasjs test -t server-lint
ℹ Tests provided results: 64/64 (100%)
Tests that pass: 64/64 (100%)
```
Scratch appLoc, seeded with `makedata` (which itself exercises the `emit_response` and `check_keepme` wrappers). The derived-variable paths these wrappers touch are covered by the suite: getdata test 4, stagedata tests 3 and 4, validatefilter, getcolvals test 1 and the mpe_derived test all configure derived variables and all pass.
## Verification
- `npm run lint:sas`: 0 warnings, 0 errors, `All matched files use @sasjs/lint code style!`
- `sasjs compile`: succeeds.
- Full SAS suite on sas.4gl.io: 64/64.
`AGENTS.md` now records the pattern (wrap the block, or resolve it in a data step) and the `call symputx(...,'G')` trap, since the next conditional added to a service body will hit it.
Enables noMacroIfInOpenCode and promotes it to "error". Open-code %if is a SAS
9.4M5 feature, so the 19 sites it found in nine files were hiding a version
requirement the codebase does not document (the requirements page says 9.4M3).
Each is rewritten so the construct no longer appears in open code:
- Six blocks become a small macro called unconditionally, which is where the
conditional then lives: drop_cas_tables (demodata), add_derived_cols
(getdata), build_sasdata4 (getdata test 4), drop_derived (stagedata),
build_staged (getrawdata), and one per option in the 20261003 migration.
- Four blocks in the two makedata STPs get the same treatment: check_outfmt,
check_keepme, emit_response and self_destruct. Their `call symputx` writes
are now explicit about the global symbol table, because a %let or a plain
symput inside a wrapper would be local to it.
- Two guards in stagedata and getrawdata are removed rather than wrapped: the
SQL only selects into a list that is already %let blank, and with no derived
variables it returns no rows, so the guard was doing nothing. The trailing
drop keeps a conditional, now in a macro and tested with %trim, since a
zero-row select can leave a single blank in the variable.
- validatefilter's two %let branches become one data step with call symputx,
which writes to the global table the later %mp_filterstore reads.
Verified: `npm run lint:sas` clean with the rule as an error; `sasjs compile`
succeeds; on sas.4gl.io the full suite passes 64/64, including the derived
variable tests in getdata test 4, stagedata tests 3 and 4 and validatefilter,
which are the paths these wrappers touch.
allan
merged commit f3bbcc8a9f into release/7.222026-11-01 10:47:45 +00:00
allan
deleted branch fix/lint-no-macro-if-in-open-code2026-11-01 10:47:45 +00:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Enables
noMacroIfInOpenCodeand promotes it toerror- the last rule that was off. It found 19 sites in 9 files, and fixing them is a real change rather than a config flip, because open-code%ifis a SAS 9.4M5 feature: the service bodies were relying on a version the requirements page does not claim (it says 9.4M3).What was done with each site
drop_cas_tables- services/admin/demodata.sasadd_derived_cols- services/editors/getdata.sasbuild_sasdata4- services/editors/getdata.test.sas (test 4's two blocks)drop_derived- services/editors/stagedata.sasbuild_staged- services/public/getrawdata.sasapply_view_limit/apply_edit_limit/apply_cell_limit- db/migrations/20261003_row_and_cell_limits.sascheck_outfmt,check_keepme,emit_response,self_destruct. Theircall symputxwrites are now explicit about the global symbol table ('G'), because a%let- or a plain symput - inside a wrapper is local to it, and&keepme/&OUTFMTare read further down the program.proc sql ... select name into: derived_droplistonly when there were derived variables; the variable is%letblank just above, and with no derived variables the select returns no rows, so the guard did nothing. Running it unconditionally is equivalent. Thedropthat consumes the list keeps its conditional, now inside a macro and tested with%trim, because a zero-rowselect intocan leave a single blank in the variable -%lengthwould then read 1 and emit a baredrop ;.%letbranches become one data step withcall symputx, sotargetds_vallands in the global table that the following%mp_filterstorereads (a%letinside a wrapper would not have reached it).Tests on sas.4gl.io
Scratch appLoc, seeded with
makedata(which itself exercises theemit_responseandcheck_keepmewrappers). The derived-variable paths these wrappers touch are covered by the suite: getdata test 4, stagedata tests 3 and 4, validatefilter, getcolvals test 1 and the mpe_derived test all configure derived variables and all pass.Verification
npm run lint:sas: 0 warnings, 0 errors,All matched files use @sasjs/lint code style!sasjs compile: succeeds.AGENTS.mdnow records the pattern (wrap the block, or resolve it in a data step) and thecall symputx(...,'G')trap, since the next conditional added to a service body will hit it.