chore(lint): enable noMacroIfInOpenCode and move the %if blocks into macros #425

Merged
allan merged 1 commits from fix/lint-no-macro-if-in-open-code into release/7.22 2026-11-01 10:47:45 +00:00
Collaborator

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` - 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.
hermes added 1 commit 2026-11-01 10:42:09 +00:00
chore(lint): require %if to sit inside a macro definition
Build / Build-and-ng-test (pull_request) Successful in 6m23s
Lighthouse Checks / lighthouse (pull_request) Successful in 20m28s
Build / Build-and-test-development (pull_request) Successful in 31m26s
9f2cd24b20
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.22 2026-11-01 10:47:45 +00:00
allan deleted branch fix/lint-no-macro-if-in-open-code 2026-11-01 10:47:45 +00:00
Sign in to join this conversation.