@backbencher/brain 0.0.0-stage → 0.1.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/CHANGELOG.md +298 -0
- package/EMBEDDINGS.md +355 -0
- package/LICENSE +21 -0
- package/README.md +415 -2
- package/bin/bb +13 -0
- package/dist/analysis-exclusions-7ZJDXOVZ.js +35 -0
- package/dist/brain-index-health-Q5APTJQL.js +17 -0
- package/dist/brain-relationship-enrichment-MYR2AM4F.js +558 -0
- package/dist/brain.sql-EIM4WS4D.js +29 -0
- package/dist/chunk-22V37YTK.js +1758 -0
- package/dist/chunk-2BSKD7O3.js +40 -0
- package/dist/chunk-2DE27DVC.js +1314 -0
- package/dist/chunk-2RRBMNBQ.js +322 -0
- package/dist/chunk-2WXYNLBA.js +29 -0
- package/dist/chunk-3CTFIJJB.js +17 -0
- package/dist/chunk-3ZTOHHSW.js +2104 -0
- package/dist/chunk-4H22TLOT.js +543 -0
- package/dist/chunk-5BVAH26D.js +340 -0
- package/dist/chunk-62DHLYW6.js +3427 -0
- package/dist/chunk-7AN7U5IZ.js +371 -0
- package/dist/chunk-7DRPNVIN.js +782 -0
- package/dist/chunk-7MBYT777.js +174 -0
- package/dist/chunk-AHDWGM7Q.js +236 -0
- package/dist/chunk-B5GEAAYF.js +818 -0
- package/dist/chunk-BEIZZ73B.js +1479 -0
- package/dist/chunk-BQ3OVQ3P.js +125 -0
- package/dist/chunk-D2Q4Y47A.js +53 -0
- package/dist/chunk-DIYAGQJL.js +34 -0
- package/dist/chunk-DWKXZ6IF.js +336 -0
- package/dist/chunk-DYM4FQEE.js +90 -0
- package/dist/chunk-ENW4BTBA.js +930 -0
- package/dist/chunk-EWMWK7WN.js +75 -0
- package/dist/chunk-EY5ZZUUA.js +626 -0
- package/dist/chunk-GBI7V7KY.js +69 -0
- package/dist/chunk-GEFFO6O2.js +152 -0
- package/dist/chunk-HJEWO5EV.js +247 -0
- package/dist/chunk-HQSPJDHI.js +613 -0
- package/dist/chunk-KAET47ZL.js +125 -0
- package/dist/chunk-KCDECRJY.js +440 -0
- package/dist/chunk-L5TDYKYK.js +76 -0
- package/dist/chunk-MLKGABMK.js +9 -0
- package/dist/chunk-NO3U3FJQ.js +201 -0
- package/dist/chunk-OWISBAOF.js +61 -0
- package/dist/chunk-QKTRECYS.js +1119 -0
- package/dist/chunk-QLK6ED4Q.js +235 -0
- package/dist/chunk-R2GW4F6R.js +19 -0
- package/dist/chunk-RCMTFSEF.js +87 -0
- package/dist/chunk-RM3PY6TG.js +376 -0
- package/dist/chunk-ROMM6KMC.js +433 -0
- package/dist/chunk-SGGCSHAX.js +12050 -0
- package/dist/chunk-SK6YVHFZ.js +211 -0
- package/dist/chunk-SM7RFQRO.js +214 -0
- package/dist/chunk-UGR4QIGS.js +67 -0
- package/dist/chunk-UQNQZXHY.js +711 -0
- package/dist/chunk-UUNHHX4M.js +13 -0
- package/dist/chunk-VL52PL2T.js +71 -0
- package/dist/chunk-WIG36E67.js +196 -0
- package/dist/chunk-XHK3WWGB.js +7436 -0
- package/dist/chunk-XTQM2IP3.js +2863 -0
- package/dist/chunk-ZE2LG76D.js +1296 -0
- package/dist/chunk-ZETTGPAW.js +6950 -0
- package/dist/cli/index.js +37 -0
- package/dist/config-HZAAYDP3.js +10 -0
- package/dist/content-search-7XIP555Q.js +593 -0
- package/dist/database-76RP44JG.js +9 -0
- package/dist/doctor-ANGETYMN.js +395 -0
- package/dist/edit-engine-CG72LDZD.js +51 -0
- package/dist/engine-BKEOGN7N.js +11 -0
- package/dist/engine-VIW6NSKO.js +18 -0
- package/dist/fingerprint-OBLIMZLP.js +33 -0
- package/dist/framework-intelligence-3LORTPOX.js +860 -0
- package/dist/freshness-3K6VU5OT.js +174 -0
- package/dist/graph-engine-HBSQNPTB.js +21 -0
- package/dist/graph_request-Q6NKB6N2.js +350 -0
- package/dist/impact_request-LYP672WE.js +68 -0
- package/dist/index-space-IUUN5WYV.js +15 -0
- package/dist/index-status-7OVGYSGW.js +34 -0
- package/dist/index.js +159 -0
- package/dist/instance-FO6QZRLH.js +19 -0
- package/dist/language-detection-DFCAU5DZ.js +11 -0
- package/dist/lsp/server.js +4301 -0
- package/dist/mcp/server.js +132 -0
- package/dist/onboard-DIQZV2NW.js +161 -0
- package/dist/override-companion-pass-XR7MHTP7.js +117 -0
- package/dist/parallel-indexer-FK2XKZXY.js +88 -0
- package/dist/pool-worker.js +1153 -0
- package/dist/project-health-6XPENVAA.js +94 -0
- package/dist/project-index-5SDPHITE.js +57 -0
- package/dist/project-registry-ZJ3DVUGO.js +355 -0
- package/dist/real-bash-analyzer-EJAU5LW5.js +2201 -0
- package/dist/real-c-analyzer-HBD3FNNH.js +5942 -0
- package/dist/real-clean-analyzer-4EEZLPV5.js +3470 -0
- package/dist/real-clojure-analyzer-Q55EBOO2.js +2818 -0
- package/dist/real-cpp-analyzer-IVVOD25R.js +4101 -0
- package/dist/real-csharp-analyzer-ZBNLNJZJ.js +3298 -0
- package/dist/real-css-analyzer-6XRQLUN7.js +15 -0
- package/dist/real-dart-analyzer-OLR72EFW.js +3944 -0
- package/dist/real-elixir-analyzer-MQWGAH2I.js +2192 -0
- package/dist/real-erlang-analyzer-7BVPQEHO.js +1731 -0
- package/dist/real-fsharp-analyzer-C2UPFNOA.js +2390 -0
- package/dist/real-go-analyzer-UKGI43YS.js +4126 -0
- package/dist/real-graphql-analyzer-VE2EEFII.js +1442 -0
- package/dist/real-groovy-analyzer-W26VACFW.js +2285 -0
- package/dist/real-haskell-analyzer-5CVVAJRX.js +2533 -0
- package/dist/real-hcl-analyzer-5ZT4U43N.js +725 -0
- package/dist/real-html-analyzer-JWNX3C6I.js +1759 -0
- package/dist/real-java-analyzer-GU42VBGQ.js +4677 -0
- package/dist/real-javascript-analyzer-QA4QOJ6W.js +14 -0
- package/dist/real-json-analyzer-MPA6PRXL.js +1640 -0
- package/dist/real-julia-analyzer-22CWYQMN.js +2085 -0
- package/dist/real-kotlin-analyzer-QDZJLXXU.js +3956 -0
- package/dist/real-lua-analyzer-G7NHLYKS.js +1957 -0
- package/dist/real-makefile-analyzer-OCY3GT5N.js +920 -0
- package/dist/real-markdown-analyzer-TEJK2JXJ.js +1215 -0
- package/dist/real-matlab-analyzer-Q5SWLN7Z.js +480 -0
- package/dist/real-nim-analyzer-YLXSK563.js +3603 -0
- package/dist/real-objective-c-analyzer-D23ACC2R.js +1518 -0
- package/dist/real-perl-analyzer-WWTRHYDT.js +1865 -0
- package/dist/real-php-analyzer-JNKJHTCL.js +9368 -0
- package/dist/real-powershell-analyzer-XRSNQERV.js +2915 -0
- package/dist/real-protobuf-analyzer-3OKUCZ7T.js +867 -0
- package/dist/real-python-analyzer-VXAFWI72.js +4880 -0
- package/dist/real-r-analyzer-ZGAIDVI4.js +2596 -0
- package/dist/real-ruby-analyzer-X2KTLVBH.js +2429 -0
- package/dist/real-rust-analyzer-OFFHPL2A.js +3359 -0
- package/dist/real-scala-analyzer-TSTW27J6.js +3293 -0
- package/dist/real-scss-analyzer-AVWYPSOQ.js +1515 -0
- package/dist/real-solidity-analyzer-UCI2KWUB.js +2608 -0
- package/dist/real-sql-analyzer-F77ZMQXR.js +3840 -0
- package/dist/real-svelte-analyzer-SF7NRUO5.js +2532 -0
- package/dist/real-swift-analyzer-MN5QT3QN.js +2311 -0
- package/dist/real-toml-analyzer-66L7BDCQ.js +929 -0
- package/dist/real-typescript-analyzer-6KEMVDWQ.js +14 -0
- package/dist/real-vb-analyzer-VICVPOP7.js +3145 -0
- package/dist/real-vba-analyzer-HXLTTHKM.js +466 -0
- package/dist/real-vue-analyzer-QXR56CFV.js +2680 -0
- package/dist/real-xml-analyzer-YL465ZJW.js +2405 -0
- package/dist/real-yaml-analyzer-SUBCBNSI.js +3528 -0
- package/dist/real-zig-analyzer-HIQLZMGY.js +2430 -0
- package/dist/regex-search-NJKHMZNO.js +239 -0
- package/dist/rules-ACHPB7QU.js +152 -0
- package/dist/scoped-path-MJYWJZQX.js +11 -0
- package/dist/setup-show-NT5ZHYNE.js +15 -0
- package/dist/setup-wizard-G5YYR5T6.js +24 -0
- package/dist/symbol-search-GZJIM2J2.js +16 -0
- package/dist/taint-KXUHHPIE.js +202 -0
- package/dist/taint-analyzer-N7YLZU3N.js +163 -0
- package/dist/tool-handlers-OJDC35DI.js +72 -0
- package/dist/tool-registry-BPEJNSZG.js +17 -0
- package/dist/tool-validation-Q57YMMBX.js +32 -0
- package/dist/type-inference-QMT2RMLV.js +13 -0
- package/dist/vector-binary-LMY4QGAN.js +19 -0
- package/dist/version-ABP2TUL2.js +9 -0
- package/dist/wasm-NMWO3WXY.js +16 -0
- package/docs/EMBEDDINGS.md +132 -0
- package/docs/README.md +178 -0
- package/docs/data/m10-baseline.json +602 -0
- package/docs/data/m10-benchmark-baseline.json +57 -0
- package/docs/data/m10-benchmark-last-run.json +127 -0
- package/docs/data/m10-final-audit.json +215 -0
- package/docs/data/m12-final-audit.json +138 -0
- package/docs/data/m12-surface-matrix.json +50 -0
- package/docs/data/public-tools.json +1902 -0
- package/docs/indexing/DEAD-ASSETS.md +88 -0
- package/docs/indexing/INDEXER-REVIEW.md +634 -0
- package/docs/indexing/PERFORMANCE-FINDINGS.md +106 -0
- package/docs/indexing/README.md +427 -0
- package/docs/indexing/discovery-matrix.md +41 -0
- package/docs/indexing/embeddings.md +325 -0
- package/docs/indexing/environment-variables.md +77 -0
- package/docs/indexing/performance.md +323 -0
- package/docs/indexing/resume.md +345 -0
- package/docs/indexing/troubleshooting.md +391 -0
- package/docs/man/bb.1 +190 -0
- package/docs/tools/bb_analyze.md +147 -0
- package/docs/tools/bb_annotate.md +110 -0
- package/docs/tools/bb_annotations.md +106 -0
- package/docs/tools/bb_ask.md +101 -0
- package/docs/tools/bb_cross_language.md +103 -0
- package/docs/tools/bb_doc.md +169 -0
- package/docs/tools/bb_edit.md +185 -0
- package/docs/tools/bb_graph.md +169 -0
- package/docs/tools/bb_health.md +156 -0
- package/docs/tools/bb_impact.md +117 -0
- package/docs/tools/bb_index.md +108 -0
- package/docs/tools/bb_migrate.md +135 -0
- package/docs/tools/bb_patterns.md +140 -0
- package/docs/tools/bb_search.md +150 -0
- package/docs/tools/bb_security.md +100 -0
- package/docs/tools/bb_visualize.md +184 -0
- package/grammars/README.md +57 -0
- package/grammars/THIRD-PARTY-NOTICES.md +50 -0
- package/grammars/tree-sitter-c/LICENSE +21 -0
- package/grammars/tree-sitter-clojure/LICENSE.md +21 -0
- package/grammars/tree-sitter-cpp/LICENSE +21 -0
- package/grammars/tree-sitter-csharp/LICENSE +21 -0
- package/grammars/tree-sitter-css/LICENSE +21 -0
- package/grammars/tree-sitter-css/README.md +21 -0
- package/grammars/tree-sitter-dart/LICENSE +20 -0
- package/grammars/tree-sitter-dart-upstream/LICENSE +20 -0
- package/grammars/tree-sitter-dart-upstream/README.md +18 -0
- package/grammars/tree-sitter-elixir/LICENSE +176 -0
- package/grammars/tree-sitter-elixir/README.md +11 -0
- package/grammars/tree-sitter-fsharp/LICENSE +21 -0
- package/grammars/tree-sitter-fsharp/README.md +45 -0
- package/grammars/tree-sitter-graphql/LICENSE +21 -0
- package/grammars/tree-sitter-graphql/README.md +106 -0
- package/grammars/tree-sitter-groovy/LICENSE +21 -0
- package/grammars/tree-sitter-groovy/README.md +4 -0
- package/grammars/tree-sitter-haskell/LICENSE +21 -0
- package/grammars/tree-sitter-haskell/README.md +394 -0
- package/grammars/tree-sitter-hcl/CHANGELOG.md +102 -0
- package/grammars/tree-sitter-hcl/LICENSE +201 -0
- package/grammars/tree-sitter-hcl/README.md +39 -0
- package/grammars/tree-sitter-html/LICENSE +21 -0
- package/grammars/tree-sitter-html/README.md +21 -0
- package/grammars/tree-sitter-julia/LICENSE +21 -0
- package/grammars/tree-sitter-julia/README.md +24 -0
- package/grammars/tree-sitter-kotlin/LICENSE +9 -0
- package/grammars/tree-sitter-lua/LICENSE.md +21 -0
- package/grammars/tree-sitter-lua/README.md +29 -0
- package/grammars/tree-sitter-make/LICENSE +21 -0
- package/grammars/tree-sitter-matlab/LICENSE +19 -0
- package/grammars/tree-sitter-matlab/README.md +99 -0
- package/grammars/tree-sitter-nim/LICENSE +373 -0
- package/grammars/tree-sitter-nim/README.md +46 -0
- package/grammars/tree-sitter-objc/LICENSE +21 -0
- package/grammars/tree-sitter-objc/README.md +20 -0
- package/grammars/tree-sitter-perl/LICENSE +7 -0
- package/grammars/tree-sitter-perl/README.md +100 -0
- package/grammars/tree-sitter-powershell/LICENSE +21 -0
- package/grammars/tree-sitter-powershell/README.md +7 -0
- package/grammars/tree-sitter-proto/LICENSE +21 -0
- package/grammars/tree-sitter-proto/README.md +105 -0
- package/grammars/tree-sitter-r/LICENSE +9 -0
- package/grammars/tree-sitter-r/README.md +66 -0
- package/grammars/tree-sitter-ruby/LICENSE +22 -0
- package/grammars/tree-sitter-scss/LICENSE +21 -0
- package/grammars/tree-sitter-scss/README.md +17 -0
- package/grammars/tree-sitter-solidity/LICENSE +7 -0
- package/grammars/tree-sitter-sql/LICENSE +21 -0
- package/grammars/tree-sitter-typescript/LICENSE +21 -0
- package/grammars/tree-sitter-vba/LICENSE +21 -0
- package/grammars/tree-sitter-vba/README.md +738 -0
- package/grammars/tree-sitter-vbnet/LICENSE +21 -0
- package/grammars/tree-sitter-vbnet/README.md +322 -0
- package/grammars/tree-sitter-xml/LICENSE +19 -0
- package/grammars/tree-sitter-yaml/LICENSE +20 -0
- package/grammars/tree-sitter-yaml/README.md +21 -0
- package/package.json +184 -4
- package/wasm/dart.wasm +0 -0
- package/wasm/tree-sitter-bash.wasm +0 -0
- package/wasm/tree-sitter-c.wasm +0 -0
- package/wasm/tree-sitter-c_sharp.wasm +0 -0
- package/wasm/tree-sitter-clean.wasm +0 -0
- package/wasm/tree-sitter-clojure.wasm +0 -0
- package/wasm/tree-sitter-core_schema.wasm +0 -0
- package/wasm/tree-sitter-cpp.wasm +0 -0
- package/wasm/tree-sitter-css.wasm +0 -0
- package/wasm/tree-sitter-dart.wasm +0 -0
- package/wasm/tree-sitter-elisp.wasm +0 -0
- package/wasm/tree-sitter-elixir.wasm +0 -0
- package/wasm/tree-sitter-elm.wasm +0 -0
- package/wasm/tree-sitter-embedded_template.wasm +0 -0
- package/wasm/tree-sitter-erlang.wasm +0 -0
- package/wasm/tree-sitter-fsharp.wasm +0 -0
- package/wasm/tree-sitter-fsharp_signature.wasm +0 -0
- package/wasm/tree-sitter-go.wasm +0 -0
- package/wasm/tree-sitter-graphql.wasm +0 -0
- package/wasm/tree-sitter-groovy.wasm +0 -0
- package/wasm/tree-sitter-haskell.wasm +0 -0
- package/wasm/tree-sitter-hcl.wasm +0 -0
- package/wasm/tree-sitter-html.wasm +0 -0
- package/wasm/tree-sitter-java.wasm +0 -0
- package/wasm/tree-sitter-javascript.wasm +0 -0
- package/wasm/tree-sitter-json.wasm +0 -0
- package/wasm/tree-sitter-json_schema.wasm +0 -0
- package/wasm/tree-sitter-julia.wasm +0 -0
- package/wasm/tree-sitter-kotlin.wasm +0 -0
- package/wasm/tree-sitter-lua.wasm +0 -0
- package/wasm/tree-sitter-make.wasm +0 -0
- package/wasm/tree-sitter-markdown.wasm +0 -0
- package/wasm/tree-sitter-markdown_inline.wasm +0 -0
- package/wasm/tree-sitter-matlab.wasm +0 -0
- package/wasm/tree-sitter-nim.wasm +0 -0
- package/wasm/tree-sitter-objc.wasm +0 -0
- package/wasm/tree-sitter-ocaml.wasm +0 -0
- package/wasm/tree-sitter-perl.wasm +0 -0
- package/wasm/tree-sitter-php.wasm +0 -0
- package/wasm/tree-sitter-php_only.wasm +0 -0
- package/wasm/tree-sitter-powershell.wasm +0 -0
- package/wasm/tree-sitter-protobuf.wasm +0 -0
- package/wasm/tree-sitter-python.wasm +0 -0
- package/wasm/tree-sitter-ql.wasm +0 -0
- package/wasm/tree-sitter-r.wasm +0 -0
- package/wasm/tree-sitter-rescript.wasm +0 -0
- package/wasm/tree-sitter-ruby.wasm +0 -0
- package/wasm/tree-sitter-rust.wasm +0 -0
- package/wasm/tree-sitter-scala.wasm +0 -0
- package/wasm/tree-sitter-scss.wasm +0 -0
- package/wasm/tree-sitter-solidity.wasm +0 -0
- package/wasm/tree-sitter-sql.wasm +0 -0
- package/wasm/tree-sitter-svelte.wasm +0 -0
- package/wasm/tree-sitter-swift.wasm +0 -0
- package/wasm/tree-sitter-systemrdl.wasm +0 -0
- package/wasm/tree-sitter-terraform.wasm +0 -0
- package/wasm/tree-sitter-tlaplus.wasm +0 -0
- package/wasm/tree-sitter-toml.wasm +0 -0
- package/wasm/tree-sitter-tsx.wasm +0 -0
- package/wasm/tree-sitter-typescript.wasm +0 -0
- package/wasm/tree-sitter-vba.wasm +0 -0
- package/wasm/tree-sitter-vbnet.wasm +0 -0
- package/wasm/tree-sitter-vue.wasm +0 -0
- package/wasm/tree-sitter-xml.wasm +0 -0
- package/wasm/tree-sitter-yaml.wasm +0 -0
- package/wasm/tree-sitter-zig.wasm +0 -0
- package/wasm/tree-sitter.wasm +0 -0
- package/wasm/tsx.wasm +0 -0
- package/wasm/typescript.wasm +0 -0
|
@@ -0,0 +1,2915 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ASTEngine
|
|
3
|
+
} from "./chunk-3ZTOHHSW.js";
|
|
4
|
+
import "./chunk-BQ3OVQ3P.js";
|
|
5
|
+
import "./chunk-SK6YVHFZ.js";
|
|
6
|
+
import "./chunk-2BSKD7O3.js";
|
|
7
|
+
import "./chunk-KAET47ZL.js";
|
|
8
|
+
import "./chunk-KCDECRJY.js";
|
|
9
|
+
import "./chunk-MLKGABMK.js";
|
|
10
|
+
|
|
11
|
+
// src/ast/real-powershell-analyzer.ts
|
|
12
|
+
var AUTOMATIC_VARIABLES = /* @__PURE__ */ new Set([
|
|
13
|
+
"this",
|
|
14
|
+
"_",
|
|
15
|
+
"psitem",
|
|
16
|
+
"args",
|
|
17
|
+
"input",
|
|
18
|
+
"psscriptroot",
|
|
19
|
+
"pscommandpath",
|
|
20
|
+
// `psmodulepath` IS NOT IN THIS SET, and its absence is a measured correction: the
|
|
21
|
+
// automatic variable is `$env:PSModulePath`, which the scope-prefix guard already keeps
|
|
22
|
+
// out of the declaration space, while `$PSModulePath` is not on
|
|
23
|
+
// about_Automatic_Variables at all. The list carried the bare spelling, so
|
|
24
|
+
// `ConsoleHost.Tests.ps1:403`'s `$psModulePath = & $powershell …` — a real local — declared
|
|
25
|
+
// nothing and every read of it was suppressed. Two rows over the whole admitted
|
|
26
|
+
// population, both real declarations.
|
|
27
|
+
"psversiontable",
|
|
28
|
+
"host",
|
|
29
|
+
"pid",
|
|
30
|
+
"lastexitcode",
|
|
31
|
+
"error",
|
|
32
|
+
"home",
|
|
33
|
+
"pwd",
|
|
34
|
+
"myinvocation",
|
|
35
|
+
"matches",
|
|
36
|
+
"null",
|
|
37
|
+
"true",
|
|
38
|
+
"false",
|
|
39
|
+
"ofs",
|
|
40
|
+
"shellid",
|
|
41
|
+
"stacktrace",
|
|
42
|
+
"executioncontext",
|
|
43
|
+
"psculture",
|
|
44
|
+
"psuiculture",
|
|
45
|
+
"psdebugcontext",
|
|
46
|
+
"psedition",
|
|
47
|
+
"iscoreclr",
|
|
48
|
+
"islinux",
|
|
49
|
+
"ismacos",
|
|
50
|
+
"iswindows",
|
|
51
|
+
"consolefilename",
|
|
52
|
+
"consoleverbosity",
|
|
53
|
+
"enabledexperimentalfeatures",
|
|
54
|
+
"errorview",
|
|
55
|
+
"maximumhistorycount",
|
|
56
|
+
"nestedpromptlevel",
|
|
57
|
+
"outputencoding",
|
|
58
|
+
// M60 #278 / ontology review 2026-10-27: `$PSHOME` and `$PROFILE` are on the SAME
|
|
59
|
+
// documented page (about_Automatic_Variables) as `$PWD` and `$SHELLID`, which this
|
|
60
|
+
// list already carries — and they were missing. `& "$PSHOME/pwsh"` therefore
|
|
61
|
+
// published `uses -> PSHOME` for an engine binding while `$PID` in the same file
|
|
62
|
+
// was suppressed, and F5 scored the row as an unexplained read against an oracle
|
|
63
|
+
// that had correctly refused it. The set is the documentation's, so the fix is to
|
|
64
|
+
// finish reading it rather than to invent a rule: these are bindings the engine
|
|
65
|
+
// supplies, not names the file declares or reads from program data.
|
|
66
|
+
"pshome",
|
|
67
|
+
"profile",
|
|
68
|
+
// The call-site family: `$PSCmdlet` is the cmdlet instance, `$PSBoundParameters`
|
|
69
|
+
// its bound arguments, and the invocation-info/event members are all documented on
|
|
70
|
+
// about_Automatic_Variables beside the ones above. Measured on OutErrorVariable.
|
|
71
|
+
// Tests.ps1: `$PSCmdlet.writeobject("foo")` published `uses -> PSCmdlet` — an
|
|
72
|
+
// engine binding read as program data — on three lines, after the pipeline-read
|
|
73
|
+
// case started reading `$PSCmdlet.Member(...)` receivers.
|
|
74
|
+
"pscmdlet",
|
|
75
|
+
"psboundparameters",
|
|
76
|
+
"pscommandline",
|
|
77
|
+
"pscmdletbindingmetadata",
|
|
78
|
+
"senderinfo",
|
|
79
|
+
"event",
|
|
80
|
+
"eventargs",
|
|
81
|
+
"eventsubscriber",
|
|
82
|
+
"sourceeventargs",
|
|
83
|
+
"sender",
|
|
84
|
+
"invocationinfo",
|
|
85
|
+
"psinvocationinfo",
|
|
86
|
+
"scriptstacktrace"
|
|
87
|
+
]);
|
|
88
|
+
var FLOW_KEYWORDS = /* @__PURE__ */ new Set([
|
|
89
|
+
"break",
|
|
90
|
+
"continue",
|
|
91
|
+
"return",
|
|
92
|
+
"exit",
|
|
93
|
+
"throw",
|
|
94
|
+
"else",
|
|
95
|
+
"elseif",
|
|
96
|
+
"finally",
|
|
97
|
+
"trap",
|
|
98
|
+
"try",
|
|
99
|
+
"catch",
|
|
100
|
+
"in",
|
|
101
|
+
"notin",
|
|
102
|
+
"as",
|
|
103
|
+
"is",
|
|
104
|
+
"and",
|
|
105
|
+
"or",
|
|
106
|
+
"xor",
|
|
107
|
+
"not",
|
|
108
|
+
"band",
|
|
109
|
+
"bor",
|
|
110
|
+
"bxor",
|
|
111
|
+
"bnot",
|
|
112
|
+
"shl",
|
|
113
|
+
"shr",
|
|
114
|
+
"eq",
|
|
115
|
+
"ne",
|
|
116
|
+
"gt",
|
|
117
|
+
"ge",
|
|
118
|
+
"lt",
|
|
119
|
+
"le",
|
|
120
|
+
"like",
|
|
121
|
+
"notlike",
|
|
122
|
+
"match",
|
|
123
|
+
"notmatch",
|
|
124
|
+
"contains",
|
|
125
|
+
"notcontains",
|
|
126
|
+
"replace",
|
|
127
|
+
"split",
|
|
128
|
+
"join",
|
|
129
|
+
"f",
|
|
130
|
+
"r",
|
|
131
|
+
// A class member's accessor keyword: `set { … }` is not a cmdlet.
|
|
132
|
+
"get",
|
|
133
|
+
"set"
|
|
134
|
+
]);
|
|
135
|
+
var isSpaceCode = (code) => code === 32 || code >= 9 && code <= 13 || code === 160 || code === 5760 || code >= 8192 && code <= 8202 || code === 8232 || code === 8233 || code === 8239 || code === 8287 || code === 12288 || code === 65279;
|
|
136
|
+
var isAsciiLetter = (c) => {
|
|
137
|
+
if (c === void 0) return false;
|
|
138
|
+
const code = c.charCodeAt(0);
|
|
139
|
+
return code >= 65 && code <= 90 || code >= 97 && code <= 122;
|
|
140
|
+
};
|
|
141
|
+
var isPlausibleSymbolName = (name) => {
|
|
142
|
+
if (name.length === 0) return false;
|
|
143
|
+
for (let i = 0; i < name.length; i++) {
|
|
144
|
+
const c = name.charCodeAt(i);
|
|
145
|
+
const ok = c >= 48 && c <= 57 || c >= 65 && c <= 90 || c >= 97 && c <= 122 || c === 95 || c === 46 || c === 58 || c === 45;
|
|
146
|
+
if (!ok) return false;
|
|
147
|
+
}
|
|
148
|
+
return true;
|
|
149
|
+
};
|
|
150
|
+
var isNewSpelling = (name) => {
|
|
151
|
+
if (name.length !== 3) return false;
|
|
152
|
+
return (name.charCodeAt(0) | 32) === 110 && (name.charCodeAt(1) | 32) === 101 && (name.charCodeAt(2) | 32) === 119;
|
|
153
|
+
};
|
|
154
|
+
var stripLeadingDash = (text) => text.length > 0 && text.charCodeAt(0) === 45 ? text.slice(1) : text;
|
|
155
|
+
var stripTypeSuffixBrackets = (text) => {
|
|
156
|
+
let out = "";
|
|
157
|
+
let i = 0;
|
|
158
|
+
while (i < text.length) {
|
|
159
|
+
if (text.charCodeAt(i) === 91) {
|
|
160
|
+
let j = i + 1;
|
|
161
|
+
while (j < text.length && isSpaceCode(text.charCodeAt(j))) j++;
|
|
162
|
+
if (j < text.length && text.charCodeAt(j) === 93) {
|
|
163
|
+
i = j + 1;
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
out += text[i];
|
|
168
|
+
i++;
|
|
169
|
+
}
|
|
170
|
+
return out;
|
|
171
|
+
};
|
|
172
|
+
var hasTrailingExtension = (text) => {
|
|
173
|
+
const dot = text.lastIndexOf(".");
|
|
174
|
+
if (dot < 0 || dot === text.length - 1) return false;
|
|
175
|
+
for (let i = dot + 1; i < text.length; i++) {
|
|
176
|
+
const c = text.charCodeAt(i);
|
|
177
|
+
const alnum = c >= 48 && c <= 57 || c >= 65 && c <= 90 || c >= 97 && c <= 122;
|
|
178
|
+
if (!alnum) return false;
|
|
179
|
+
}
|
|
180
|
+
return true;
|
|
181
|
+
};
|
|
182
|
+
var sourcedPartName = (target) => {
|
|
183
|
+
const normalized = target.split("\\").join("/");
|
|
184
|
+
const base = normalized.slice(normalized.lastIndexOf("/") + 1);
|
|
185
|
+
if (base.length === 0) return null;
|
|
186
|
+
return isPlausibleSymbolName(base) ? base : null;
|
|
187
|
+
};
|
|
188
|
+
var stripOpenCommentDelim = (line) => {
|
|
189
|
+
let i = 0;
|
|
190
|
+
while (i < line.length && isSpaceCode(line.charCodeAt(i))) i++;
|
|
191
|
+
if (line.charCodeAt(i) === 60 && line.charCodeAt(i + 1) === 35) return line.slice(i + 2);
|
|
192
|
+
return line;
|
|
193
|
+
};
|
|
194
|
+
var stripCloseCommentDelim = (line) => {
|
|
195
|
+
let end = line.length;
|
|
196
|
+
while (end > 0 && isSpaceCode(line.charCodeAt(end - 1))) end--;
|
|
197
|
+
if (end >= 2 && line.charCodeAt(end - 1) === 62 && line.charCodeAt(end - 2) === 35) {
|
|
198
|
+
return line.slice(0, end - 2);
|
|
199
|
+
}
|
|
200
|
+
return line;
|
|
201
|
+
};
|
|
202
|
+
var stripCommentMarkerRun = (text) => {
|
|
203
|
+
let i = 0;
|
|
204
|
+
while (i < text.length && text.charCodeAt(i) === 35) i++;
|
|
205
|
+
if (i > 0 && i < text.length && isSpaceCode(text.charCodeAt(i))) i++;
|
|
206
|
+
return text.slice(i);
|
|
207
|
+
};
|
|
208
|
+
var NAMED_BLOCK_KEYWORDS = /* @__PURE__ */ new Set(["begin", "process", "end", "clean", "dynamicparam"]);
|
|
209
|
+
var LANGUAGE_KEYWORD_COMMANDS = /* @__PURE__ */ new Set([
|
|
210
|
+
"inlinescript",
|
|
211
|
+
"parallel",
|
|
212
|
+
"sequence",
|
|
213
|
+
"workflow",
|
|
214
|
+
"param",
|
|
215
|
+
"function",
|
|
216
|
+
"class",
|
|
217
|
+
"enum",
|
|
218
|
+
"filter",
|
|
219
|
+
"data",
|
|
220
|
+
"default",
|
|
221
|
+
"hidden",
|
|
222
|
+
"static",
|
|
223
|
+
"type",
|
|
224
|
+
"namespace",
|
|
225
|
+
"module",
|
|
226
|
+
"export"
|
|
227
|
+
]);
|
|
228
|
+
var DSC_KEYWORDS = /* @__PURE__ */ new Set(["configuration", "node"]);
|
|
229
|
+
var SWITCH_ARGUMENT_VALUES = /* @__PURE__ */ new Set([
|
|
230
|
+
"stop",
|
|
231
|
+
"silentlycontinue",
|
|
232
|
+
"continue",
|
|
233
|
+
"inquire",
|
|
234
|
+
"yes",
|
|
235
|
+
"no",
|
|
236
|
+
"none",
|
|
237
|
+
"ignore",
|
|
238
|
+
"true",
|
|
239
|
+
"false",
|
|
240
|
+
"0",
|
|
241
|
+
"1",
|
|
242
|
+
"pass",
|
|
243
|
+
"fail"
|
|
244
|
+
]);
|
|
245
|
+
var IMPORT_MODULE_MODULE_PARAMETERS = /* @__PURE__ */ new Set(["name", "assembly"]);
|
|
246
|
+
var QUALIFIED_PREFIXES = ["$global:", "$script:", "$env:", "$using:", "$local:", "$private:", "$variable:"];
|
|
247
|
+
var carriesQualifierByte = (t) => {
|
|
248
|
+
for (let i = 0; i < t.length; i++) {
|
|
249
|
+
const c = t.charCodeAt(i);
|
|
250
|
+
if (c === 46 || c === 92 || c === 47 || c === 58) return true;
|
|
251
|
+
}
|
|
252
|
+
return false;
|
|
253
|
+
};
|
|
254
|
+
var FILE_DEFINABLE_SCOPE_PREFIXES = ["global", "script", "local", "private", "function"];
|
|
255
|
+
var hasScopePrefix = (t, prefixes) => {
|
|
256
|
+
for (const p of prefixes) {
|
|
257
|
+
if (t.length <= p.length + 1) continue;
|
|
258
|
+
if (t.charCodeAt(p.length) !== 58) continue;
|
|
259
|
+
let eq = true;
|
|
260
|
+
for (let i = 0; i < p.length; i++) {
|
|
261
|
+
let c = t.charCodeAt(i);
|
|
262
|
+
if (c >= 65 && c <= 90) c += 32;
|
|
263
|
+
if (c !== p.charCodeAt(i)) {
|
|
264
|
+
eq = false;
|
|
265
|
+
break;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
if (eq) return true;
|
|
269
|
+
}
|
|
270
|
+
return false;
|
|
271
|
+
};
|
|
272
|
+
var hasFileDefinableScopePrefix = (t) => hasScopePrefix(t, FILE_DEFINABLE_SCOPE_PREFIXES);
|
|
273
|
+
var definableScopeOf = (name) => {
|
|
274
|
+
for (const p of FILE_DEFINABLE_SCOPE_PREFIXES) {
|
|
275
|
+
if (name.length <= p.length + 1) continue;
|
|
276
|
+
if (name.charCodeAt(p.length) !== 58) continue;
|
|
277
|
+
let eq = true;
|
|
278
|
+
for (let i = 0; i < p.length; i++) {
|
|
279
|
+
let c = name.charCodeAt(i);
|
|
280
|
+
if (c >= 65 && c <= 90) c += 32;
|
|
281
|
+
if (c !== p.charCodeAt(i)) {
|
|
282
|
+
eq = false;
|
|
283
|
+
break;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
if (!eq) continue;
|
|
287
|
+
const bare = name.slice(p.length + 1);
|
|
288
|
+
if (bare.length === 0) return null;
|
|
289
|
+
return { scope: p, bare };
|
|
290
|
+
}
|
|
291
|
+
return null;
|
|
292
|
+
};
|
|
293
|
+
var isAsciiUpper = (c) => c >= 65 && c <= 90;
|
|
294
|
+
var isRelativeProjectFilePath = (t) => {
|
|
295
|
+
const n = t.length;
|
|
296
|
+
if (n < 4) return false;
|
|
297
|
+
const isPs = (i) => {
|
|
298
|
+
const c = t.charCodeAt(i);
|
|
299
|
+
return c === 112 || c === 80;
|
|
300
|
+
};
|
|
301
|
+
const isS = (i) => {
|
|
302
|
+
const c = t.charCodeAt(i);
|
|
303
|
+
return c === 115 || c === 83;
|
|
304
|
+
};
|
|
305
|
+
const isDot = (i) => t.charCodeAt(i) === 46;
|
|
306
|
+
const isOne = (i) => t.charCodeAt(i) === 49;
|
|
307
|
+
let endsAdmitted = isDot(n - 4) && isPs(n - 3) && isS(n - 2) && isOne(n - 1);
|
|
308
|
+
if (!endsAdmitted && n >= 5 && isDot(n - 5) && isPs(n - 4) && isS(n - 3) && isOne(n - 1)) {
|
|
309
|
+
const c = t.charCodeAt(n - 2);
|
|
310
|
+
endsAdmitted = c === 109 || c === 77 || c === 100 || c === 68;
|
|
311
|
+
}
|
|
312
|
+
if (!endsAdmitted) {
|
|
313
|
+
const dot = t.lastIndexOf(".");
|
|
314
|
+
if (dot > 0 && dot < t.length - 1) {
|
|
315
|
+
let ext = "";
|
|
316
|
+
for (let i = dot + 1; i < t.length; i++) {
|
|
317
|
+
const c = t.charCodeAt(i);
|
|
318
|
+
if (c >= 65 && c <= 90) {
|
|
319
|
+
ext += String.fromCharCode(c + 32);
|
|
320
|
+
continue;
|
|
321
|
+
}
|
|
322
|
+
if (c >= 97 && c <= 122) {
|
|
323
|
+
ext += String.fromCharCode(c);
|
|
324
|
+
continue;
|
|
325
|
+
}
|
|
326
|
+
ext = "";
|
|
327
|
+
break;
|
|
328
|
+
}
|
|
329
|
+
if (ext.length > 0 && (PROGRAM_EXTENSIONS.has(ext) || ext === "dll" || ext === "nupkg" || ext === "zip")) {
|
|
330
|
+
endsAdmitted = true;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
if (!endsAdmitted) return false;
|
|
335
|
+
if (t.charCodeAt(0) === 47) return false;
|
|
336
|
+
if (t.charCodeAt(0) === 92) return false;
|
|
337
|
+
const colon = t.indexOf(":");
|
|
338
|
+
if (colon > 0) {
|
|
339
|
+
if (colon === 1) return false;
|
|
340
|
+
for (let i = 0; i < colon; i++) {
|
|
341
|
+
const c = t.charCodeAt(i);
|
|
342
|
+
const legal = c >= 97 && c <= 122 || c >= 65 && c <= 90 || c >= 48 && c <= 57 || c === 43 || c === 45 || c === 46;
|
|
343
|
+
if (!legal) return true;
|
|
344
|
+
}
|
|
345
|
+
return false;
|
|
346
|
+
}
|
|
347
|
+
return true;
|
|
348
|
+
};
|
|
349
|
+
var runtimeVariableTarget = (t) => {
|
|
350
|
+
if (t.length < 2) return null;
|
|
351
|
+
if (t.charCodeAt(0) !== 36) return null;
|
|
352
|
+
let i = 1;
|
|
353
|
+
while (i < t.length) {
|
|
354
|
+
const c = t.charCodeAt(i);
|
|
355
|
+
const isWord = c >= 65 && c <= 90 || c >= 97 && c <= 122 || c >= 48 && c <= 57 || c === 95;
|
|
356
|
+
if (!isWord) break;
|
|
357
|
+
i++;
|
|
358
|
+
}
|
|
359
|
+
if (i === 1) return null;
|
|
360
|
+
const name = t.slice(1, i);
|
|
361
|
+
const sep = t.charCodeAt(i);
|
|
362
|
+
if (sep === 58) return { name, isPath: false };
|
|
363
|
+
if (sep === 47 || sep === 92) return { name, isPath: true };
|
|
364
|
+
return null;
|
|
365
|
+
};
|
|
366
|
+
var PROGRAM_EXTENSIONS = /* @__PURE__ */ new Set(["exe", "cmd", "bat", "com", "msi"]);
|
|
367
|
+
var programNameOf = (t) => {
|
|
368
|
+
for (let i = 0; i < t.length; i++) {
|
|
369
|
+
const c = t.charCodeAt(i);
|
|
370
|
+
if (c === 47 || c === 92 || c === 36) return null;
|
|
371
|
+
}
|
|
372
|
+
const dot = t.lastIndexOf(".");
|
|
373
|
+
if (dot <= 0 || dot === t.length - 1) return null;
|
|
374
|
+
const ext = t.slice(dot + 1).toLowerCase();
|
|
375
|
+
return PROGRAM_EXTENSIONS.has(ext) ? t : null;
|
|
376
|
+
};
|
|
377
|
+
var isWordCode = (code) => code >= 48 && code <= 57 || code >= 65 && code <= 90 || code >= 97 && code <= 122 || code === 95 || code === 46;
|
|
378
|
+
var addTypeNamesIn = (text, roots, types) => {
|
|
379
|
+
const keywords = ["namespace", "class", "struct", "interface", "enum"];
|
|
380
|
+
let currentNamespace = "";
|
|
381
|
+
for (let i = 0; i < text.length; i++) {
|
|
382
|
+
for (const kw of keywords) {
|
|
383
|
+
if (i + kw.length > text.length) continue;
|
|
384
|
+
let ok = true;
|
|
385
|
+
for (let j = 0; j < kw.length; j++) {
|
|
386
|
+
if ((text.charCodeAt(i + j) | 32) !== kw.charCodeAt(j)) {
|
|
387
|
+
ok = false;
|
|
388
|
+
break;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
if (!ok) continue;
|
|
392
|
+
if (i > 0 && isWordCode(text.charCodeAt(i - 1))) continue;
|
|
393
|
+
let k = i + kw.length;
|
|
394
|
+
if (k < text.length && isWordCode(text.charCodeAt(k))) continue;
|
|
395
|
+
while (k < text.length && isSpaceCode(text.charCodeAt(k))) k++;
|
|
396
|
+
const start = k;
|
|
397
|
+
while (k < text.length && isWordCode(text.charCodeAt(k))) k++;
|
|
398
|
+
if (k === start) continue;
|
|
399
|
+
const name = text.slice(start, k);
|
|
400
|
+
const dot = name.indexOf(".");
|
|
401
|
+
roots.add((dot > 0 ? name.slice(0, dot) : name).toLowerCase());
|
|
402
|
+
if (kw === "namespace") currentNamespace = name;
|
|
403
|
+
else if (types !== null && currentNamespace.length > 0) {
|
|
404
|
+
types.set(`${currentNamespace}.${name}`.toLowerCase(), `${currentNamespace}.${name}`);
|
|
405
|
+
}
|
|
406
|
+
i = k - 1;
|
|
407
|
+
break;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
};
|
|
411
|
+
var externalReadingOf = (t) => {
|
|
412
|
+
if (t.charCodeAt(0) === 34 || t.charCodeAt(0) === 39) return null;
|
|
413
|
+
if (isRelativeProjectFilePath(t)) return null;
|
|
414
|
+
for (let i = 1; i < t.length; i++) {
|
|
415
|
+
if (t.charCodeAt(i) !== 45) continue;
|
|
416
|
+
if (isAsciiUpper(t.charCodeAt(0)) && isAsciiUpper(t.charCodeAt(i + 1))) return "cmdlet";
|
|
417
|
+
}
|
|
418
|
+
if (!carriesQualifierByte(t)) return null;
|
|
419
|
+
if (hasFileDefinableScopePrefix(t)) return null;
|
|
420
|
+
return "qualified";
|
|
421
|
+
};
|
|
422
|
+
var arbiterOwnerOf = (t) => {
|
|
423
|
+
const dot = t.lastIndexOf(".");
|
|
424
|
+
if (dot <= 0) return null;
|
|
425
|
+
return t.slice(0, dot);
|
|
426
|
+
};
|
|
427
|
+
var arbiterCanCheckOwner = (t) => {
|
|
428
|
+
const owner = arbiterOwnerOf(t);
|
|
429
|
+
if (owner === null) return true;
|
|
430
|
+
for (let i = 0; i < owner.length; i++) {
|
|
431
|
+
if (owner.charCodeAt(i) === 46) return true;
|
|
432
|
+
}
|
|
433
|
+
for (let i = 0; i < owner.length; i++) {
|
|
434
|
+
const c = owner.charCodeAt(i);
|
|
435
|
+
if (c === 58 || c === 47 || c === 32 || c === 9 || c === 10 || c === 13) return true;
|
|
436
|
+
}
|
|
437
|
+
return false;
|
|
438
|
+
};
|
|
439
|
+
var DOTNET_SYSTEM_NAMESPACES = /* @__PURE__ */ new Set([
|
|
440
|
+
"accessibility",
|
|
441
|
+
"buffers",
|
|
442
|
+
"codedom",
|
|
443
|
+
"collections",
|
|
444
|
+
"componentmodel",
|
|
445
|
+
"configuration",
|
|
446
|
+
"data",
|
|
447
|
+
"deployment",
|
|
448
|
+
"diagnostics",
|
|
449
|
+
"directoryservices",
|
|
450
|
+
"drawing",
|
|
451
|
+
"dynamic",
|
|
452
|
+
"formats",
|
|
453
|
+
"globalization",
|
|
454
|
+
"io",
|
|
455
|
+
"linq",
|
|
456
|
+
"management",
|
|
457
|
+
"media",
|
|
458
|
+
"net",
|
|
459
|
+
"numerics",
|
|
460
|
+
"printing",
|
|
461
|
+
"reflection",
|
|
462
|
+
"resources",
|
|
463
|
+
"runtime",
|
|
464
|
+
"security",
|
|
465
|
+
"servicemodel",
|
|
466
|
+
"serviceprocess",
|
|
467
|
+
"text",
|
|
468
|
+
"threading",
|
|
469
|
+
"transactions",
|
|
470
|
+
"web",
|
|
471
|
+
"windows",
|
|
472
|
+
"xml"
|
|
473
|
+
]);
|
|
474
|
+
var FRAMEWORK_NESTED_NAMESPACES = /* @__PURE__ */ new Set([
|
|
475
|
+
"system.management.automation.language",
|
|
476
|
+
"system.management.automation.runspaces",
|
|
477
|
+
"system.management.automation.provider",
|
|
478
|
+
"system.management.automation.security",
|
|
479
|
+
"system.management.automation.remoting",
|
|
480
|
+
"system.management.automation.host",
|
|
481
|
+
"system.management.automation.moduleanalysis",
|
|
482
|
+
"system.management.automation.debugging",
|
|
483
|
+
"system.io.compression",
|
|
484
|
+
"system.io.isolatedstorage",
|
|
485
|
+
"system.io.memorymappedfiles",
|
|
486
|
+
"system.io.packaging",
|
|
487
|
+
"system.io.pipes",
|
|
488
|
+
"system.io.enumeration",
|
|
489
|
+
"system.collections.generic",
|
|
490
|
+
"system.collections.concurrent",
|
|
491
|
+
"system.collections.specialized",
|
|
492
|
+
"system.collections.objectmodel",
|
|
493
|
+
"system.collections.immutable",
|
|
494
|
+
"system.collections.frozen",
|
|
495
|
+
"system.security.cryptography.x509certificates",
|
|
496
|
+
"system.security.cryptography.certificates",
|
|
497
|
+
"system.text.json",
|
|
498
|
+
"system.text.regularexpressions",
|
|
499
|
+
"system.text.unicode",
|
|
500
|
+
"system.text.encodings.web",
|
|
501
|
+
"system.diagnostics.codeanalysis",
|
|
502
|
+
"system.diagnostics.tracing",
|
|
503
|
+
"system.diagnostics.eventlog",
|
|
504
|
+
"system.diagnostics.metrics",
|
|
505
|
+
"system.diagnostics.symbolstore"
|
|
506
|
+
]);
|
|
507
|
+
var RESOLVED_FRAMEWORK_TYPES = /* @__PURE__ */ new Map([
|
|
508
|
+
// ── the documented TYPE ACCELERATORS (about_Type_Accelerators) ────────────────────────────────
|
|
509
|
+
["adsi", "System.DirectoryServices.DirectoryEntry"],
|
|
510
|
+
["adsisearcher", "System.DirectoryServices.DirectorySearcher"],
|
|
511
|
+
["alias", "System.Management.Automation.AliasAttribute"],
|
|
512
|
+
["allowemptycollection", "System.Management.Automation.AllowEmptyCollectionAttribute"],
|
|
513
|
+
["allowemptystring", "System.Management.Automation.AllowEmptyStringAttribute"],
|
|
514
|
+
["allow_null", "System.Management.Automation.AllowNullAttribute"],
|
|
515
|
+
["array", "System.Array"],
|
|
516
|
+
["bigint", "System.Numerics.BigInteger"],
|
|
517
|
+
["bool", "System.Boolean"],
|
|
518
|
+
["byte", "System.Byte"],
|
|
519
|
+
["char", "System.Char"],
|
|
520
|
+
["cimclass", "Microsoft.Management.Infrastructure.CimClass"],
|
|
521
|
+
["cimconverter", "Microsoft.Management.Infrastructure.CimConverter"],
|
|
522
|
+
["ciminstance", "Microsoft.Management.Infrastructure.CimInstance"],
|
|
523
|
+
["cimtype", "Microsoft.Management.Infrastructure.CimType"],
|
|
524
|
+
["cmdletbinding", "System.Management.Automation.CmdletBindingAttribute"],
|
|
525
|
+
["cultureinfo", "System.Globalization.CultureInfo"],
|
|
526
|
+
["datetime", "System.DateTime"],
|
|
527
|
+
["decimal", "System.Decimal"],
|
|
528
|
+
["double", "System.Double"],
|
|
529
|
+
["dsclocalconfigurationmanager", "Microsoft.PowerShell.DesiredStateConfiguration.LocalConfigurationManager"],
|
|
530
|
+
["dscproperty", "System.Management.Automation.DscPropertyAttribute"],
|
|
531
|
+
["dscresource", "System.Management.Automation.DscResourceAttribute"],
|
|
532
|
+
["float", "System.Single"],
|
|
533
|
+
["guid", "System.Guid"],
|
|
534
|
+
["hashtable", "System.Collections.Hashtable"],
|
|
535
|
+
["initialsessionstate", "System.Management.Automation.Runspaces.InitialSessionState"],
|
|
536
|
+
["int", "System.Int32"],
|
|
537
|
+
["int16", "System.Int16"],
|
|
538
|
+
["int32", "System.Int32"],
|
|
539
|
+
["int64", "System.Int64"],
|
|
540
|
+
["ipaddress", "System.Net.IPAddress"],
|
|
541
|
+
["ipendpoint", "System.Net.IPEndPoint"],
|
|
542
|
+
["long", "System.Int64"],
|
|
543
|
+
["mailaddress", "System.Net.Mail.MailAddress"],
|
|
544
|
+
["moduleinfo", "System.Management.Automation.PSModuleInfo"],
|
|
545
|
+
["nullable", "System.Nullable"],
|
|
546
|
+
["object", "System.Object"],
|
|
547
|
+
["objectsecurity", "System.Security.AccessControl.ObjectSecurity"],
|
|
548
|
+
["ordered", "System.Collections.Specialized.OrderedDictionary"],
|
|
549
|
+
["outputtype", "System.Management.Automation.OutputTypeAttribute"],
|
|
550
|
+
["parameter", "System.Management.Automation.ParameterAttribute"],
|
|
551
|
+
["physicaladdress", "System.Net.NetworkInformation.PhysicalAddress"],
|
|
552
|
+
["psobject", "System.Management.Automation.PSObject"],
|
|
553
|
+
["pscredential", "System.Management.Automation.PSCredential"],
|
|
554
|
+
["pscustomobject", "System.Management.Automation.PSCustomObject"],
|
|
555
|
+
["pslistmodifier", "System.Management.Automation.PSListModifier"],
|
|
556
|
+
["psprimitivedictionary", "System.Management.Automation.PSPrimitiveDictionary"],
|
|
557
|
+
["pstypenameattribute", "System.Management.Automation.PSTypeNameAttribute"],
|
|
558
|
+
["psvariable", "System.Management.Automation.PSVariable"],
|
|
559
|
+
["ref", "System.Management.Automation.PSReference"],
|
|
560
|
+
["regex", "System.Text.RegularExpressions.Regex"],
|
|
561
|
+
["runspace", "System.Management.Automation.Runspaces.Runspace"],
|
|
562
|
+
["runspacefactory", "System.Management.Automation.Runspaces.RunspaceFactory"],
|
|
563
|
+
["sbyte", "System.SByte"],
|
|
564
|
+
["scriptblock", "System.Management.Automation.ScriptBlock"],
|
|
565
|
+
["securestring", "System.Security.SecureString"],
|
|
566
|
+
["semver", "System.Management.Automation.SemanticVersion"],
|
|
567
|
+
["short", "System.Int16"],
|
|
568
|
+
["single", "System.Single"],
|
|
569
|
+
["string", "System.String"],
|
|
570
|
+
["switch", "System.Management.Automation.SwitchParameter"],
|
|
571
|
+
["timespan", "System.TimeSpan"],
|
|
572
|
+
["type", "System.Type"],
|
|
573
|
+
["uint", "System.UInt32"],
|
|
574
|
+
["uint16", "System.UInt16"],
|
|
575
|
+
["uint32", "System.UInt32"],
|
|
576
|
+
["uint64", "System.UInt64"],
|
|
577
|
+
["ulong", "System.UInt64"],
|
|
578
|
+
["uri", "System.Uri"],
|
|
579
|
+
["ushort", "System.UInt16"],
|
|
580
|
+
["validatecount", "System.Management.Automation.ValidateCountAttribute"],
|
|
581
|
+
["validatedrive", "System.Management.Automation.ValidateDriveAttribute"],
|
|
582
|
+
["validatelength", "System.Management.Automation.ValidateLengthAttribute"],
|
|
583
|
+
["validatenotnull", "System.Management.Automation.ValidateNotNullAttribute"],
|
|
584
|
+
["validatenotnullorempty", "System.Management.Automation.ValidateNotNullOrEmptyAttribute"],
|
|
585
|
+
["validatepattern", "System.Management.Automation.ValidatePatternAttribute"],
|
|
586
|
+
["validaterange", "System.Management.Automation.ValidateRangeAttribute"],
|
|
587
|
+
["validatescript", "System.Management.Automation.ValidateScriptAttribute"],
|
|
588
|
+
["validateset", "System.Management.Automation.ValidateSetAttribute"],
|
|
589
|
+
["version", "System.Version"],
|
|
590
|
+
["void", "System.Void"],
|
|
591
|
+
["wildcardpattern", "System.Management.Automation.WildcardPattern"],
|
|
592
|
+
["wmi", "System.Management.ManagementObject"],
|
|
593
|
+
["wmiclass", "System.Management.ManagementClass"],
|
|
594
|
+
["wmisearcher", "System.Management.ManagementObjectSearcher"],
|
|
595
|
+
["xml", "System.Xml.XmlDocument"],
|
|
596
|
+
// ── NOT accelerators: resolved by the implicit-`System.` attempt, or by the loaded-assembly
|
|
597
|
+
// search, to the framework type of that name. Cited per line, and only where the resolution is
|
|
598
|
+
// a documented fact rather than a coin toss (see the block comment's "deliberately not" list).
|
|
599
|
+
["bitconverter", "System.BitConverter"],
|
|
600
|
+
// System.BitConverter
|
|
601
|
+
["console", "System.Console"],
|
|
602
|
+
// System.Console (implicit `System.`)
|
|
603
|
+
["convert", "System.Convert"],
|
|
604
|
+
// System.Convert (implicit `System.`)
|
|
605
|
+
["datetimeoffset", "System.DateTimeOffset"],
|
|
606
|
+
// System.DateTimeOffset
|
|
607
|
+
["enum", "System.Enum"],
|
|
608
|
+
// System.Enum (implicit `System.`)
|
|
609
|
+
["environment", "System.Environment"],
|
|
610
|
+
// System.Environment (implicit `System.`)
|
|
611
|
+
["experimentalfeature", "System.Management.Automation.ExperimentalFeature"],
|
|
612
|
+
["gc", "System.GC"],
|
|
613
|
+
// System.GC (implicit `System.`)
|
|
614
|
+
["linebreakpoint", "System.Management.Automation.LineBreakpoint"],
|
|
615
|
+
["math", "System.Math"],
|
|
616
|
+
// System.Math (implicit `System.`)
|
|
617
|
+
["powershell", "System.Management.Automation.PowerShell"],
|
|
618
|
+
// assembly search: the PS host object
|
|
619
|
+
["proxycommand", "System.Management.Automation.ProxyCommand"],
|
|
620
|
+
["psserializer", "System.Management.Automation.PSSerializer"],
|
|
621
|
+
["securityprotocoltype", "System.Net.SecurityProtocolType"],
|
|
622
|
+
["servicepointmanager", "System.Net.ServicePointManager"],
|
|
623
|
+
["tuple", "System.Tuple"],
|
|
624
|
+
// System.Tuple (implicit `System.`)
|
|
625
|
+
["waithandle", "System.Threading.WaitHandle"]
|
|
626
|
+
]);
|
|
627
|
+
var classMembersOf = (map, literal) => {
|
|
628
|
+
const exact = map.get(literal);
|
|
629
|
+
if (exact !== void 0) return { name: literal, members: exact };
|
|
630
|
+
const folded = literal.toLowerCase();
|
|
631
|
+
for (const [name, members] of map) if (name.toLowerCase() === folded) return { name, members };
|
|
632
|
+
return void 0;
|
|
633
|
+
};
|
|
634
|
+
var usingScopeRootOf = (t) => {
|
|
635
|
+
if (t.length <= 6) return null;
|
|
636
|
+
if (t.charCodeAt(5) !== 58) return null;
|
|
637
|
+
for (let i = 0; i < 5; i++) {
|
|
638
|
+
const c = t.charCodeAt(i) | 32;
|
|
639
|
+
if (c !== "using".charCodeAt(i)) return null;
|
|
640
|
+
}
|
|
641
|
+
return t.slice(0, 5);
|
|
642
|
+
};
|
|
643
|
+
var moduleQualifiedCommandOf = (t) => {
|
|
644
|
+
const at = t.indexOf("\\");
|
|
645
|
+
if (at <= 0) return null;
|
|
646
|
+
if (t.indexOf("\\", at + 1) >= 0) return null;
|
|
647
|
+
for (let i = 0; i < t.length; i++) if (t.charCodeAt(i) === 58) return null;
|
|
648
|
+
const leaf = t.slice(at + 1);
|
|
649
|
+
if (leaf.length === 0) return null;
|
|
650
|
+
const dot = leaf.lastIndexOf(".");
|
|
651
|
+
if (dot > 0) {
|
|
652
|
+
let ext = leaf.slice(dot + 1).toLowerCase();
|
|
653
|
+
if (ext === "exe" || ext === "ps1" || ext === "psm1" || ext === "psd1" || ext === "dll" || ext === "cmd" || ext === "bat" || ext === "com" || ext === "msi") return null;
|
|
654
|
+
}
|
|
655
|
+
return t.slice(0, at);
|
|
656
|
+
};
|
|
657
|
+
var envProviderRootOf = (t) => {
|
|
658
|
+
if (t.length <= 4) return null;
|
|
659
|
+
if (t.charCodeAt(3) !== 58) return null;
|
|
660
|
+
const a = t.charCodeAt(0) | 32, b = t.charCodeAt(1) | 32, c = t.charCodeAt(2) | 32;
|
|
661
|
+
if (!(a === 101 && b === 110 && c === 118)) return null;
|
|
662
|
+
return t.slice(0, 3);
|
|
663
|
+
};
|
|
664
|
+
var dotNetShorthandRootOf = (t) => {
|
|
665
|
+
const dot = t.indexOf(".");
|
|
666
|
+
if (dot <= 0) return null;
|
|
667
|
+
const root = t.slice(0, dot);
|
|
668
|
+
let folded = "";
|
|
669
|
+
for (let i = 0; i < root.length; i++) {
|
|
670
|
+
const c = root.charCodeAt(i);
|
|
671
|
+
folded += String.fromCharCode(c >= 65 && c <= 90 ? c + 32 : c);
|
|
672
|
+
}
|
|
673
|
+
return DOTNET_SYSTEM_NAMESPACES.has(folded) ? root : null;
|
|
674
|
+
};
|
|
675
|
+
var isCommandPositionCall = (rel) => {
|
|
676
|
+
if (rel.type !== ASTEngine.RelationshipType.Calls) return false;
|
|
677
|
+
const md = rel.metadata ?? {};
|
|
678
|
+
if (md.methodCall === true) return false;
|
|
679
|
+
if (md.staticMemberCall === true) return false;
|
|
680
|
+
return true;
|
|
681
|
+
};
|
|
682
|
+
var commandReadingOf = (t) => {
|
|
683
|
+
if (t.charCodeAt(0) === 34 || t.charCodeAt(0) === 39) return null;
|
|
684
|
+
if (isRelativeProjectFilePath(t)) return null;
|
|
685
|
+
if (hasFileDefinableScopePrefix(t)) return null;
|
|
686
|
+
return "command";
|
|
687
|
+
};
|
|
688
|
+
var isTypeAccelerator = (declared) => {
|
|
689
|
+
const bare = stripTypeSuffixBrackets(declared).trim();
|
|
690
|
+
return TYPE_ACCELERATORS.has(bare.toLowerCase()) || TYPE_ACCELERATORS.has(bare);
|
|
691
|
+
};
|
|
692
|
+
var TYPE_ACCELERATORS = /* @__PURE__ */ new Set([
|
|
693
|
+
// the primitives and their aliases
|
|
694
|
+
"bool",
|
|
695
|
+
"byte",
|
|
696
|
+
"char",
|
|
697
|
+
"datetime",
|
|
698
|
+
"decimal",
|
|
699
|
+
"double",
|
|
700
|
+
"float",
|
|
701
|
+
"int",
|
|
702
|
+
"int16",
|
|
703
|
+
"int32",
|
|
704
|
+
"int64",
|
|
705
|
+
"long",
|
|
706
|
+
"sbyte",
|
|
707
|
+
"short",
|
|
708
|
+
"single",
|
|
709
|
+
"string",
|
|
710
|
+
"uint",
|
|
711
|
+
"uint16",
|
|
712
|
+
"uint32",
|
|
713
|
+
"uint64",
|
|
714
|
+
"ulong",
|
|
715
|
+
"ushort",
|
|
716
|
+
"void",
|
|
717
|
+
"bigint",
|
|
718
|
+
// collections and the object model
|
|
719
|
+
"array",
|
|
720
|
+
"hashtable",
|
|
721
|
+
"ordered",
|
|
722
|
+
"object",
|
|
723
|
+
"psobject",
|
|
724
|
+
"pscustomobject",
|
|
725
|
+
"pslistmodifier",
|
|
726
|
+
"psprimitivedictionary",
|
|
727
|
+
"pscredential",
|
|
728
|
+
"scriptblock",
|
|
729
|
+
"switch",
|
|
730
|
+
"type",
|
|
731
|
+
"ref",
|
|
732
|
+
"reftype",
|
|
733
|
+
"void",
|
|
734
|
+
"xml",
|
|
735
|
+
"regex",
|
|
736
|
+
"wildcardpattern",
|
|
737
|
+
// values the engine names
|
|
738
|
+
"cultureinfo",
|
|
739
|
+
"guid",
|
|
740
|
+
"ipaddress",
|
|
741
|
+
"mailaddress",
|
|
742
|
+
"semver",
|
|
743
|
+
"timespan",
|
|
744
|
+
"uri",
|
|
745
|
+
"version",
|
|
746
|
+
"nullstring",
|
|
747
|
+
// CIM / WMI
|
|
748
|
+
"cimclass",
|
|
749
|
+
"cimconverter",
|
|
750
|
+
"ciminstance",
|
|
751
|
+
"cimsession",
|
|
752
|
+
"cimtype",
|
|
753
|
+
"wmi",
|
|
754
|
+
"wmiclass",
|
|
755
|
+
"wmisearcher",
|
|
756
|
+
// attributes (metadata, skipped before this set, listed for completeness)
|
|
757
|
+
"alias",
|
|
758
|
+
"allowemptycollection",
|
|
759
|
+
"allowemptystring",
|
|
760
|
+
"allownull",
|
|
761
|
+
"argumentcompleter",
|
|
762
|
+
"argumentcompletions",
|
|
763
|
+
"cmdletbinding",
|
|
764
|
+
"dscresource",
|
|
765
|
+
"experimentaction",
|
|
766
|
+
"experimental",
|
|
767
|
+
"experimentalfeature",
|
|
768
|
+
"feature",
|
|
769
|
+
"flags",
|
|
770
|
+
"nocompatiblecommand",
|
|
771
|
+
"norunspaceaffinity",
|
|
772
|
+
"parameter",
|
|
773
|
+
"psdefaultvalue",
|
|
774
|
+
"pstypenameattribute",
|
|
775
|
+
"supportswildcards",
|
|
776
|
+
"validatecount",
|
|
777
|
+
"validatedrive",
|
|
778
|
+
"validatelength",
|
|
779
|
+
"validatenotnull",
|
|
780
|
+
"validatenotnullorempty",
|
|
781
|
+
"validatepattern",
|
|
782
|
+
"validaterange",
|
|
783
|
+
"validatescript",
|
|
784
|
+
"validateset",
|
|
785
|
+
"validatetrusteddata",
|
|
786
|
+
"validateuserdrive"
|
|
787
|
+
]);
|
|
788
|
+
var memberOverrides = (derived, base) => {
|
|
789
|
+
if (base.isHidden) return false;
|
|
790
|
+
if (base.isStatic) return false;
|
|
791
|
+
return derived.kind === base.kind;
|
|
792
|
+
};
|
|
793
|
+
var memberHeaderFacts = (node, nameNode) => {
|
|
794
|
+
const named = [];
|
|
795
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
796
|
+
const c = node.child(i);
|
|
797
|
+
if (!c || !c.isNamed) continue;
|
|
798
|
+
named.push({ type: c.type, text: c.text });
|
|
799
|
+
}
|
|
800
|
+
const modifiers = [];
|
|
801
|
+
let prefix = "";
|
|
802
|
+
for (let i = 0; i < named.length; i++) {
|
|
803
|
+
if (named[i].type !== "class_attribute") continue;
|
|
804
|
+
const prevType = i > 0 ? named[i - 1].type : "";
|
|
805
|
+
const nextType = named[i + 1]?.type ?? "";
|
|
806
|
+
const isNamePrefix = (prevType === "attribute" || prevType === "type_literal") && nextType === "simple_name";
|
|
807
|
+
if (isNamePrefix) prefix = named[i].text;
|
|
808
|
+
else modifiers.push(named[i].text.toLowerCase());
|
|
809
|
+
}
|
|
810
|
+
const name = prefix ? prefix + nameNode.text : nameNode.text;
|
|
811
|
+
return { name, modifiers: modifiers.join(" ") };
|
|
812
|
+
};
|
|
813
|
+
var RealPowershellAnalyzer = class {
|
|
814
|
+
analyze(rootNode, content, filePath) {
|
|
815
|
+
const symbols = [];
|
|
816
|
+
const relationships = [];
|
|
817
|
+
const imports = [];
|
|
818
|
+
const exports = [];
|
|
819
|
+
const exportedNames = /* @__PURE__ */ new Set();
|
|
820
|
+
const relKeys = /* @__PURE__ */ new Set();
|
|
821
|
+
const frameworks = /* @__PURE__ */ new Set(["PowerShell"]);
|
|
822
|
+
const patterns = /* @__PURE__ */ new Set();
|
|
823
|
+
const fileName = this.basenameOf(filePath);
|
|
824
|
+
const lines = content.split("\n");
|
|
825
|
+
symbols.push({
|
|
826
|
+
name: fileName,
|
|
827
|
+
kind: ASTEngine.SymbolKind.Module,
|
|
828
|
+
range: this.nodeToRange(rootNode),
|
|
829
|
+
visibility: "public",
|
|
830
|
+
metadata: { powershellSpecific: true, isScript: true, scriptPath: filePath }
|
|
831
|
+
});
|
|
832
|
+
let cyclomaticComplexity = 1;
|
|
833
|
+
let dscDepth = 0;
|
|
834
|
+
const dscBlocks = /* @__PURE__ */ new Set();
|
|
835
|
+
const interfaceBlocks = /* @__PURE__ */ new Set();
|
|
836
|
+
const nodeKey = (n) => `${n.startIndex}:${n.endIndex}`;
|
|
837
|
+
const dscOwner = /* @__PURE__ */ new Map();
|
|
838
|
+
const frames = [{ owner: fileName, scope: fileName, containerLine: 1 }];
|
|
839
|
+
const top = () => frames[frames.length - 1];
|
|
840
|
+
const atFileScope = () => frames.length === 1;
|
|
841
|
+
const baseClassByClass = /* @__PURE__ */ new Map();
|
|
842
|
+
const classDeclaredSpelling = /* @__PURE__ */ new Map();
|
|
843
|
+
const classMembers = /* @__PURE__ */ new Map();
|
|
844
|
+
const overrideCandidates = [];
|
|
845
|
+
const evidenceClassFor = (type, isImport = false) => {
|
|
846
|
+
if (isImport) return "import_based";
|
|
847
|
+
switch (type) {
|
|
848
|
+
case ASTEngine.RelationshipType.Contains:
|
|
849
|
+
case ASTEngine.RelationshipType.Extends:
|
|
850
|
+
case ASTEngine.RelationshipType.Implements:
|
|
851
|
+
case ASTEngine.RelationshipType.Exports:
|
|
852
|
+
return "structural";
|
|
853
|
+
case ASTEngine.RelationshipType.References:
|
|
854
|
+
// `part_of` is read from a DIRECTIVE — the dot-source operator is syntax that
|
|
855
|
+
// names the part, and the edge exists because the directive exists. That is
|
|
856
|
+
// the fleet's `import_based` class (erlang ER-D46: "both are directives"), and
|
|
857
|
+
// it is the class css's plain-file `@import` relabel carries. The branch is
|
|
858
|
+
// LIVE rather than dead: the `isImport` short-circuit above fires only for
|
|
859
|
+
// rows that pass `import: true`, which a `part_of` row does not.
|
|
860
|
+
case ASTEngine.RelationshipType.PartOf:
|
|
861
|
+
return "import_based";
|
|
862
|
+
default:
|
|
863
|
+
return "usage_contextual";
|
|
864
|
+
}
|
|
865
|
+
};
|
|
866
|
+
const addRel = (source, target, type, confidence, line, metadata = {}) => {
|
|
867
|
+
if (!source || !target) return;
|
|
868
|
+
const key = `${source}\0${type}\0${target}\0${line}`;
|
|
869
|
+
if (relKeys.has(key)) return;
|
|
870
|
+
relKeys.add(key);
|
|
871
|
+
const isImport = metadata.import === true;
|
|
872
|
+
relationships.push({
|
|
873
|
+
source,
|
|
874
|
+
target,
|
|
875
|
+
type,
|
|
876
|
+
confidence,
|
|
877
|
+
line_number: line,
|
|
878
|
+
// The caller's metadata comes LAST so an explicit `evidenceClass` at a call
|
|
879
|
+
// site wins over the default — that is how the `overrides` edges say `inferred`
|
|
880
|
+
// without every other call site having to repeat the reasoning.
|
|
881
|
+
metadata: { evidenceClass: evidenceClassFor(type, isImport), ...metadata }
|
|
882
|
+
});
|
|
883
|
+
};
|
|
884
|
+
const stripDollar = (text) => {
|
|
885
|
+
if (text.length < 2 || text.charCodeAt(0) !== 36) return text;
|
|
886
|
+
if (text.charCodeAt(1) === 123 && text.charCodeAt(text.length - 1) === 125) {
|
|
887
|
+
return text.slice(2, -1);
|
|
888
|
+
}
|
|
889
|
+
return text.slice(1);
|
|
890
|
+
};
|
|
891
|
+
const unquoteTarget = (text) => {
|
|
892
|
+
if (text.length < 2) return text;
|
|
893
|
+
const first = text.charCodeAt(0);
|
|
894
|
+
if (first !== 34 && first !== 39) return text;
|
|
895
|
+
if (text.charCodeAt(text.length - 1) !== first) return text;
|
|
896
|
+
return text.slice(1, -1);
|
|
897
|
+
};
|
|
898
|
+
const isQualified = (raw) => {
|
|
899
|
+
const lower = raw.toLowerCase();
|
|
900
|
+
return QUALIFIED_PREFIXES.some((p) => lower.startsWith(p));
|
|
901
|
+
};
|
|
902
|
+
const isDeclarableVariable = (text) => !isQualified(text) || definableScopeOf(stripDollar(text.trim())) !== null;
|
|
903
|
+
const isAutomatic = (name) => AUTOMATIC_VARIABLES.has(name.toLowerCase());
|
|
904
|
+
const symbolMap = /* @__PURE__ */ new Map();
|
|
905
|
+
const pushSymbol = (name, kind, node, line, meta = {}, parameters, ownerOverride) => {
|
|
906
|
+
const raw = name.trim();
|
|
907
|
+
if (raw === "") return null;
|
|
908
|
+
if (isQualified(raw)) return null;
|
|
909
|
+
const owner = ownerOverride ?? top().owner;
|
|
910
|
+
const clean = stripDollar(raw);
|
|
911
|
+
if (clean === "") return null;
|
|
912
|
+
if (kind === ASTEngine.SymbolKind.Variable && isAutomatic(clean)) return null;
|
|
913
|
+
const key = `${owner}\0${clean.toLowerCase()}\0${kind}`;
|
|
914
|
+
const existing = symbolMap.get(key);
|
|
915
|
+
if (existing) return existing;
|
|
916
|
+
const sym = {
|
|
917
|
+
name: clean,
|
|
918
|
+
kind,
|
|
919
|
+
range: this.nodeToRange(node),
|
|
920
|
+
scope: owner,
|
|
921
|
+
ownerName: owner,
|
|
922
|
+
visibility: "public",
|
|
923
|
+
parameters,
|
|
924
|
+
metadata: { powershellSpecific: true, ...meta }
|
|
925
|
+
};
|
|
926
|
+
if (typeof meta.isStatic === "boolean") sym.isStatic = meta.isStatic;
|
|
927
|
+
if (typeof meta.isAbstract === "boolean") sym.isAbstract = meta.isAbstract;
|
|
928
|
+
if (Array.isArray(meta.annotations)) sym.decorators = meta.annotations;
|
|
929
|
+
if (typeof meta.returnType === "string" && meta.returnType) sym.returnType = meta.returnType;
|
|
930
|
+
if (typeof meta.docstring === "string" && meta.docstring) sym.docstring = meta.docstring;
|
|
931
|
+
symbols.push(sym);
|
|
932
|
+
symbolMap.set(key, sym);
|
|
933
|
+
return sym;
|
|
934
|
+
};
|
|
935
|
+
const addContains = (target, line, kind) => {
|
|
936
|
+
const owner = top().owner;
|
|
937
|
+
if (!owner || owner === target) return;
|
|
938
|
+
if ((kind === void 0 || kind === ASTEngine.SymbolKind.Variable) && isAutomatic(target)) return;
|
|
939
|
+
addRel(owner, target, ASTEngine.RelationshipType.Contains, 1, line ?? top().containerLine, {});
|
|
940
|
+
};
|
|
941
|
+
const declareSymbol = (rawName, kind, node, line, meta = {}, parameters, containsLine = line) => {
|
|
942
|
+
const name = stripDollar(rawName);
|
|
943
|
+
if (name === "") return;
|
|
944
|
+
const scoped = kind === ASTEngine.SymbolKind.Variable ? definableScopeOf(name) : null;
|
|
945
|
+
const bare = scoped === null ? name : scoped.bare;
|
|
946
|
+
const scopeOwner = scoped !== null && (scoped.scope === "script" || scoped.scope === "global") ? fileName : void 0;
|
|
947
|
+
if (kind === ASTEngine.SymbolKind.Variable && isAutomatic(bare)) return;
|
|
948
|
+
if (kind === ASTEngine.SymbolKind.Variable && scoped === null && isQualified(rawName)) return;
|
|
949
|
+
const key = `${scopeOwner ?? top().owner}\0${bare.toLowerCase()}\0${kind}`;
|
|
950
|
+
if (symbolMap.has(key)) return;
|
|
951
|
+
pushSymbol(
|
|
952
|
+
bare,
|
|
953
|
+
kind,
|
|
954
|
+
node,
|
|
955
|
+
line,
|
|
956
|
+
scoped === null ? meta : { ...meta, scopeQualifier: scoped.scope },
|
|
957
|
+
parameters,
|
|
958
|
+
scopeOwner
|
|
959
|
+
);
|
|
960
|
+
addContains(bare, containsLine, kind);
|
|
961
|
+
};
|
|
962
|
+
const emitMemberUses = (node, kind) => {
|
|
963
|
+
const member = this.findChildByType(node, "member_name");
|
|
964
|
+
if (!member) return;
|
|
965
|
+
let leaf = member.text;
|
|
966
|
+
if (!leaf) return;
|
|
967
|
+
let quotedMemberName = false;
|
|
968
|
+
if (leaf.length >= 2) {
|
|
969
|
+
const q = leaf.charCodeAt(0);
|
|
970
|
+
if ((q === 34 || q === 39) && leaf.charCodeAt(leaf.length - 1) === q) {
|
|
971
|
+
const inner = leaf.slice(1, -1);
|
|
972
|
+
if (inner.length > 0 && inner.indexOf("$") < 0) {
|
|
973
|
+
leaf = inner;
|
|
974
|
+
quotedMemberName = true;
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
if (leaf.charCodeAt(0) === 36) {
|
|
979
|
+
const raw = stripDollar(leaf.trim());
|
|
980
|
+
const name = raw.length > 1 && raw.charCodeAt(0) === 64 ? raw.slice(1) : raw;
|
|
981
|
+
if (!name || isAutomatic(name) || !isPlausibleSymbolName(name)) return;
|
|
982
|
+
addRel(
|
|
983
|
+
top().scope,
|
|
984
|
+
name,
|
|
985
|
+
ASTEngine.RelationshipType.Uses,
|
|
986
|
+
0.9,
|
|
987
|
+
member.startPosition.row + 1,
|
|
988
|
+
{
|
|
989
|
+
powershellSpecific: true,
|
|
990
|
+
isMemberAccess: true,
|
|
991
|
+
isVariableReference: true,
|
|
992
|
+
isDynamicMemberName: true,
|
|
993
|
+
...kind === "write" ? { isMemberWrite: true } : { isMemberReference: true }
|
|
994
|
+
}
|
|
995
|
+
);
|
|
996
|
+
return;
|
|
997
|
+
}
|
|
998
|
+
let hasNameChar = false;
|
|
999
|
+
for (let i = 0; i < leaf.length; i++) {
|
|
1000
|
+
const c = leaf.charCodeAt(i);
|
|
1001
|
+
if (c >= 65 && c <= 90 || c >= 97 && c <= 122 || c === 95) {
|
|
1002
|
+
hasNameChar = true;
|
|
1003
|
+
break;
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
if (!hasNameChar || !isPlausibleSymbolName(leaf)) return;
|
|
1007
|
+
addRel(
|
|
1008
|
+
top().scope,
|
|
1009
|
+
leaf,
|
|
1010
|
+
ASTEngine.RelationshipType.Uses,
|
|
1011
|
+
kind === "write" ? 0.9 : 0.8,
|
|
1012
|
+
member.startPosition.row + 1,
|
|
1013
|
+
{
|
|
1014
|
+
powershellSpecific: true,
|
|
1015
|
+
isMemberAccess: true,
|
|
1016
|
+
// PSD14 — THE MEMBER'S NAME WAS WRITTEN IN QUOTES, so its punctuation is DATA. A quoted
|
|
1017
|
+
// name is how PowerShell spells a property whose name no identifier can carry
|
|
1018
|
+
// (`$build.triggerInfo.'pr.number'` is ONE property called `pr.number`), and the dot in
|
|
1019
|
+
// it is not a qualifier. The externality pass asks the arbiter's OWNER question about the
|
|
1020
|
+
// text before that dot — `pr` — which the project answers with a VARIABLE, so the row was
|
|
1021
|
+
// REFUTED for a claim the source never made. MEASURED: 2 whole-population rows. The row
|
|
1022
|
+
// and its `noDeclarationInFile` fact stay; only the name claim is withheld.
|
|
1023
|
+
...quotedMemberName ? { isQuotedMemberName: true } : {},
|
|
1024
|
+
...kind === "write" ? { isMemberWrite: true } : { isMemberReference: true }
|
|
1025
|
+
}
|
|
1026
|
+
);
|
|
1027
|
+
};
|
|
1028
|
+
const emitParameterDefaultReads = (sp) => {
|
|
1029
|
+
const def = findFirstChild(sp, "script_parameter_default");
|
|
1030
|
+
if (def) emitVariableReads(def, true, "isParameterDefault");
|
|
1031
|
+
};
|
|
1032
|
+
const emitVariableReads = (node, skipBlocks = false, marker) => {
|
|
1033
|
+
const work2 = [node];
|
|
1034
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1035
|
+
while (work2.length > 0) {
|
|
1036
|
+
const n = work2.pop();
|
|
1037
|
+
if (n.type === "comment") continue;
|
|
1038
|
+
if (skipBlocks && (n.type === "script_block_expression" || n.type === "named_block")) continue;
|
|
1039
|
+
if (skipBlocks && n.type === "element_access") continue;
|
|
1040
|
+
if (n.type === "expandable_here_string_literal" && marker !== "isHereStringInterpolation") continue;
|
|
1041
|
+
if (n.type === "variable" || n.type === "braced_variable") {
|
|
1042
|
+
if (seen.has(n.startIndex)) continue;
|
|
1043
|
+
seen.add(n.startIndex);
|
|
1044
|
+
const trimmed = n.text.trim();
|
|
1045
|
+
const rawName = stripDollar(trimmed);
|
|
1046
|
+
const name = rawName.length > 1 && rawName.charCodeAt(0) === 64 ? rawName.slice(1) : rawName;
|
|
1047
|
+
const isScopedName = isQualified(n.text) || isQualified(`$${name}`);
|
|
1048
|
+
if (!isScopedName && !isPlausibleSymbolName(name)) continue;
|
|
1049
|
+
if (!name || isAutomatic(name)) continue;
|
|
1050
|
+
const isScoped = isScopedName;
|
|
1051
|
+
if (isScoped && name.indexOf(":") < 0) continue;
|
|
1052
|
+
const scopedRead = isScoped ? definableScopeOf(name) : null;
|
|
1053
|
+
const scopedOwner = scopedRead === null ? null : scopedRead.scope === "script" || scopedRead.scope === "global" ? fileName : top().owner;
|
|
1054
|
+
const publishedName = scopedRead === null ? name : `${scopedOwner}.${scopedRead.bare}`;
|
|
1055
|
+
const lead = n.text.length - n.text.trimStart().length;
|
|
1056
|
+
let skippedLines = 0;
|
|
1057
|
+
for (let i = 0; i < lead; i++) {
|
|
1058
|
+
if (n.text.charCodeAt(i) === 10) skippedLines++;
|
|
1059
|
+
}
|
|
1060
|
+
addRel(
|
|
1061
|
+
top().scope,
|
|
1062
|
+
publishedName,
|
|
1063
|
+
ASTEngine.RelationshipType.Uses,
|
|
1064
|
+
0.9,
|
|
1065
|
+
n.startPosition.row + 1 + skippedLines,
|
|
1066
|
+
{
|
|
1067
|
+
powershellSpecific: true,
|
|
1068
|
+
isVariableReference: true,
|
|
1069
|
+
...marker ? { [marker]: true } : {},
|
|
1070
|
+
...scopedRead !== null ? { isScopedVariableRead: true, scopeQualifier: scopedRead.scope } : isScoped ? { isScopedEnvironmentRead: true } : {},
|
|
1071
|
+
// A read written in the BRACED spelling. The marker's job is to make the
|
|
1072
|
+
// normalization independently revertible: once `${a}` and `$a` are the
|
|
1073
|
+
// same string in the result, nothing distinguishes them, so a mutant for
|
|
1074
|
+
// "the braces come back" has nothing to key on and would be a no-op — a
|
|
1075
|
+
// mutant that cannot fail is decoration.
|
|
1076
|
+
...trimmed.charCodeAt(0) === 36 && trimmed.charCodeAt(1) === 123 ? { isBracedVariable: true } : {}
|
|
1077
|
+
}
|
|
1078
|
+
);
|
|
1079
|
+
continue;
|
|
1080
|
+
}
|
|
1081
|
+
if (n.type === "member_access") {
|
|
1082
|
+
emitMemberUses(n, "read");
|
|
1083
|
+
continue;
|
|
1084
|
+
}
|
|
1085
|
+
for (let i = n.namedChildCount - 1; i >= 0; i--) {
|
|
1086
|
+
const c = n.namedChild(i);
|
|
1087
|
+
if (c) work2.push(c);
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
};
|
|
1091
|
+
const emitAssignmentReads = (node) => {
|
|
1092
|
+
const work2 = [node];
|
|
1093
|
+
while (work2.length > 0) {
|
|
1094
|
+
const n = work2.pop();
|
|
1095
|
+
if (n.type === "command") continue;
|
|
1096
|
+
if (n.type === "element_access") continue;
|
|
1097
|
+
if (n.type === "expandable_here_string_literal") continue;
|
|
1098
|
+
if (n.type === "variable" || n.type === "braced_variable" || n.type === "member_access") {
|
|
1099
|
+
emitVariableReads(n, true, "isAssignmentRead");
|
|
1100
|
+
continue;
|
|
1101
|
+
}
|
|
1102
|
+
if (n.type === "script_block_expression" || n.type === "named_block") continue;
|
|
1103
|
+
for (let i = n.namedChildCount - 1; i >= 0; i--) {
|
|
1104
|
+
const c = n.namedChild(i);
|
|
1105
|
+
if (c) work2.push(c);
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
};
|
|
1109
|
+
const isBranch = (type) => type === "if_statement" || type === "elseif_clause" || type === "while_statement" || type === "do_statement" || type === "for_statement" || type === "foreach_statement" || type === "switch_statement" || type === "try_statement" || type === "catch_clause" || type === "trap_statement" || // A `switch` CASE is a decision point. `switch_clause` was never named by
|
|
1110
|
+
// this lane, so a 10-case switch measured a cyclomatic complexity of 1
|
|
1111
|
+
// (measured: 174 `switch_clause` nodes in the production draw, all
|
|
1112
|
+
// uncounted).
|
|
1113
|
+
type === "switch_clause";
|
|
1114
|
+
const findFirstChild = (node, type) => {
|
|
1115
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
1116
|
+
const child = node.namedChild(i);
|
|
1117
|
+
if (child && child.type === type) return child;
|
|
1118
|
+
}
|
|
1119
|
+
return null;
|
|
1120
|
+
};
|
|
1121
|
+
const findDescendant = (node, type, maxDepth = 6) => {
|
|
1122
|
+
let frontier = [node];
|
|
1123
|
+
for (let d = 0; d < maxDepth && frontier.length > 0; d++) {
|
|
1124
|
+
const next = [];
|
|
1125
|
+
for (const n of frontier) {
|
|
1126
|
+
if (n.type === type) return n;
|
|
1127
|
+
for (let i = n.namedChildCount - 1; i >= 0; i--) {
|
|
1128
|
+
const child = n.namedChild(i);
|
|
1129
|
+
if (child) next.push(child);
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
frontier = next;
|
|
1133
|
+
}
|
|
1134
|
+
return null;
|
|
1135
|
+
};
|
|
1136
|
+
const findDescendantVariable = (node) => findDescendant(node, "variable", 12);
|
|
1137
|
+
const nodeIsAVariableReference = (node) => node.type === "variable";
|
|
1138
|
+
const typeSpecText = (node) => {
|
|
1139
|
+
const specTextOf = (literal) => {
|
|
1140
|
+
const spec = findFirstChild(literal, "type_spec");
|
|
1141
|
+
if (spec) return spec.text;
|
|
1142
|
+
return this.stripBrackets(literal.text);
|
|
1143
|
+
};
|
|
1144
|
+
const direct = node.type === "type_literal" ? node : findFirstChild(node, "type_literal");
|
|
1145
|
+
if (direct) return specTextOf(direct);
|
|
1146
|
+
for (const list of this.collectChildrenByType(node, "attribute_list")) {
|
|
1147
|
+
for (const attr of this.collectChildrenByType(list, "attribute")) {
|
|
1148
|
+
const literal = findFirstChild(attr, "type_literal");
|
|
1149
|
+
if (literal) return specTextOf(literal);
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
return "";
|
|
1153
|
+
};
|
|
1154
|
+
const collectParameters = (list) => {
|
|
1155
|
+
const params = [];
|
|
1156
|
+
if (!list) return params;
|
|
1157
|
+
for (let i = 0; i < list.namedChildCount; i++) {
|
|
1158
|
+
const p = list.namedChild(i);
|
|
1159
|
+
if (!p) continue;
|
|
1160
|
+
if (p.type !== "script_parameter" && p.type !== "class_method_parameter") continue;
|
|
1161
|
+
const varNode = findFirstChild(p, "variable");
|
|
1162
|
+
if (!varNode) continue;
|
|
1163
|
+
const declared = typeSpecText(p);
|
|
1164
|
+
const def = findFirstChild(p, "script_parameter_default");
|
|
1165
|
+
const entry = {
|
|
1166
|
+
name: stripDollar(varNode.text),
|
|
1167
|
+
// An untyped parameter is not `any` by declaration — it is untyped.
|
|
1168
|
+
// `[int]$Default = 0` and `$Default = 0` are different contracts and
|
|
1169
|
+
// the store's `parameters` JSON is the only place either survives.
|
|
1170
|
+
type: declared || "untyped",
|
|
1171
|
+
isOptional: def !== null
|
|
1172
|
+
};
|
|
1173
|
+
if (def) {
|
|
1174
|
+
const val = def.namedChildCount > 1 ? def.namedChild(1) : null;
|
|
1175
|
+
if (val) entry.defaultValue = val.text;
|
|
1176
|
+
}
|
|
1177
|
+
params.push(entry);
|
|
1178
|
+
}
|
|
1179
|
+
return params;
|
|
1180
|
+
};
|
|
1181
|
+
const commandNameOf = (node) => {
|
|
1182
|
+
const field = node.childForFieldName?.("command_name");
|
|
1183
|
+
const fieldNode = field ?? null;
|
|
1184
|
+
const nameNode = fieldNode ?? findFirstChild(node, "command_name");
|
|
1185
|
+
if (nameNode) return unquoteTarget(nameNode.text);
|
|
1186
|
+
const expr = findFirstChild(node, "command_name_expr");
|
|
1187
|
+
if (expr) {
|
|
1188
|
+
const inner = findFirstChild(expr, "command_name");
|
|
1189
|
+
if (inner) return inner.text;
|
|
1190
|
+
return unquoteTarget(expr.text);
|
|
1191
|
+
}
|
|
1192
|
+
return "";
|
|
1193
|
+
};
|
|
1194
|
+
const commandArgs = (node, sourceNodes, onlyParameters) => {
|
|
1195
|
+
const out = [];
|
|
1196
|
+
const elements = findFirstChild(node, "command_elements");
|
|
1197
|
+
if (!elements) return out;
|
|
1198
|
+
const isValueToken = (c) => !!c && (c.type === "generic_token" || c.type === "string_literal" || c.type === "expandable_string_literal");
|
|
1199
|
+
for (let i = 0; i < elements.namedChildCount; i++) {
|
|
1200
|
+
const c = elements.namedChild(i);
|
|
1201
|
+
if (!c) continue;
|
|
1202
|
+
if (c.type === "command_parameter") {
|
|
1203
|
+
const next = elements.namedChild(i + 1);
|
|
1204
|
+
let flag = "";
|
|
1205
|
+
{
|
|
1206
|
+
const raw = this.stripQuotes(c.text).trim();
|
|
1207
|
+
let s = raw.length > 0 && raw.charCodeAt(0) === 45 ? raw.slice(1) : raw;
|
|
1208
|
+
const colon = s.indexOf(":");
|
|
1209
|
+
if (colon >= 0) s = s.slice(0, colon);
|
|
1210
|
+
flag = s.toLowerCase();
|
|
1211
|
+
}
|
|
1212
|
+
if (next && isValueToken(next) && !SWITCH_ARGUMENT_VALUES.has(next.text.toLowerCase()) && (!onlyParameters || onlyParameters.has(flag))) {
|
|
1213
|
+
out.push(this.stripQuotes(next.text));
|
|
1214
|
+
sourceNodes?.push(next);
|
|
1215
|
+
}
|
|
1216
|
+
continue;
|
|
1217
|
+
}
|
|
1218
|
+
if (isValueToken(c)) {
|
|
1219
|
+
out.push(this.stripQuotes(c.text));
|
|
1220
|
+
sourceNodes?.push(c);
|
|
1221
|
+
continue;
|
|
1222
|
+
}
|
|
1223
|
+
if (c.type === "array_literal_expression" || c.type === "unary_expression") {
|
|
1224
|
+
const lit = this.findQuoted(c);
|
|
1225
|
+
if (lit) {
|
|
1226
|
+
const value = this.stripQuotes(lit.text);
|
|
1227
|
+
const v2 = findDescendantVariable(c);
|
|
1228
|
+
if (v2 && value.trim() === v2.text) {
|
|
1229
|
+
out.push(stripDollar(v2.text));
|
|
1230
|
+
sourceNodes?.push(v2);
|
|
1231
|
+
} else {
|
|
1232
|
+
out.push(value);
|
|
1233
|
+
sourceNodes?.push(lit);
|
|
1234
|
+
}
|
|
1235
|
+
continue;
|
|
1236
|
+
}
|
|
1237
|
+
const v = findDescendantVariable(c);
|
|
1238
|
+
if (v) {
|
|
1239
|
+
out.push(stripDollar(v.text));
|
|
1240
|
+
sourceNodes?.push(v);
|
|
1241
|
+
continue;
|
|
1242
|
+
}
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
return out;
|
|
1246
|
+
};
|
|
1247
|
+
const looksLikeModulePath = (text) => text.length > 0 && (text.includes("/") || text.includes("\\") || hasTrailingExtension(text));
|
|
1248
|
+
const addImport = (moduleName, via, line, node) => {
|
|
1249
|
+
if (!moduleName) return;
|
|
1250
|
+
imports.push({
|
|
1251
|
+
module: moduleName,
|
|
1252
|
+
items: ["*"],
|
|
1253
|
+
isDefault: true,
|
|
1254
|
+
range: this.nodeToRange(node),
|
|
1255
|
+
metadata: { powershellSpecific: true, via }
|
|
1256
|
+
});
|
|
1257
|
+
const meta = { powershellSpecific: true, import: true, via, fileLevel: atFileScope() };
|
|
1258
|
+
addRel(top().scope, moduleName, ASTEngine.RelationshipType.Imports, 0.9, line, { ...meta });
|
|
1259
|
+
addRel(top().scope, moduleName, ASTEngine.RelationshipType.References, 0.9, line, { ...meta });
|
|
1260
|
+
};
|
|
1261
|
+
const isExportWildcard = (name) => {
|
|
1262
|
+
for (let i = 0; i < name.length; i++) {
|
|
1263
|
+
const code = name.charCodeAt(i);
|
|
1264
|
+
if (code === 42 || code === 63) return true;
|
|
1265
|
+
}
|
|
1266
|
+
return false;
|
|
1267
|
+
};
|
|
1268
|
+
const addExport = (name, via, line) => {
|
|
1269
|
+
if (!name || isExportWildcard(name)) return;
|
|
1270
|
+
if (exportedNames.has(name.toLowerCase())) return;
|
|
1271
|
+
exportedNames.add(name.toLowerCase());
|
|
1272
|
+
exports.push({ name, range: this.lineRange(line), metadata: { powershellSpecific: true, via } });
|
|
1273
|
+
addRel(top().scope, name, ASTEngine.RelationshipType.Exports, 1, line, {
|
|
1274
|
+
powershellSpecific: true,
|
|
1275
|
+
viaExportModuleMember: true,
|
|
1276
|
+
via,
|
|
1277
|
+
fileLevel: atFileScope()
|
|
1278
|
+
});
|
|
1279
|
+
};
|
|
1280
|
+
const detectFrameworks = (cmdName) => {
|
|
1281
|
+
const lower = cmdName.toLowerCase();
|
|
1282
|
+
if (lower.startsWith("get-az") || lower.startsWith("new-az") || lower.startsWith("set-az") || lower.startsWith("remove-az") || lower.startsWith("connect-az") || lower === "az" || lower.startsWith("az_")) frameworks.add("Azure Az Module");
|
|
1283
|
+
if (lower.startsWith("get-msol") || lower.startsWith("set-msol") || lower.startsWith("connect-msol"))
|
|
1284
|
+
frameworks.add("Azure AD / MSOnline");
|
|
1285
|
+
if (lower.startsWith("get-exo") || lower.startsWith("set-exo") || lower.startsWith("new-exo") || lower.startsWith("get-mailbox") || lower.startsWith("set-mailbox") || lower.startsWith("enable-mailbox")) frameworks.add("Exchange Online");
|
|
1286
|
+
if (lower.startsWith("get-sp") || lower.startsWith("set-sp") || lower.startsWith("new-sp") || lower.startsWith("connect-pnp") || lower.startsWith("get-pnp")) frameworks.add("SharePoint PnP");
|
|
1287
|
+
if (lower.startsWith("get-ad") || lower.startsWith("set-ad") || lower.startsWith("new-ad") || lower.startsWith("remove-ad"))
|
|
1288
|
+
frameworks.add("Active Directory");
|
|
1289
|
+
if (lower.startsWith("get-vm") || lower.startsWith("new-vm") || lower.startsWith("start-vm") || lower.startsWith("stop-vm"))
|
|
1290
|
+
frameworks.add("VMware / Hyper-V");
|
|
1291
|
+
if (lower.startsWith("invoke-rest") || lower.startsWith("invoke-web") || lower === "irm" || lower === "iwr")
|
|
1292
|
+
patterns.add("REST API Calls");
|
|
1293
|
+
if (lower.startsWith("write-") || lower.startsWith("read-")) patterns.add("I/O Operations");
|
|
1294
|
+
if (lower === "get-service" || lower === "stop-service" || lower === "start-service" || lower === "restart-service")
|
|
1295
|
+
patterns.add("Service Management");
|
|
1296
|
+
};
|
|
1297
|
+
const usingNamespacePrefixes = [];
|
|
1298
|
+
const typeNamesInFile = /* @__PURE__ */ new Set();
|
|
1299
|
+
const addTypeCreatedNames = /* @__PURE__ */ new Set();
|
|
1300
|
+
const addTypeDeclarations = /* @__PURE__ */ new Map();
|
|
1301
|
+
const recordAddTypeDeclaration = (full) => {
|
|
1302
|
+
if (full.length === 0) return;
|
|
1303
|
+
const key = full.toLowerCase();
|
|
1304
|
+
if (!addTypeDeclarations.has(key)) addTypeDeclarations.set(key, full);
|
|
1305
|
+
};
|
|
1306
|
+
{
|
|
1307
|
+
const stack = [rootNode];
|
|
1308
|
+
const hereStringBodies = [];
|
|
1309
|
+
let addTypeSeen = false;
|
|
1310
|
+
const valueNodeText = (n) => this.stripQuotes(n.text).trim();
|
|
1311
|
+
while (stack.length > 0) {
|
|
1312
|
+
const n = stack.pop();
|
|
1313
|
+
if (n.type === "class_statement" || n.type === "enum_statement") {
|
|
1314
|
+
const nameNode = findFirstChild(n, n.type === "class_statement" ? "class_name" : "enum_name");
|
|
1315
|
+
if (nameNode && nameNode.text) typeNamesInFile.add(nameNode.text.toLowerCase());
|
|
1316
|
+
}
|
|
1317
|
+
if (n.type === "string_literal" && n.text.length > 2 && n.text.charCodeAt(0) === 64) {
|
|
1318
|
+
hereStringBodies.push(n.text);
|
|
1319
|
+
}
|
|
1320
|
+
if (n.type === "command") {
|
|
1321
|
+
const nameNode = findFirstChild(n, "command_name");
|
|
1322
|
+
const leaf = nameNode ? nameNode.text.toLowerCase() : "";
|
|
1323
|
+
if (leaf === "using" || leaf === "add-type") {
|
|
1324
|
+
const elements = findFirstChild(n, "command_elements");
|
|
1325
|
+
const values = [];
|
|
1326
|
+
let addNamespace = "";
|
|
1327
|
+
let addName = "";
|
|
1328
|
+
if (elements) {
|
|
1329
|
+
for (let i = 0; i < elements.namedChildCount; i++) {
|
|
1330
|
+
const c = elements.namedChild(i);
|
|
1331
|
+
if (!c) continue;
|
|
1332
|
+
if (c.type === "command_parameter") {
|
|
1333
|
+
const next = elements.namedChild(i + 1);
|
|
1334
|
+
if (!next) continue;
|
|
1335
|
+
const flag = stripLeadingDash(this.stripQuotes(c.text)).toLowerCase();
|
|
1336
|
+
if (leaf === "add-type" && (flag === "namespace" || flag === "name")) {
|
|
1337
|
+
const spelled = valueNodeText(next);
|
|
1338
|
+
if (spelled.length > 0) addTypeCreatedNames.add(spelled.toLowerCase());
|
|
1339
|
+
if (spelled.length > 0) {
|
|
1340
|
+
if (flag === "namespace") addNamespace = spelled;
|
|
1341
|
+
else addName = spelled;
|
|
1342
|
+
}
|
|
1343
|
+
}
|
|
1344
|
+
continue;
|
|
1345
|
+
}
|
|
1346
|
+
if (c.type === "generic_token" || c.type === "string_literal" || c.type === "expandable_string_literal" || c.type === "array_literal_expression" || c.type === "unary_expression") values.push(c);
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1349
|
+
if (leaf === "using") {
|
|
1350
|
+
if (values.length >= 2) {
|
|
1351
|
+
const directive = valueNodeText(values[0]).toLowerCase();
|
|
1352
|
+
const target = valueNodeText(values[1]);
|
|
1353
|
+
if (directive === "namespace" && target.length > 0) usingNamespacePrefixes.push(target);
|
|
1354
|
+
}
|
|
1355
|
+
} else {
|
|
1356
|
+
addTypeSeen = true;
|
|
1357
|
+
if (addNamespace.length > 0 && addName.length > 0) {
|
|
1358
|
+
recordAddTypeDeclaration(`${addNamespace}.${addName}`);
|
|
1359
|
+
}
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
}
|
|
1363
|
+
for (let i = n.childCount - 1; i >= 0; i--) {
|
|
1364
|
+
const child = n.child(i);
|
|
1365
|
+
if (child) stack.push(child);
|
|
1366
|
+
}
|
|
1367
|
+
}
|
|
1368
|
+
if (addTypeSeen) {
|
|
1369
|
+
for (const body of hereStringBodies) {
|
|
1370
|
+
addTypeNamesIn(body, addTypeCreatedNames, addTypeDeclarations);
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1374
|
+
const work = [{ node: rootNode, exit: false }];
|
|
1375
|
+
while (work.length > 0) {
|
|
1376
|
+
const { node, exit } = work.pop();
|
|
1377
|
+
if (!node) continue;
|
|
1378
|
+
if (exit) {
|
|
1379
|
+
if (dscBlocks.has(nodeKey(node))) {
|
|
1380
|
+
dscBlocks.delete(nodeKey(node));
|
|
1381
|
+
const declared = dscOwner.get(nodeKey(node));
|
|
1382
|
+
dscOwner.delete(nodeKey(node));
|
|
1383
|
+
if (declared !== void 0) frames.pop();
|
|
1384
|
+
dscDepth--;
|
|
1385
|
+
}
|
|
1386
|
+
if (interfaceBlocks.has(nodeKey(node))) {
|
|
1387
|
+
interfaceBlocks.delete(nodeKey(node));
|
|
1388
|
+
frames.pop();
|
|
1389
|
+
}
|
|
1390
|
+
if (node.type === "function_statement" || node.type === "class_statement" || node.type === "enum_statement" || node.type === "class_method_definition") {
|
|
1391
|
+
frames.pop();
|
|
1392
|
+
}
|
|
1393
|
+
continue;
|
|
1394
|
+
}
|
|
1395
|
+
work.push({ node, exit: true });
|
|
1396
|
+
if (isBranch(node.type)) cyclomaticComplexity++;
|
|
1397
|
+
switch (node.type) {
|
|
1398
|
+
// ------------------------------------------------------- functions
|
|
1399
|
+
case "function_statement": {
|
|
1400
|
+
const nameNode = findFirstChild(node, "function_name");
|
|
1401
|
+
if (!nameNode) break;
|
|
1402
|
+
const fnName = nameNode.text;
|
|
1403
|
+
const inline = findFirstChild(node, "function_parameter_declaration");
|
|
1404
|
+
const inlineList = inline ? findFirstChild(inline, "parameter_list") : null;
|
|
1405
|
+
const direct = findFirstChild(node, "parameter_list");
|
|
1406
|
+
const blockList = this.findParamList(node);
|
|
1407
|
+
const params = collectParameters(inlineList ?? direct ?? blockList);
|
|
1408
|
+
const parameterForm = inlineList ? "inline" : direct ?? blockList ? "paramBlock" : "none";
|
|
1409
|
+
const annotations = [];
|
|
1410
|
+
annotations.push(...this.allAttributeTexts(node));
|
|
1411
|
+
for (const attr of annotations) {
|
|
1412
|
+
if (attr.includes("CmdletBinding")) patterns.add("Advanced Functions");
|
|
1413
|
+
if (attr.includes("Parameter")) patterns.add("Parameter Attributes");
|
|
1414
|
+
if (attr.includes("ValidateSet") || attr.includes("ValidateRange") || attr.includes("ValidatePattern"))
|
|
1415
|
+
patterns.add("Input Validation");
|
|
1416
|
+
}
|
|
1417
|
+
const line = nameNode.startPosition.row + 1;
|
|
1418
|
+
declareSymbol(fnName, ASTEngine.SymbolKind.Function, node, line, {
|
|
1419
|
+
...annotations.length > 0 ? { annotations } : {},
|
|
1420
|
+
docstring: this.docstringAbove(nameNode.startPosition.row, lines),
|
|
1421
|
+
returnType: "any",
|
|
1422
|
+
parameterForm
|
|
1423
|
+
}, params);
|
|
1424
|
+
frames.push({ owner: fnName, scope: fnName, containerLine: line });
|
|
1425
|
+
{
|
|
1426
|
+
const paramList = inlineList ?? direct ?? blockList;
|
|
1427
|
+
if (paramList) {
|
|
1428
|
+
for (const sp of this.collectChildrenByType(paramList, "script_parameter")) {
|
|
1429
|
+
const v = findFirstChild(sp, "variable");
|
|
1430
|
+
if (!v) continue;
|
|
1431
|
+
if (isQualified(v.text)) continue;
|
|
1432
|
+
declareSymbol(stripDollar(v.text), ASTEngine.SymbolKind.Variable, v, v.startPosition.row + 1, {
|
|
1433
|
+
isParameter: true,
|
|
1434
|
+
type: typeSpecText(sp) || "untyped"
|
|
1435
|
+
});
|
|
1436
|
+
emitParameterDefaultReads(sp);
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1439
|
+
}
|
|
1440
|
+
break;
|
|
1441
|
+
}
|
|
1442
|
+
// ------------------------------------------------- script parameters
|
|
1443
|
+
case "param_block": {
|
|
1444
|
+
if (!atFileScope()) break;
|
|
1445
|
+
const list = findFirstChild(node, "parameter_list");
|
|
1446
|
+
if (!list) break;
|
|
1447
|
+
for (const p of this.collectChildrenByType(list, "script_parameter")) {
|
|
1448
|
+
const varNode = findFirstChild(p, "variable");
|
|
1449
|
+
if (!varNode || isQualified(varNode.text)) continue;
|
|
1450
|
+
const vline = varNode.startPosition.row + 1;
|
|
1451
|
+
const declared = typeSpecText(p);
|
|
1452
|
+
const attrs = this.attributeTexts(p);
|
|
1453
|
+
declareSymbol(varNode.text, ASTEngine.SymbolKind.Variable, varNode, vline, {
|
|
1454
|
+
isScriptParameter: true,
|
|
1455
|
+
type: declared || "untyped",
|
|
1456
|
+
...attrs ? { annotations: attrs } : {}
|
|
1457
|
+
});
|
|
1458
|
+
emitParameterDefaultReads(p);
|
|
1459
|
+
}
|
|
1460
|
+
break;
|
|
1461
|
+
}
|
|
1462
|
+
// ---------------------------------------------------------- classes
|
|
1463
|
+
case "class_statement": {
|
|
1464
|
+
const names = this.collectChildrenByType(node, "simple_name");
|
|
1465
|
+
if (names.length === 0) break;
|
|
1466
|
+
const className = names[0].text;
|
|
1467
|
+
if (!classDeclaredSpelling.has(className.toLowerCase())) classDeclaredSpelling.set(className.toLowerCase(), className);
|
|
1468
|
+
const baseClass = names.length > 1 ? names[1].text : "";
|
|
1469
|
+
const interfaces = names.slice(2).map((n) => n.text);
|
|
1470
|
+
const declLine = names[0].startPosition.row + 1;
|
|
1471
|
+
if (baseClass) {
|
|
1472
|
+
baseClassByClass.set(className, baseClass);
|
|
1473
|
+
addRel(className, baseClass, ASTEngine.RelationshipType.Extends, 1, declLine, {
|
|
1474
|
+
powershellSpecific: true
|
|
1475
|
+
});
|
|
1476
|
+
}
|
|
1477
|
+
for (const iface of interfaces) {
|
|
1478
|
+
addRel(className, iface, ASTEngine.RelationshipType.Implements, 0.85, declLine, {
|
|
1479
|
+
powershellSpecific: true
|
|
1480
|
+
});
|
|
1481
|
+
}
|
|
1482
|
+
if (!classMembers.has(className)) classMembers.set(className, /* @__PURE__ */ new Set());
|
|
1483
|
+
declareSymbol(className, ASTEngine.SymbolKind.Class, node, declLine, {
|
|
1484
|
+
...baseClass ? { baseClass } : {},
|
|
1485
|
+
docstring: this.docstringAbove(names[0].startPosition.row, lines)
|
|
1486
|
+
});
|
|
1487
|
+
frames.push({ owner: className, scope: className, containerLine: declLine });
|
|
1488
|
+
break;
|
|
1489
|
+
}
|
|
1490
|
+
// ------------------------------------------------------------ enums
|
|
1491
|
+
case "enum_statement": {
|
|
1492
|
+
const nameNode = findFirstChild(node, "simple_name");
|
|
1493
|
+
if (!nameNode) break;
|
|
1494
|
+
const line = nameNode.startPosition.row + 1;
|
|
1495
|
+
declareSymbol(nameNode.text, ASTEngine.SymbolKind.Enum, node, line, {
|
|
1496
|
+
docstring: this.docstringAbove(nameNode.startPosition.row, lines)
|
|
1497
|
+
});
|
|
1498
|
+
frames.push({ owner: nameNode.text, scope: nameNode.text, containerLine: line });
|
|
1499
|
+
break;
|
|
1500
|
+
}
|
|
1501
|
+
case "enum_member": {
|
|
1502
|
+
const nameNode = findFirstChild(node, "simple_name");
|
|
1503
|
+
if (!nameNode) break;
|
|
1504
|
+
const line = nameNode.startPosition.row + 1;
|
|
1505
|
+
declareSymbol(nameNode.text, ASTEngine.SymbolKind.EnumMember, node, line, {});
|
|
1506
|
+
break;
|
|
1507
|
+
}
|
|
1508
|
+
// -------------------------------------------------- class members
|
|
1509
|
+
case "class_method_definition": {
|
|
1510
|
+
const nameNode = findFirstChild(node, "simple_name");
|
|
1511
|
+
if (!nameNode) break;
|
|
1512
|
+
const hdr = memberHeaderFacts(node, nameNode);
|
|
1513
|
+
const memberName = hdr.name;
|
|
1514
|
+
const className = top().owner;
|
|
1515
|
+
const isCtor = memberName === className;
|
|
1516
|
+
const line = nameNode.startPosition.row + 1;
|
|
1517
|
+
const params = collectParameters(this.findMethodParamList(node));
|
|
1518
|
+
const attrText = hdr.modifiers;
|
|
1519
|
+
const symName = isCtor ? "constructor" : memberName;
|
|
1520
|
+
const returnType = typeSpecText(node) || (isCtor ? "void" : "any");
|
|
1521
|
+
declareSymbol(symName, isCtor ? ASTEngine.SymbolKind.Constructor : ASTEngine.SymbolKind.Method, node, line, {
|
|
1522
|
+
isStatic: attrText.includes("static"),
|
|
1523
|
+
isHidden: attrText.includes("hidden"),
|
|
1524
|
+
returnType,
|
|
1525
|
+
...this.allAttributeTexts(node).length > 0 ? { annotations: this.allAttributeTexts(node) } : {},
|
|
1526
|
+
docstring: this.docstringAbove(nameNode.startPosition.row, lines)
|
|
1527
|
+
}, params);
|
|
1528
|
+
if (!isCtor) {
|
|
1529
|
+
if (classMembers.has(className)) {
|
|
1530
|
+
classMembers.get(className).add({
|
|
1531
|
+
name: memberName,
|
|
1532
|
+
kind: "method",
|
|
1533
|
+
isStatic: attrText.includes("static"),
|
|
1534
|
+
isHidden: attrText.includes("hidden")
|
|
1535
|
+
});
|
|
1536
|
+
}
|
|
1537
|
+
overrideCandidates.push({
|
|
1538
|
+
className,
|
|
1539
|
+
memberName,
|
|
1540
|
+
line,
|
|
1541
|
+
fact: {
|
|
1542
|
+
name: memberName,
|
|
1543
|
+
kind: "method",
|
|
1544
|
+
isStatic: attrText.includes("static"),
|
|
1545
|
+
isHidden: attrText.includes("hidden")
|
|
1546
|
+
}
|
|
1547
|
+
});
|
|
1548
|
+
}
|
|
1549
|
+
frames.push({ owner: className, scope: symName, containerLine: line });
|
|
1550
|
+
break;
|
|
1551
|
+
}
|
|
1552
|
+
case "class_property_definition": {
|
|
1553
|
+
const varNode = findFirstChild(node, "variable");
|
|
1554
|
+
if (!varNode) break;
|
|
1555
|
+
const line = varNode.startPosition.row + 1;
|
|
1556
|
+
const attrText = memberHeaderFacts(node, varNode).modifiers;
|
|
1557
|
+
const className = top().owner;
|
|
1558
|
+
const name = stripDollar(varNode.text);
|
|
1559
|
+
const declared = typeSpecText(node);
|
|
1560
|
+
declareSymbol(name, ASTEngine.SymbolKind.Property, varNode, line, {
|
|
1561
|
+
isStatic: attrText.includes("static"),
|
|
1562
|
+
isHidden: attrText.includes("hidden"),
|
|
1563
|
+
type: declared || "untyped",
|
|
1564
|
+
docstring: this.docstringAbove(varNode.startPosition.row, lines)
|
|
1565
|
+
});
|
|
1566
|
+
if (classMembers.has(className)) {
|
|
1567
|
+
classMembers.get(className).add({
|
|
1568
|
+
name,
|
|
1569
|
+
kind: "property",
|
|
1570
|
+
isStatic: attrText.includes("static"),
|
|
1571
|
+
isHidden: attrText.includes("hidden")
|
|
1572
|
+
});
|
|
1573
|
+
}
|
|
1574
|
+
if (declared && !isTypeAccelerator(declared)) {
|
|
1575
|
+
addRel(className, declared, ASTEngine.RelationshipType.Uses, 0.8, line, {
|
|
1576
|
+
powershellSpecific: true,
|
|
1577
|
+
isTypeReference: true,
|
|
1578
|
+
viaMemberType: name
|
|
1579
|
+
});
|
|
1580
|
+
}
|
|
1581
|
+
break;
|
|
1582
|
+
}
|
|
1583
|
+
// ----------------------------------------------------- assignments
|
|
1584
|
+
case "assignment_expression": {
|
|
1585
|
+
const left = findFirstChild(node, "left_assignment_expression");
|
|
1586
|
+
if (left) {
|
|
1587
|
+
const op = findFirstChild(node, "assignement_operator");
|
|
1588
|
+
const opText = op ? op.text.trim() : "=";
|
|
1589
|
+
if (opText.length > 1) {
|
|
1590
|
+
const lhsVar = findDescendantVariable(left);
|
|
1591
|
+
if (lhsVar) {
|
|
1592
|
+
const lname = stripDollar(lhsVar.text.trim());
|
|
1593
|
+
if (isPlausibleSymbolName(lname) && !isQualified(lhsVar.text) && !isAutomatic(lname)) {
|
|
1594
|
+
addRel(top().scope, lname, ASTEngine.RelationshipType.Uses, 0.9, lhsVar.startPosition.row + 1, {
|
|
1595
|
+
powershellSpecific: true,
|
|
1596
|
+
isVariableReference: true,
|
|
1597
|
+
isCompoundAssignmentRead: true,
|
|
1598
|
+
assignmentOperator: opText
|
|
1599
|
+
});
|
|
1600
|
+
}
|
|
1601
|
+
}
|
|
1602
|
+
}
|
|
1603
|
+
}
|
|
1604
|
+
if (left) {
|
|
1605
|
+
const memberAccess = findDescendant(left, "member_access", 14);
|
|
1606
|
+
const elementAccess = findDescendant(left, "element_access", 14);
|
|
1607
|
+
if (memberAccess || elementAccess) {
|
|
1608
|
+
if (memberAccess) emitMemberUses(memberAccess, "write");
|
|
1609
|
+
} else {
|
|
1610
|
+
const varNode = findDescendantVariable(left);
|
|
1611
|
+
if (varNode && isDeclarableVariable(varNode.text)) {
|
|
1612
|
+
const line = varNode.startPosition.row + 1;
|
|
1613
|
+
declareSymbol(varNode.text, ASTEngine.SymbolKind.Variable, varNode, line, { isVariable: true });
|
|
1614
|
+
}
|
|
1615
|
+
}
|
|
1616
|
+
}
|
|
1617
|
+
const operator = findFirstChild(node, "assignement_operator");
|
|
1618
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
1619
|
+
const child = node.namedChild(i);
|
|
1620
|
+
if (!child || child.type === "assignement_operator") continue;
|
|
1621
|
+
if (child.type === "left_assignment_expression") continue;
|
|
1622
|
+
if (child.type !== "pipeline") continue;
|
|
1623
|
+
emitAssignmentReads(child);
|
|
1624
|
+
}
|
|
1625
|
+
break;
|
|
1626
|
+
}
|
|
1627
|
+
// --------------------------------------------------------- foreach
|
|
1628
|
+
case "foreach_statement": {
|
|
1629
|
+
const varNode = findFirstChild(node, "variable");
|
|
1630
|
+
if (varNode && isDeclarableVariable(varNode.text)) {
|
|
1631
|
+
const line = varNode.startPosition.row + 1;
|
|
1632
|
+
declareSymbol(varNode.text, ASTEngine.SymbolKind.Variable, varNode, line, { isLoopVariable: true });
|
|
1633
|
+
}
|
|
1634
|
+
break;
|
|
1635
|
+
}
|
|
1636
|
+
// --------------------------- expression reads (conditions, return/throw)
|
|
1637
|
+
//
|
|
1638
|
+
// A VARIABLE READ IS A READ WHEREVER IT APPEARS, and this lane was reading only
|
|
1639
|
+
// five contexts: assignment right-hand sides, command arguments, string
|
|
1640
|
+
// interpolation, member/element access and the ++/-- family. Conditions and
|
|
1641
|
+
// flow-control statements were not among them, so `if ($x)`, `while ($y)`,
|
|
1642
|
+
// `for (…; $i -lt $n; …)`, `switch ($x)`, `return $z` and `throw $e` published NO
|
|
1643
|
+
// `uses` edge at all. MEASURED on a six-shape reproduction: analyzer 0 reads,
|
|
1644
|
+
// independent oracle 5 (`$x`, `$y`, `$n`, `$z`, `$e`). Over the primary corpus
|
|
1645
|
+
// this is the residue the F5 sample kept naming — `uses -> PID` in a condition, a
|
|
1646
|
+
// `$null` check, a `return $result` — and it is a drop, not a modelling choice:
|
|
1647
|
+
// the same variable in the same position is read whether the statement around it
|
|
1648
|
+
// is an assignment or an `if`.
|
|
1649
|
+
//
|
|
1650
|
+
// Read from the CONDITION WRAPPERS only, and never the statement block: the body
|
|
1651
|
+
// is visited by this same switch, so descending into it here would double-read
|
|
1652
|
+
// every command argument and every interpolated string with a different marker
|
|
1653
|
+
// and lose their per-site attribution (the argument reader's own comment records
|
|
1654
|
+
// what that costs). `for_initializer` and `for_iterator` are deliberately NOT in
|
|
1655
|
+
// the list: the assignment case owns the initializer (and the `$i` there is a
|
|
1656
|
+
// DECLARATION, not a read), and the ++/-- case owns the iterator.
|
|
1657
|
+
// A PIPELINE HEAD IS A READ. `$result4 | Should -Be "x"` reads `$result4`, and
|
|
1658
|
+
// a bare `$Value` statement (`function f { $Value }`, which is how a PowerShell
|
|
1659
|
+
// function RETURNS the parameter) reads it too. Neither is a command: the
|
|
1660
|
+
// `command` branch reads only the ARGUMENTS of a command, so the variable at the
|
|
1661
|
+
// HEAD of a pipeline had no owner in this switch at all. MEASURED on the
|
|
1662
|
+
// ParameterBinding corpus file: nine `$resultN | Should` pipelines plus two bare
|
|
1663
|
+
// `$Value` bodies, 15 MISSING `uses` rows on one file; over the primary corpus
|
|
1664
|
+
// this was the second-largest recipe in the residue (`uses result` 73,
|
|
1665
|
+
// `uses actual` 19, `uses results` 14). A chain that CONTAINS a command is read
|
|
1666
|
+
// by the command branch and is skipped here.
|
|
1667
|
+
case "pipeline_chain": {
|
|
1668
|
+
const hasOwnReader = (n) => {
|
|
1669
|
+
if (n.type === "invokation_expression" || n.type === "post_increment_expression" || n.type === "pre_increment_expression" || n.type === "post_decrement_expression" || n.type === "pre_decrement_expression" || n.type === "sub_expression" || n.type === "expandable_string_literal" || n.type === "expandable_here_string_literal" || n.type === "command") return true;
|
|
1670
|
+
for (let i = 0; i < n.namedChildCount; i++) {
|
|
1671
|
+
const c = n.namedChild(i);
|
|
1672
|
+
if (c && hasOwnReader(c)) return true;
|
|
1673
|
+
}
|
|
1674
|
+
return false;
|
|
1675
|
+
};
|
|
1676
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
1677
|
+
const c = node.namedChild(i);
|
|
1678
|
+
if (!c || c.type === "command" || hasOwnReader(c)) continue;
|
|
1679
|
+
emitVariableReads(c, true, "isPipelineRead");
|
|
1680
|
+
}
|
|
1681
|
+
break;
|
|
1682
|
+
}
|
|
1683
|
+
case "if_statement":
|
|
1684
|
+
case "elseif_clause":
|
|
1685
|
+
case "while_statement":
|
|
1686
|
+
case "do_statement":
|
|
1687
|
+
case "for_statement":
|
|
1688
|
+
case "switch_statement":
|
|
1689
|
+
case "flow_control_statement": {
|
|
1690
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
1691
|
+
const c = node.namedChild(i);
|
|
1692
|
+
if (!c) continue;
|
|
1693
|
+
if (c.type === "pipeline" || c.type === "switch_condition" || c.type === "while_condition" || c.type === "for_condition") {
|
|
1694
|
+
emitVariableReads(c, true, "isExpressionRead");
|
|
1695
|
+
}
|
|
1696
|
+
}
|
|
1697
|
+
break;
|
|
1698
|
+
}
|
|
1699
|
+
// ----------------------------- a type literal is a TYPE REFERENCE
|
|
1700
|
+
//
|
|
1701
|
+
// A type named anywhere in an expression is a dependency on that type, and this
|
|
1702
|
+
// analyzer only published one when it happened to sit on a DECLARATION: PS12's
|
|
1703
|
+
// cast branch and the class-property branch. Everything else was invisible —
|
|
1704
|
+
// `$Node -is [Markdig.Syntax.Inlines.LinkInline]`, `@{ Alias = [System.Management.
|
|
1705
|
+
// Automation.AliasAttribute] }`, `[int[]]@(1,2)` in an argument, a type table
|
|
1706
|
+
// written as a hashtable of accelerator -> full type. MEASURED on the primary
|
|
1707
|
+
// corpus: 99 of the 126 truth edges in TypeAccelerator.Tests.ps1 (a file that IS
|
|
1708
|
+
// a table of types) and the single largest MISSING class after the oracle fixes
|
|
1709
|
+
// — 556 `uses string` and 151 `uses System` were the FRAGMENTED spellings of the
|
|
1710
|
+
// same rule, but the whole-name rows were still dropped.
|
|
1711
|
+
//
|
|
1712
|
+
// The exclusions are the positions where the type is DECLARATION METADATA rather
|
|
1713
|
+
// than a reference, and each is a lane rule that already exists:
|
|
1714
|
+
// · a parameter's type and a class method's RETURN type are stored on the
|
|
1715
|
+
// symbol (`type:`) and are not separate edges;
|
|
1716
|
+
// · an ATTRIBUTE's type names the attribute, which the attribute list already
|
|
1717
|
+
// records as annotations;
|
|
1718
|
+
// · a cast or class-property type is published by its own branch (above/below),
|
|
1719
|
+
// at the DECLARATION's site, so reading it here would double the row with a
|
|
1720
|
+
// different line;
|
|
1721
|
+
// · the object of `::` is a static call/member, and that edge already names the
|
|
1722
|
+
// type in full (`System.IO.Path::GetFullPath`), so a second `uses System.IO.
|
|
1723
|
+
// Path` is the same fact twice — the F5 oracle states the identical rule.
|
|
1724
|
+
case "type_literal": {
|
|
1725
|
+
if (this.withinRecovery(node)) break;
|
|
1726
|
+
let p = node.parent;
|
|
1727
|
+
let metadata = false;
|
|
1728
|
+
for (let d = 0; p && d < 10; d++) {
|
|
1729
|
+
if (p.type === "attribute" || p.type === "script_parameter" || p.type === "class_method_parameter" || p.type === "param_block" || p.type === "class_method_definition") {
|
|
1730
|
+
metadata = true;
|
|
1731
|
+
break;
|
|
1732
|
+
}
|
|
1733
|
+
if (p.type === "statement_list" || p.type === "script_block" || p.type === "program") break;
|
|
1734
|
+
p = p.parent;
|
|
1735
|
+
}
|
|
1736
|
+
if (metadata) break;
|
|
1737
|
+
const parent = node.parent;
|
|
1738
|
+
if (!parent) break;
|
|
1739
|
+
if (parent.type === "cast_expression" || parent.type === "class_property_definition") break;
|
|
1740
|
+
let isStaticObject = false;
|
|
1741
|
+
for (let i = 0; i < parent.childCount; i++) {
|
|
1742
|
+
const c = parent.child(i);
|
|
1743
|
+
if (c && c.startIndex === node.endIndex) {
|
|
1744
|
+
if (c.type === "::" || c.text === "::") isStaticObject = true;
|
|
1745
|
+
break;
|
|
1746
|
+
}
|
|
1747
|
+
}
|
|
1748
|
+
if (isStaticObject) break;
|
|
1749
|
+
const typeName = typeSpecText(node) || this.stripBrackets(node.text);
|
|
1750
|
+
if (!typeName || isTypeAccelerator(typeName)) break;
|
|
1751
|
+
let publishedType = typeName;
|
|
1752
|
+
let viaUsingNamespace = null;
|
|
1753
|
+
if (usingNamespacePrefixes.length > 0 && typeName.indexOf("[") < 0 && typeName.indexOf("]") < 0 && typeName.indexOf(",") < 0) {
|
|
1754
|
+
const dot = typeName.indexOf(".");
|
|
1755
|
+
if (dot > 0) {
|
|
1756
|
+
const rootText = typeName.slice(0, dot);
|
|
1757
|
+
const rootFolded = rootText.toLowerCase();
|
|
1758
|
+
if (rootFolded !== "system" && !typeNamesInFile.has(rootFolded) && !RESOLVED_FRAMEWORK_TYPES.has(rootFolded) && !DOTNET_SYSTEM_NAMESPACES.has(rootFolded)) {
|
|
1759
|
+
for (const prefix of usingNamespacePrefixes) {
|
|
1760
|
+
const prefixIsSystemRooted = prefix.length > 6 && prefix.charAt(prefix.length - 1) !== "." && (prefix.charCodeAt(0) | 32) === 115 && (prefix.charCodeAt(1) | 32) === 121 && (prefix.charCodeAt(2) | 32) === 115 && (prefix.charCodeAt(3) | 32) === 116 && (prefix.charCodeAt(4) | 32) === 101 && (prefix.charCodeAt(5) | 32) === 109 && prefix.charCodeAt(6) === 46;
|
|
1761
|
+
if (!prefixIsSystemRooted) continue;
|
|
1762
|
+
if (!FRAMEWORK_NESTED_NAMESPACES.has(`${prefix}.${rootText}`.toLowerCase())) continue;
|
|
1763
|
+
publishedType = `${prefix}.${typeName}`;
|
|
1764
|
+
viaUsingNamespace = prefix;
|
|
1765
|
+
break;
|
|
1766
|
+
}
|
|
1767
|
+
}
|
|
1768
|
+
}
|
|
1769
|
+
}
|
|
1770
|
+
addRel(top().scope, publishedType, ASTEngine.RelationshipType.Uses, 0.8, node.startPosition.row + 1, {
|
|
1771
|
+
powershellSpecific: true,
|
|
1772
|
+
isTypeReference: true,
|
|
1773
|
+
...viaUsingNamespace === null ? {} : {
|
|
1774
|
+
viaUsingNamespace,
|
|
1775
|
+
typeSpellingAsWritten: typeName
|
|
1776
|
+
}
|
|
1777
|
+
});
|
|
1778
|
+
break;
|
|
1779
|
+
}
|
|
1780
|
+
// ------------------------------------------- typed declarations (PS12)
|
|
1781
|
+
case "cast_expression": {
|
|
1782
|
+
const typeLit = findFirstChild(node, "type_literal");
|
|
1783
|
+
const target = findFirstChild(node, "unary_expression");
|
|
1784
|
+
const varNode = target ? findFirstChild(target, "variable") : null;
|
|
1785
|
+
if (!varNode) {
|
|
1786
|
+
const castType = typeLit ? typeSpecText(node) : "";
|
|
1787
|
+
if (castType && !isTypeAccelerator(castType)) {
|
|
1788
|
+
addRel(top().scope, castType, ASTEngine.RelationshipType.Uses, 0.8, typeLit.startPosition.row + 1, {
|
|
1789
|
+
powershellSpecific: true,
|
|
1790
|
+
isTypeReference: true,
|
|
1791
|
+
isCastExpression: true
|
|
1792
|
+
});
|
|
1793
|
+
}
|
|
1794
|
+
break;
|
|
1795
|
+
}
|
|
1796
|
+
if (!isDeclarableVariable(varNode.text)) break;
|
|
1797
|
+
const line = varNode.startPosition.row + 1;
|
|
1798
|
+
const declared = typeSpecText(node);
|
|
1799
|
+
declareSymbol(varNode.text, ASTEngine.SymbolKind.Variable, varNode, line, {
|
|
1800
|
+
isVariable: true,
|
|
1801
|
+
isTypedDeclaration: true,
|
|
1802
|
+
type: declared || "untyped",
|
|
1803
|
+
...this.allAttributeTexts(node).length > 0 ? { annotations: this.allAttributeTexts(node) } : {}
|
|
1804
|
+
});
|
|
1805
|
+
if (declared && !isTypeAccelerator(declared)) {
|
|
1806
|
+
addRel(top().scope, declared, ASTEngine.RelationshipType.Uses, 0.8, line, {
|
|
1807
|
+
powershellSpecific: true,
|
|
1808
|
+
isTypeReference: true
|
|
1809
|
+
});
|
|
1810
|
+
}
|
|
1811
|
+
break;
|
|
1812
|
+
}
|
|
1813
|
+
case "unary_expression":
|
|
1814
|
+
case "expression_with_unary_operator": {
|
|
1815
|
+
const typeLit = findFirstChild(node, "type_literal");
|
|
1816
|
+
const varNode = findFirstChild(node, "variable");
|
|
1817
|
+
if (!typeLit || !varNode || !isDeclarableVariable(varNode.text)) break;
|
|
1818
|
+
const line = varNode.startPosition.row + 1;
|
|
1819
|
+
const declared = typeSpecText(node);
|
|
1820
|
+
declareSymbol(varNode.text, ASTEngine.SymbolKind.Variable, varNode, line, {
|
|
1821
|
+
isVariable: true,
|
|
1822
|
+
type: declared || "untyped"
|
|
1823
|
+
});
|
|
1824
|
+
if (declared && !TYPE_ACCELERATORS.has(declared)) {
|
|
1825
|
+
addRel(top().scope, declared, ASTEngine.RelationshipType.Uses, 0.8, line, {
|
|
1826
|
+
powershellSpecific: true,
|
|
1827
|
+
isTypeReference: true
|
|
1828
|
+
});
|
|
1829
|
+
}
|
|
1830
|
+
break;
|
|
1831
|
+
}
|
|
1832
|
+
// ------------------------------------------------- hashtable (PS15)
|
|
1833
|
+
case "hash_entry": {
|
|
1834
|
+
const keyNode = findFirstChild(node, "key_expression");
|
|
1835
|
+
if (!keyNode) break;
|
|
1836
|
+
const keyName = keyNode.text;
|
|
1837
|
+
if (!keyName) break;
|
|
1838
|
+
let quoted = false;
|
|
1839
|
+
let interpolating = false;
|
|
1840
|
+
if (keyName.length >= 2) {
|
|
1841
|
+
const q = keyName.charCodeAt(0);
|
|
1842
|
+
if ((q === 34 || q === 39) && keyName.charCodeAt(keyName.length - 1) === q) {
|
|
1843
|
+
quoted = true;
|
|
1844
|
+
const inner = keyName.slice(1, -1);
|
|
1845
|
+
if (q === 34 && inner.includes("$")) interpolating = true;
|
|
1846
|
+
}
|
|
1847
|
+
}
|
|
1848
|
+
if (quoted) {
|
|
1849
|
+
if (interpolating) break;
|
|
1850
|
+
} else if (keyName.includes("$") || keyName.includes(" ")) break;
|
|
1851
|
+
const line = keyNode.startPosition.row + 1;
|
|
1852
|
+
declareSymbol(keyName, ASTEngine.SymbolKind.Property, keyNode, line, {
|
|
1853
|
+
isHashtableKey: true,
|
|
1854
|
+
type: "untyped"
|
|
1855
|
+
});
|
|
1856
|
+
break;
|
|
1857
|
+
}
|
|
1858
|
+
// ------------------------------------------------ member reads (PS9)
|
|
1859
|
+
case "member_access": {
|
|
1860
|
+
emitMemberUses(node, "read");
|
|
1861
|
+
break;
|
|
1862
|
+
}
|
|
1863
|
+
case "element_access": {
|
|
1864
|
+
const keyRoot = node.namedChildCount > 1 ? node.namedChild(1) : null;
|
|
1865
|
+
if (!keyRoot) break;
|
|
1866
|
+
const idx = findDescendant(keyRoot, "string_literal", 12) ?? findDescendant(keyRoot, "variable", 12) ?? findDescendant(keyRoot, "integer_literal", 12) ?? findDescendant(keyRoot, "real_literal", 12);
|
|
1867
|
+
if (!idx) break;
|
|
1868
|
+
const idxLine = idx.startPosition.row + 1;
|
|
1869
|
+
if (idx.type === "string_literal") {
|
|
1870
|
+
const key = this.stripQuotes(idx.text);
|
|
1871
|
+
if (key && !key.includes("$")) {
|
|
1872
|
+
addRel(top().scope, key, ASTEngine.RelationshipType.Uses, 0.8, idxLine, {
|
|
1873
|
+
powershellSpecific: true,
|
|
1874
|
+
isElementAccess: true,
|
|
1875
|
+
isConstantKey: true
|
|
1876
|
+
});
|
|
1877
|
+
}
|
|
1878
|
+
} else if (idx.type === "variable") {
|
|
1879
|
+
addRel(top().scope, stripDollar(idx.text), ASTEngine.RelationshipType.Uses, 0.9, idxLine, {
|
|
1880
|
+
powershellSpecific: true,
|
|
1881
|
+
isElementAccess: true,
|
|
1882
|
+
isVariableReference: true
|
|
1883
|
+
});
|
|
1884
|
+
}
|
|
1885
|
+
break;
|
|
1886
|
+
}
|
|
1887
|
+
// ------------------------------- interpolation (PS16, the groovy G7)
|
|
1888
|
+
case "sub_expression": {
|
|
1889
|
+
const inner = node.namedChildCount > 0 ? node.namedChild(0) : null;
|
|
1890
|
+
if (inner) emitVariableReads(inner);
|
|
1891
|
+
break;
|
|
1892
|
+
}
|
|
1893
|
+
case "expandable_string_literal": {
|
|
1894
|
+
emitVariableReads(node);
|
|
1895
|
+
break;
|
|
1896
|
+
}
|
|
1897
|
+
// F9: an INCREMENT or DECREMENT reads its operand. `$n++` / `++$n` are the
|
|
1898
|
+
// one remaining place a variable is read and nothing was published — the F9
|
|
1899
|
+
// audit found these four types in the "the analyzer never names it" set, and
|
|
1900
|
+
// probing them confirmed all four emit nothing at all, so `$n++` left the
|
|
1901
|
+
// graph with `contains -> n` and no read.
|
|
1902
|
+
//
|
|
1903
|
+
// A `uses` is the right edge and not a weaker one: the operand is genuinely
|
|
1904
|
+
// read, and the WRITE half has no edge in this lane's model because a write to
|
|
1905
|
+
// a local is not a fact the graph can express (a member write can, which is
|
|
1906
|
+
// what `isMemberWrite` is for). The metadata says which operation it was so a
|
|
1907
|
+
// reader is not left guessing, and so a fixture can pin the distinction.
|
|
1908
|
+
case "post_increment_expression":
|
|
1909
|
+
case "post_decrement_expression":
|
|
1910
|
+
case "pre_increment_expression":
|
|
1911
|
+
case "pre_decrement_expression": {
|
|
1912
|
+
const operand = findDescendant(node, "variable", 6);
|
|
1913
|
+
if (operand) {
|
|
1914
|
+
const name = stripDollar(operand.text.trim());
|
|
1915
|
+
if (isPlausibleSymbolName(name) && !isQualified(operand.text) && !isAutomatic(name)) {
|
|
1916
|
+
addRel(top().scope, name, ASTEngine.RelationshipType.Uses, 0.9, operand.startPosition.row + 1, {
|
|
1917
|
+
powershellSpecific: true,
|
|
1918
|
+
isVariableReference: true,
|
|
1919
|
+
isCounterUpdate: true,
|
|
1920
|
+
isPostfix: node.type.startsWith("post_")
|
|
1921
|
+
});
|
|
1922
|
+
}
|
|
1923
|
+
}
|
|
1924
|
+
break;
|
|
1925
|
+
}
|
|
1926
|
+
case "expandable_here_string_literal": {
|
|
1927
|
+
emitVariableReads(node, false, "isHereStringInterpolation");
|
|
1928
|
+
break;
|
|
1929
|
+
}
|
|
1930
|
+
// ---------------------------------------- invocation (PS17/PS18/PS19)
|
|
1931
|
+
case "invokation_expression": {
|
|
1932
|
+
const typeLit = findFirstChild(node, "type_literal");
|
|
1933
|
+
const member = findFirstChild(node, "member_name");
|
|
1934
|
+
const line = (member ?? typeLit ?? node).startPosition.row + 1;
|
|
1935
|
+
const argList = findFirstChild(node, "argument_list");
|
|
1936
|
+
if (argList) emitVariableReads(argList, true, "isArgumentReference");
|
|
1937
|
+
if (typeLit) {
|
|
1938
|
+
const typeName = typeSpecText(node) || this.stripBrackets(typeLit.text);
|
|
1939
|
+
if (!typeName) break;
|
|
1940
|
+
const memberName = member?.text ?? "";
|
|
1941
|
+
if (isNewSpelling(memberName)) {
|
|
1942
|
+
addRel(top().scope, typeName, ASTEngine.RelationshipType.Instantiates, 0.9, line, {
|
|
1943
|
+
powershellSpecific: true,
|
|
1944
|
+
dotNetNew: true
|
|
1945
|
+
});
|
|
1946
|
+
} else if (memberName) {
|
|
1947
|
+
addRel(top().scope, `${typeName}.${memberName}`, ASTEngine.RelationshipType.Calls, 0.8, line, {
|
|
1948
|
+
powershellSpecific: true,
|
|
1949
|
+
staticMemberCall: true,
|
|
1950
|
+
staticType: typeName
|
|
1951
|
+
});
|
|
1952
|
+
}
|
|
1953
|
+
break;
|
|
1954
|
+
}
|
|
1955
|
+
const receiver = findFirstChild(node, "variable");
|
|
1956
|
+
if (member) {
|
|
1957
|
+
const dynamicTarget = member.text.length > 0 && member.text.charCodeAt(0) === 36;
|
|
1958
|
+
if (dynamicTarget) {
|
|
1959
|
+
const dynName = stripDollar(member.text);
|
|
1960
|
+
if (isPlausibleSymbolName(dynName)) {
|
|
1961
|
+
addRel(top().scope, dynName, ASTEngine.RelationshipType.Uses, 0.9, member.startPosition.row + 1, {
|
|
1962
|
+
powershellSpecific: true,
|
|
1963
|
+
isDynamicMethodTarget: true,
|
|
1964
|
+
isVariableReference: true
|
|
1965
|
+
});
|
|
1966
|
+
}
|
|
1967
|
+
break;
|
|
1968
|
+
}
|
|
1969
|
+
addRel(top().scope, member.text, ASTEngine.RelationshipType.Calls, 0.6, line, {
|
|
1970
|
+
powershellSpecific: true,
|
|
1971
|
+
methodCall: true,
|
|
1972
|
+
on: receiver ? stripDollar(receiver.text) : ""
|
|
1973
|
+
});
|
|
1974
|
+
if (receiver) {
|
|
1975
|
+
const name = stripDollar(receiver.text);
|
|
1976
|
+
if (name && !isQualified(receiver.text) && !isAutomatic(name)) {
|
|
1977
|
+
addRel(top().scope, name, ASTEngine.RelationshipType.Uses, 0.9, receiver.startPosition.row + 1, {
|
|
1978
|
+
powershellSpecific: true,
|
|
1979
|
+
isReceiverReference: true,
|
|
1980
|
+
isVariableReference: true
|
|
1981
|
+
});
|
|
1982
|
+
}
|
|
1983
|
+
}
|
|
1984
|
+
}
|
|
1985
|
+
break;
|
|
1986
|
+
}
|
|
1987
|
+
// ------------------------------------------------------- commands
|
|
1988
|
+
case "command": {
|
|
1989
|
+
const cmdNameNode = findFirstChild(node, "command_name");
|
|
1990
|
+
if (cmdNameNode && this.nameWithinRecovery(cmdNameNode)) break;
|
|
1991
|
+
const cmdName = commandNameOf(node);
|
|
1992
|
+
if (!cmdName) break;
|
|
1993
|
+
const leaf = cmdName.includes("::") ? cmdName.slice(cmdName.lastIndexOf("::") + 2) : cmdName;
|
|
1994
|
+
const lowerLeaf = leaf.toLowerCase();
|
|
1995
|
+
const nameLine = (findFirstChild(node, "command_name") ?? node).startPosition.row + 1;
|
|
1996
|
+
if (lowerLeaf === "interface") {
|
|
1997
|
+
const ifaceElements = findFirstChild(node, "command_elements");
|
|
1998
|
+
const ifaceBlock = ifaceElements ? findDescendant(ifaceElements, "script_block_expression") : null;
|
|
1999
|
+
const ifaceName = ifaceElements ? this.collectChildrenByType(ifaceElements, "generic_token")[0] ?? null : null;
|
|
2000
|
+
if (ifaceBlock && ifaceName) {
|
|
2001
|
+
const ifaceLine = ifaceName.startPosition.row + 1;
|
|
2002
|
+
declareSymbol(ifaceName.text, ASTEngine.SymbolKind.Interface, ifaceName, ifaceLine, {
|
|
2003
|
+
powershellSpecific: true,
|
|
2004
|
+
docstring: this.docstringAbove(ifaceName.startPosition.row, lines)
|
|
2005
|
+
});
|
|
2006
|
+
interfaceBlocks.add(nodeKey(node));
|
|
2007
|
+
frames.push({ owner: ifaceName.text, scope: ifaceName.text, containerLine: ifaceLine });
|
|
2008
|
+
}
|
|
2009
|
+
break;
|
|
2010
|
+
}
|
|
2011
|
+
if (NAMED_BLOCK_KEYWORDS.has(lowerLeaf)) break;
|
|
2012
|
+
if (FLOW_KEYWORDS.has(lowerLeaf)) break;
|
|
2013
|
+
if (LANGUAGE_KEYWORD_COMMANDS.has(lowerLeaf)) break;
|
|
2014
|
+
const elements = findFirstChild(node, "command_elements");
|
|
2015
|
+
const blockArg = elements ? findDescendant(elements, "script_block_expression") : null;
|
|
2016
|
+
const firstToken = elements ? this.collectChildrenByType(elements, "generic_token")[0] ?? null : null;
|
|
2017
|
+
const isAssignmentForm = !!firstToken && firstToken.text === "=";
|
|
2018
|
+
if (DSC_KEYWORDS.has(lowerLeaf) && blockArg) {
|
|
2019
|
+
const declared = elements && elements.namedChildCount > 1 ? elements.namedChild(1) : null;
|
|
2020
|
+
if (lowerLeaf === "configuration" && declared && declared.type === "generic_token") {
|
|
2021
|
+
const line = declared.startPosition.row + 1;
|
|
2022
|
+
declareSymbol(declared.text, ASTEngine.SymbolKind.Class, declared, line, { isDscConfiguration: true });
|
|
2023
|
+
dscDepth++;
|
|
2024
|
+
dscBlocks.add(nodeKey(node));
|
|
2025
|
+
dscOwner.set(nodeKey(node), declared.text);
|
|
2026
|
+
frames.push({ owner: declared.text, scope: declared.text, containerLine: line });
|
|
2027
|
+
}
|
|
2028
|
+
break;
|
|
2029
|
+
}
|
|
2030
|
+
if (dscDepth > 0) {
|
|
2031
|
+
if (blockArg && firstToken && firstToken.type === "generic_token") {
|
|
2032
|
+
const line = firstToken.startPosition.row + 1;
|
|
2033
|
+
declareSymbol(firstToken.text, ASTEngine.SymbolKind.Class, firstToken, line, {
|
|
2034
|
+
isDscResource: true,
|
|
2035
|
+
dscResourceType: leaf
|
|
2036
|
+
});
|
|
2037
|
+
dscDepth++;
|
|
2038
|
+
dscBlocks.add(nodeKey(node));
|
|
2039
|
+
dscOwner.set(nodeKey(node), firstToken.text);
|
|
2040
|
+
frames.push({ owner: firstToken.text, scope: firstToken.text, containerLine: line });
|
|
2041
|
+
break;
|
|
2042
|
+
}
|
|
2043
|
+
if (isAssignmentForm) {
|
|
2044
|
+
declareSymbol(leaf, ASTEngine.SymbolKind.Property, node, nameLine, { isDscProperty: true });
|
|
2045
|
+
break;
|
|
2046
|
+
}
|
|
2047
|
+
break;
|
|
2048
|
+
}
|
|
2049
|
+
const op = findFirstChild(node, "command_invokation_operator");
|
|
2050
|
+
if (op) {
|
|
2051
|
+
const target = unquoteTarget(leaf);
|
|
2052
|
+
if (looksLikeModulePath(target)) {
|
|
2053
|
+
const part = sourcedPartName(target);
|
|
2054
|
+
if (op.text === "." && part !== null) {
|
|
2055
|
+
addRel(top().scope, part, ASTEngine.RelationshipType.PartOf, 0.9, nameLine, {
|
|
2056
|
+
powershellSpecific: true,
|
|
2057
|
+
invokationOperator: op.text,
|
|
2058
|
+
// MANDATORY on every `part_of` row (src/ast/types.ts:170-183): this
|
|
2059
|
+
// file carries the directive and names the part, so it is the
|
|
2060
|
+
// library side of the pair. A dot-sourced file does not know it is
|
|
2061
|
+
// being sourced, so PowerShell has only the one role.
|
|
2062
|
+
partRole: "library",
|
|
2063
|
+
partPath: target
|
|
2064
|
+
});
|
|
2065
|
+
} else {
|
|
2066
|
+
addRel(top().scope, target, ASTEngine.RelationshipType.Calls, 0.9, nameLine, {
|
|
2067
|
+
powershellSpecific: true,
|
|
2068
|
+
invokationOperator: op.text
|
|
2069
|
+
});
|
|
2070
|
+
}
|
|
2071
|
+
} else if (target.startsWith("$")) {
|
|
2072
|
+
const invoked = stripDollar(target);
|
|
2073
|
+
if (isPlausibleSymbolName(invoked)) {
|
|
2074
|
+
addRel(top().scope, invoked, ASTEngine.RelationshipType.Uses, 0.9, nameLine, {
|
|
2075
|
+
powershellSpecific: true,
|
|
2076
|
+
isInvokedVariable: true,
|
|
2077
|
+
isVariableReference: true
|
|
2078
|
+
});
|
|
2079
|
+
}
|
|
2080
|
+
} else if (target.length > 0 && isPlausibleSymbolName(target)) {
|
|
2081
|
+
addRel(top().scope, target, ASTEngine.RelationshipType.Calls, 0.9, nameLine, {
|
|
2082
|
+
powershellSpecific: true,
|
|
2083
|
+
invokationOperator: op.text
|
|
2084
|
+
});
|
|
2085
|
+
}
|
|
2086
|
+
break;
|
|
2087
|
+
}
|
|
2088
|
+
detectFrameworks(leaf);
|
|
2089
|
+
if (leaf === "Import-Module" || leaf === "ipmo") {
|
|
2090
|
+
const argNodes = [];
|
|
2091
|
+
const args = commandArgs(node, argNodes, IMPORT_MODULE_MODULE_PARAMETERS);
|
|
2092
|
+
for (let ai = 0; ai < args.length; ai++) {
|
|
2093
|
+
const arg = args[ai];
|
|
2094
|
+
if (SWITCH_ARGUMENT_VALUES.has(arg.toLowerCase())) continue;
|
|
2095
|
+
const argNode = argNodes[ai];
|
|
2096
|
+
if (argNode && nodeIsAVariableReference(argNode)) continue;
|
|
2097
|
+
addImport(arg, leaf, nameLine, node);
|
|
2098
|
+
}
|
|
2099
|
+
const argElementsRead = findFirstChild(node, "command_elements");
|
|
2100
|
+
if (argElementsRead) emitVariableReads(argElementsRead, true, "isArgumentReference");
|
|
2101
|
+
break;
|
|
2102
|
+
}
|
|
2103
|
+
if (leaf === "New-Object") {
|
|
2104
|
+
const comObjectOut = { node: null };
|
|
2105
|
+
const typeNameOut = { node: null };
|
|
2106
|
+
const comObject = this.parameterValue(node, "ComObject", comObjectOut) ?? this.parameterValue(node, "comobject", comObjectOut);
|
|
2107
|
+
const typeName = this.parameterValue(node, "TypeName", typeNameOut) ?? this.parameterValue(node, "typename", typeNameOut);
|
|
2108
|
+
const positionalNodes = [];
|
|
2109
|
+
const positional = commandArgs(node, positionalNodes)[0];
|
|
2110
|
+
const constructed = comObject ?? typeName ?? positional;
|
|
2111
|
+
const constructedFrom = comObject ? comObjectOut.node : typeName ? typeNameOut.node : positionalNodes[0] ?? null;
|
|
2112
|
+
if (constructed && !isTypeAccelerator(constructed)) {
|
|
2113
|
+
if (constructedFrom && nodeIsAVariableReference(constructedFrom)) {
|
|
2114
|
+
addRel(top().scope, constructed, ASTEngine.RelationshipType.Uses, 0.9, nameLine, {
|
|
2115
|
+
powershellSpecific: true,
|
|
2116
|
+
viaNewObject: true,
|
|
2117
|
+
isDynamicTypeName: true,
|
|
2118
|
+
isVariableReference: true
|
|
2119
|
+
});
|
|
2120
|
+
} else {
|
|
2121
|
+
addRel(top().scope, constructed, ASTEngine.RelationshipType.Instantiates, 0.9, nameLine, {
|
|
2122
|
+
powershellSpecific: true,
|
|
2123
|
+
viaNewObject: true,
|
|
2124
|
+
...comObject ? { isComObject: true } : {}
|
|
2125
|
+
});
|
|
2126
|
+
}
|
|
2127
|
+
}
|
|
2128
|
+
break;
|
|
2129
|
+
}
|
|
2130
|
+
if (leaf === "Export-ModuleMember") {
|
|
2131
|
+
const elements2 = findFirstChild(node, "command_elements");
|
|
2132
|
+
if (elements2) {
|
|
2133
|
+
const isFlag = (t) => t === "function" || t === "cmdlet" || t === "variable" || t === "alias";
|
|
2134
|
+
for (let i = 0; i < elements2.namedChildCount; i++) {
|
|
2135
|
+
const c = elements2.namedChild(i);
|
|
2136
|
+
if (!c) continue;
|
|
2137
|
+
if (c.type === "command_parameter") {
|
|
2138
|
+
const flag = stripLeadingDash(stripDollar(c.text)).toLowerCase();
|
|
2139
|
+
if (!isFlag(flag)) continue;
|
|
2140
|
+
const via = `-${flag}`;
|
|
2141
|
+
for (let j = i + 1; j < elements2.namedChildCount; j++) {
|
|
2142
|
+
const v = elements2.namedChild(j);
|
|
2143
|
+
if (!v) break;
|
|
2144
|
+
if (v.type === "command_parameter") break;
|
|
2145
|
+
if (v.type === "command_argument_sep") continue;
|
|
2146
|
+
if (v.type === "generic_token" || v.type === "string_literal" || v.type === "expandable_string_literal") {
|
|
2147
|
+
addExport(this.stripQuotes(v.text), via, v.startPosition.row + 1);
|
|
2148
|
+
} else {
|
|
2149
|
+
for (const t of this.literalTokens(v)) addExport(t, via, v.startPosition.row + 1);
|
|
2150
|
+
}
|
|
2151
|
+
}
|
|
2152
|
+
continue;
|
|
2153
|
+
}
|
|
2154
|
+
if (c.type === "array_literal_expression" || c.type === "unary_expression" || c.type === "command_argument_sep" || c.type === "string_literal" || c.type === "generic_token") {
|
|
2155
|
+
for (const t of this.literalTokens(c)) addExport(t, leaf, c.startPosition.row + 1);
|
|
2156
|
+
}
|
|
2157
|
+
}
|
|
2158
|
+
}
|
|
2159
|
+
break;
|
|
2160
|
+
}
|
|
2161
|
+
if (lowerLeaf === "using") {
|
|
2162
|
+
const args = commandArgs(node);
|
|
2163
|
+
if (args.length >= 2) {
|
|
2164
|
+
const directive = args[0].toLowerCase();
|
|
2165
|
+
addImport(args[1], directive === "namespace" ? "using namespace" : "using module", nameLine, node);
|
|
2166
|
+
}
|
|
2167
|
+
break;
|
|
2168
|
+
}
|
|
2169
|
+
const argElements = findFirstChild(node, "command_elements");
|
|
2170
|
+
if (argElements) emitVariableReads(argElements, true, "isArgumentReference");
|
|
2171
|
+
addRel(top().scope, leaf, ASTEngine.RelationshipType.Calls, leaf.includes("-") ? 0.9 : 0.6, nameLine, {
|
|
2172
|
+
powershellSpecific: true
|
|
2173
|
+
});
|
|
2174
|
+
break;
|
|
2175
|
+
}
|
|
2176
|
+
// ------------------------------------------------------- comments
|
|
2177
|
+
case "comment": {
|
|
2178
|
+
const text = node.text;
|
|
2179
|
+
if (text.startsWith("#requires") || text.startsWith("#Requires")) {
|
|
2180
|
+
const line = node.startPosition.row + 1;
|
|
2181
|
+
const moduleNames = this.extractRequiresModules(text);
|
|
2182
|
+
for (const m of moduleNames) addImport(m.name, m.via, line, node);
|
|
2183
|
+
const version = this.extractRequiresValue(text, "version");
|
|
2184
|
+
let versionShaped = !!version;
|
|
2185
|
+
let versionDots = 0;
|
|
2186
|
+
for (let vi = 0; versionShaped && vi < version.length; vi++) {
|
|
2187
|
+
const ch = version.charCodeAt(vi);
|
|
2188
|
+
if (ch === 46) {
|
|
2189
|
+
versionDots++;
|
|
2190
|
+
continue;
|
|
2191
|
+
}
|
|
2192
|
+
if (ch < 48 || ch > 57) versionShaped = false;
|
|
2193
|
+
}
|
|
2194
|
+
if (version && versionShaped && versionDots >= 1) {
|
|
2195
|
+
addRel(top().scope, version, ASTEngine.RelationshipType.References, 0.9, line, {
|
|
2196
|
+
powershellSpecific: true,
|
|
2197
|
+
requiresVersion: true,
|
|
2198
|
+
fileLevel: atFileScope()
|
|
2199
|
+
});
|
|
2200
|
+
}
|
|
2201
|
+
}
|
|
2202
|
+
break;
|
|
2203
|
+
}
|
|
2204
|
+
}
|
|
2205
|
+
for (let i = node.childCount - 1; i >= 0; i--) {
|
|
2206
|
+
const child = node.child(i);
|
|
2207
|
+
if (child) work.push({ node: child, exit: false });
|
|
2208
|
+
}
|
|
2209
|
+
}
|
|
2210
|
+
for (const cand of overrideCandidates) {
|
|
2211
|
+
let baseWritten = baseClassByClass.get(cand.className);
|
|
2212
|
+
let guard = 0;
|
|
2213
|
+
while (baseWritten && guard++ < 8) {
|
|
2214
|
+
const base = classDeclaredSpelling.get(baseWritten.toLowerCase()) ?? baseWritten;
|
|
2215
|
+
const inherited = classMembers.get(base);
|
|
2216
|
+
const wanted = cand.memberName.toLowerCase();
|
|
2217
|
+
const inheritedFact = inherited ? [...inherited].find((m) => m.name.toLowerCase() === wanted) : void 0;
|
|
2218
|
+
if (inheritedFact && memberOverrides(cand.fact, inheritedFact)) {
|
|
2219
|
+
addRel(cand.className, base, ASTEngine.RelationshipType.Overrides, 0.9, cand.line, {
|
|
2220
|
+
powershellSpecific: true,
|
|
2221
|
+
overridesMember: cand.memberName,
|
|
2222
|
+
// The one edge in this lane that is INFERRED rather than read: PowerShell
|
|
2223
|
+
// has no `override` keyword, so this is a name match between a subclass
|
|
2224
|
+
// member and the base's declared member set. Calling it `structural` would
|
|
2225
|
+
// overstate the evidence, and the tier is the whole point of emitting one.
|
|
2226
|
+
evidenceClass: "inferred"
|
|
2227
|
+
});
|
|
2228
|
+
break;
|
|
2229
|
+
}
|
|
2230
|
+
baseWritten = baseClassByClass.get(base) ?? baseClassByClass.get(baseWritten);
|
|
2231
|
+
}
|
|
2232
|
+
}
|
|
2233
|
+
const declaredFolded = /* @__PURE__ */ new Set();
|
|
2234
|
+
const declaredQualified = /* @__PURE__ */ new Set();
|
|
2235
|
+
for (const s of symbols) {
|
|
2236
|
+
declaredFolded.add(s.name.toLowerCase());
|
|
2237
|
+
const sc = s.scope;
|
|
2238
|
+
if (typeof sc === "string" && sc.length > 0) declaredQualified.add(`${sc}.${s.name}`.toLowerCase());
|
|
2239
|
+
}
|
|
2240
|
+
const declaredInFile = (name) => declaredFolded.has(name.toLowerCase()) || declaredQualified.has(name.toLowerCase());
|
|
2241
|
+
const foldedValueSpellings = /* @__PURE__ */ new Map();
|
|
2242
|
+
for (const sym of symbols) {
|
|
2243
|
+
const kind = String(sym.kind ?? "");
|
|
2244
|
+
if (kind !== "variable" && kind !== "property" && kind !== "parameter") continue;
|
|
2245
|
+
if (!sym.name) continue;
|
|
2246
|
+
const key = sym.name.toLowerCase();
|
|
2247
|
+
let set = foldedValueSpellings.get(key);
|
|
2248
|
+
if (!set) foldedValueSpellings.set(key, set = /* @__PURE__ */ new Set());
|
|
2249
|
+
set.add(sym.name);
|
|
2250
|
+
}
|
|
2251
|
+
for (const rel of relationships) {
|
|
2252
|
+
if (rel.type !== ASTEngine.RelationshipType.Uses) continue;
|
|
2253
|
+
const md = rel.metadata;
|
|
2254
|
+
if (!md) continue;
|
|
2255
|
+
if (md.isVariableReference !== true && md.isMemberAccess !== true && md.isMemberReference !== true) continue;
|
|
2256
|
+
const t = String(rel.target ?? "");
|
|
2257
|
+
if (t.length === 0 || t.indexOf(".") >= 0 || t.indexOf(":") >= 0) continue;
|
|
2258
|
+
if (declaredFolded.has(t.toLowerCase()) && symbols.some((sym) => sym.name === t)) continue;
|
|
2259
|
+
const spellings = foldedValueSpellings.get(t.toLowerCase());
|
|
2260
|
+
if (spellings === void 0 || spellings.size !== 1) continue;
|
|
2261
|
+
const declared = [...spellings][0];
|
|
2262
|
+
if (declared === t) continue;
|
|
2263
|
+
rel.target = declared;
|
|
2264
|
+
md.caseFoldedTo = declared;
|
|
2265
|
+
md.powershellCaseFolded = true;
|
|
2266
|
+
}
|
|
2267
|
+
const ownedByThisFile = (name) => {
|
|
2268
|
+
const dot = name.lastIndexOf(".");
|
|
2269
|
+
if (dot <= 0) return false;
|
|
2270
|
+
return declaredInFile(name.slice(0, dot));
|
|
2271
|
+
};
|
|
2272
|
+
{
|
|
2273
|
+
const variablesByName = /* @__PURE__ */ new Map();
|
|
2274
|
+
const qnOccurrences = /* @__PURE__ */ new Map();
|
|
2275
|
+
for (const s of symbols) {
|
|
2276
|
+
const scope = String(s.scope ?? "");
|
|
2277
|
+
const qn = scope.length > 0 ? `${scope}.${s.name}` : String(s.name);
|
|
2278
|
+
qnOccurrences.set(qn, (qnOccurrences.get(qn) ?? 0) + 1);
|
|
2279
|
+
if (s.kind !== ASTEngine.SymbolKind.Variable) continue;
|
|
2280
|
+
const key = String(s.name).toLowerCase();
|
|
2281
|
+
const list = variablesByName.get(key) ?? [];
|
|
2282
|
+
list.push({ qn, scope, name: String(s.name) });
|
|
2283
|
+
variablesByName.set(key, list);
|
|
2284
|
+
}
|
|
2285
|
+
for (const rel of relationships) {
|
|
2286
|
+
if (rel.type !== ASTEngine.RelationshipType.Uses) continue;
|
|
2287
|
+
if (rel.metadata?.isVariableReference !== true) continue;
|
|
2288
|
+
const target = String(rel.target ?? "");
|
|
2289
|
+
if (target.length === 0) continue;
|
|
2290
|
+
let isBareIdentifier = true;
|
|
2291
|
+
for (let i = 0; i < target.length; i++) {
|
|
2292
|
+
const c = target.charCodeAt(i);
|
|
2293
|
+
if (c === 46 || c === 58 || c === 36 || c === 91 || c === 93 || c === 40 || c === 41) {
|
|
2294
|
+
isBareIdentifier = false;
|
|
2295
|
+
break;
|
|
2296
|
+
}
|
|
2297
|
+
}
|
|
2298
|
+
if (!isBareIdentifier) continue;
|
|
2299
|
+
const source = String(rel.source ?? "");
|
|
2300
|
+
const visible = (variablesByName.get(target.toLowerCase()) ?? []).filter(
|
|
2301
|
+
(c) => c.scope.length === 0 || source === c.scope || source.startsWith(`${c.scope}.`)
|
|
2302
|
+
);
|
|
2303
|
+
if (visible.length !== 1) continue;
|
|
2304
|
+
const hit = visible[0];
|
|
2305
|
+
if ((qnOccurrences.get(hit.qn) ?? 0) !== 1) continue;
|
|
2306
|
+
rel.target = hit.qn;
|
|
2307
|
+
const md = rel.metadata ?? (rel.metadata = {});
|
|
2308
|
+
md.referencedName = target;
|
|
2309
|
+
md.boundToVariable = true;
|
|
2310
|
+
}
|
|
2311
|
+
}
|
|
2312
|
+
for (const rel of relationships) {
|
|
2313
|
+
const target = rel.target;
|
|
2314
|
+
if (target == null || target.length === 0) continue;
|
|
2315
|
+
if (rel.metadata?.boundToVariable === true) continue;
|
|
2316
|
+
const staticMeta = rel.metadata ?? {};
|
|
2317
|
+
if (staticMeta.staticMemberCall === true && typeof staticMeta.staticType === "string") {
|
|
2318
|
+
const declaredOwner = classMembersOf(classMembers, staticMeta.staticType);
|
|
2319
|
+
const memberLeafRaw = target.slice(target.lastIndexOf(".") + 1);
|
|
2320
|
+
const memberLeaf = memberLeafRaw.toLowerCase();
|
|
2321
|
+
if (declaredOwner) {
|
|
2322
|
+
const declaredMember = [...declaredOwner.members].find((m) => m.name.toLowerCase() === memberLeaf);
|
|
2323
|
+
if (declaredMember) {
|
|
2324
|
+
rel.target = `${declaredOwner.name}.${declaredMember.name}`;
|
|
2325
|
+
continue;
|
|
2326
|
+
}
|
|
2327
|
+
rel.metadata.ownerMemberAbsent = { owner: declaredOwner.name, member: memberLeafRaw };
|
|
2328
|
+
continue;
|
|
2329
|
+
}
|
|
2330
|
+
const resolvedType = RESOLVED_FRAMEWORK_TYPES.get(staticMeta.staticType.toLowerCase());
|
|
2331
|
+
if (resolvedType !== void 0) {
|
|
2332
|
+
rel.target = `${resolvedType}.${memberLeafRaw}`;
|
|
2333
|
+
const resolvedMeta = rel.metadata ?? (rel.metadata = {});
|
|
2334
|
+
resolvedMeta.staticTypeResolved = resolvedType;
|
|
2335
|
+
resolvedMeta.typeAccelerator = staticMeta.staticType;
|
|
2336
|
+
continue;
|
|
2337
|
+
}
|
|
2338
|
+
if (staticMeta.staticType.indexOf(".") > 0) {
|
|
2339
|
+
} else {
|
|
2340
|
+
rel.metadata.ownerMemberAbsent = { owner: staticMeta.staticType, member: memberLeafRaw };
|
|
2341
|
+
continue;
|
|
2342
|
+
}
|
|
2343
|
+
}
|
|
2344
|
+
const type = rel.type;
|
|
2345
|
+
if (declaredInFile(target)) continue;
|
|
2346
|
+
if (ownedByThisFile(target)) {
|
|
2347
|
+
if (type === ASTEngine.RelationshipType.Calls || type === ASTEngine.RelationshipType.Uses) {
|
|
2348
|
+
rel.metadata.noDeclarationInFile = true;
|
|
2349
|
+
}
|
|
2350
|
+
continue;
|
|
2351
|
+
}
|
|
2352
|
+
const runtime = runtimeVariableTarget(target);
|
|
2353
|
+
let hasLetter = false;
|
|
2354
|
+
for (let i = 0; i < target.length; i++) {
|
|
2355
|
+
const c = target.charCodeAt(i);
|
|
2356
|
+
if (c >= 65 && c <= 90 || c >= 97 && c <= 122) {
|
|
2357
|
+
hasLetter = true;
|
|
2358
|
+
break;
|
|
2359
|
+
}
|
|
2360
|
+
}
|
|
2361
|
+
let keyHasPathByte = false;
|
|
2362
|
+
for (let i = 0; i < target.length; i++) {
|
|
2363
|
+
const c = target.charCodeAt(i);
|
|
2364
|
+
if (c === 47 || c === 92) {
|
|
2365
|
+
keyHasPathByte = true;
|
|
2366
|
+
break;
|
|
2367
|
+
}
|
|
2368
|
+
}
|
|
2369
|
+
const isQuotedMemberNameWithDot = rel.metadata?.isQuotedMemberName === true && target.indexOf(".") > 0;
|
|
2370
|
+
const isDataNameKey = rel.metadata?.isConstantKey === true && !keyHasPathByte || isQuotedMemberNameWithDot;
|
|
2371
|
+
const programName = rel.type === ASTEngine.RelationshipType.Calls && isCommandPositionCall(rel) ? programNameOf(target) : null;
|
|
2372
|
+
if (programName !== null) {
|
|
2373
|
+
rel.metadata.sourcePathClaim = [programName];
|
|
2374
|
+
continue;
|
|
2375
|
+
}
|
|
2376
|
+
let runtimeCreatedType = false;
|
|
2377
|
+
if (addTypeCreatedNames.size > 0 && (rel.type === ASTEngine.RelationshipType.Instantiates || rel.type === ASTEngine.RelationshipType.Uses)) {
|
|
2378
|
+
const typeDot = target.indexOf(".");
|
|
2379
|
+
const typeRoot = (typeDot > 0 ? target.slice(0, typeDot) : target).toLowerCase();
|
|
2380
|
+
runtimeCreatedType = addTypeCreatedNames.has(typeRoot) || addTypeCreatedNames.has(target.toLowerCase());
|
|
2381
|
+
}
|
|
2382
|
+
let callIntoCompiledType = false;
|
|
2383
|
+
if (addTypeDeclarations.size > 0 && rel.type === ASTEngine.RelationshipType.Calls) {
|
|
2384
|
+
const callDot = target.lastIndexOf(".");
|
|
2385
|
+
if (callDot > 0) {
|
|
2386
|
+
callIntoCompiledType = addTypeDeclarations.has(target.slice(0, callDot).toLowerCase());
|
|
2387
|
+
}
|
|
2388
|
+
}
|
|
2389
|
+
let runtimeBindingRead = false;
|
|
2390
|
+
if (rel.type === ASTEngine.RelationshipType.Uses && rel.metadata?.isVariableReference === true) {
|
|
2391
|
+
if (target.length > 0 && target.indexOf(".") < 0 && target.indexOf(":") < 0 && target.charCodeAt(0) !== 36) {
|
|
2392
|
+
runtimeBindingRead = true;
|
|
2393
|
+
}
|
|
2394
|
+
}
|
|
2395
|
+
const via = isDataNameKey || runtime !== null || runtimeCreatedType || callIntoCompiledType || !hasLetter ? null : externalReadingOf(target) ?? (isCommandPositionCall(rel) ? commandReadingOf(target) : null) ?? (runtimeBindingRead ? "runtime-binding" : null);
|
|
2396
|
+
if (via) {
|
|
2397
|
+
rel.metadata.externalTarget = true;
|
|
2398
|
+
rel.metadata.externalVia = via;
|
|
2399
|
+
if (via === "runtime-binding") rel.metadata.noDeclarationInFile = true;
|
|
2400
|
+
const moduleName = moduleQualifiedCommandOf(target);
|
|
2401
|
+
if (moduleName !== null) {
|
|
2402
|
+
rel.metadata.externalReason = "module-namespace";
|
|
2403
|
+
rel.metadata.externalSpecifier = moduleName;
|
|
2404
|
+
}
|
|
2405
|
+
if (arbiterCanCheckOwner(target)) rel.metadata.isExternalTarget = true;
|
|
2406
|
+
continue;
|
|
2407
|
+
}
|
|
2408
|
+
if (type === ASTEngine.RelationshipType.Calls || type === ASTEngine.RelationshipType.Uses) {
|
|
2409
|
+
rel.metadata.noDeclarationInFile = true;
|
|
2410
|
+
}
|
|
2411
|
+
}
|
|
2412
|
+
const detectedPatterns = [...patterns];
|
|
2413
|
+
const detectedFrameworks = [...frameworks];
|
|
2414
|
+
const psDeclaredHere = /* @__PURE__ */ new Set();
|
|
2415
|
+
for (const sym of symbols) {
|
|
2416
|
+
if (sym.name) psDeclaredHere.add(sym.name);
|
|
2417
|
+
const q = sym.qualifiedName;
|
|
2418
|
+
if (typeof q === "string" && q.length > 0) psDeclaredHere.add(q);
|
|
2419
|
+
}
|
|
2420
|
+
let psNamespaceClaims = 0;
|
|
2421
|
+
let psShorthandClaims = 0;
|
|
2422
|
+
for (const rel of relationships) {
|
|
2423
|
+
if (rel.type !== ASTEngine.RelationshipType.Uses && rel.type !== ASTEngine.RelationshipType.Calls && rel.type !== ASTEngine.RelationshipType.Instantiates) continue;
|
|
2424
|
+
const tgt = String(rel.target ?? "");
|
|
2425
|
+
const md = rel.metadata;
|
|
2426
|
+
if (!md || typeof md !== "object") continue;
|
|
2427
|
+
if (md.isExternal === true) continue;
|
|
2428
|
+
let roots = null;
|
|
2429
|
+
const envRoot = envProviderRootOf(tgt);
|
|
2430
|
+
const usingRoot = usingScopeRootOf(tgt);
|
|
2431
|
+
if (tgt === "System" || tgt.startsWith("System.")) roots = ["System"];
|
|
2432
|
+
else if (envRoot !== null) roots = [envRoot];
|
|
2433
|
+
else if (usingRoot !== null) roots = [usingRoot];
|
|
2434
|
+
else if (md.dotNetNew === true) {
|
|
2435
|
+
const shorthand = dotNetShorthandRootOf(tgt);
|
|
2436
|
+
if (shorthand !== null) {
|
|
2437
|
+
roots = [shorthand];
|
|
2438
|
+
if (psDeclaredHere.has(tgt)) roots = null;
|
|
2439
|
+
}
|
|
2440
|
+
}
|
|
2441
|
+
if (roots === null) continue;
|
|
2442
|
+
if (psDeclaredHere.has(tgt)) continue;
|
|
2443
|
+
md.languageNamespace = roots;
|
|
2444
|
+
if (roots[0] === "System") psNamespaceClaims++;
|
|
2445
|
+
else psShorthandClaims++;
|
|
2446
|
+
}
|
|
2447
|
+
return {
|
|
2448
|
+
language: "powershell",
|
|
2449
|
+
symbols,
|
|
2450
|
+
relationships,
|
|
2451
|
+
imports,
|
|
2452
|
+
exports,
|
|
2453
|
+
errors: [],
|
|
2454
|
+
// engine-owned syntax errors (engine.ts merges treeErrors)
|
|
2455
|
+
metadata: {
|
|
2456
|
+
complexity: symbols.length + relationships.length,
|
|
2457
|
+
linesOfCode: lines.length,
|
|
2458
|
+
cyclomaticComplexity,
|
|
2459
|
+
framework: detectedFrameworks[0],
|
|
2460
|
+
patterns: detectedPatterns,
|
|
2461
|
+
detectedFrameworks,
|
|
2462
|
+
frameworkPatterns: detectedPatterns.map((p) => ({
|
|
2463
|
+
framework: "PowerShell",
|
|
2464
|
+
patterns: [{ type: "usage", detected: true, description: p }]
|
|
2465
|
+
})),
|
|
2466
|
+
architecturalInsights: [],
|
|
2467
|
+
architecturalScore: Math.min(100, 60 + relationships.length),
|
|
2468
|
+
codeQuality: "Excellent",
|
|
2469
|
+
hasTypeInference: false,
|
|
2470
|
+
hasFrameworkIntelligence: true,
|
|
2471
|
+
analysisVersion: "6.0-ps-file-owner-lines-scope-reads"
|
|
2472
|
+
}
|
|
2473
|
+
};
|
|
2474
|
+
}
|
|
2475
|
+
// ------------------------------------------------------------- helpers
|
|
2476
|
+
/** PS1 — the file's own basename, extension included. */
|
|
2477
|
+
basenameOf(filePath) {
|
|
2478
|
+
const normalized = filePath.split("\\").join("/");
|
|
2479
|
+
const base = normalized.slice(normalized.lastIndexOf("/") + 1);
|
|
2480
|
+
return base.length > 0 ? base : "script.ps1";
|
|
2481
|
+
}
|
|
2482
|
+
lineRange(line) {
|
|
2483
|
+
return {
|
|
2484
|
+
startLine: line,
|
|
2485
|
+
startColumn: 1,
|
|
2486
|
+
endLine: line,
|
|
2487
|
+
endColumn: 1,
|
|
2488
|
+
startIndex: 0,
|
|
2489
|
+
endIndex: 0
|
|
2490
|
+
};
|
|
2491
|
+
}
|
|
2492
|
+
nodeToRange(node) {
|
|
2493
|
+
return {
|
|
2494
|
+
startLine: node.startPosition.row + 1,
|
|
2495
|
+
startColumn: node.startPosition.column + 1,
|
|
2496
|
+
endLine: node.endPosition.row + 1,
|
|
2497
|
+
endColumn: node.endPosition.column + 1,
|
|
2498
|
+
startIndex: node.startIndex,
|
|
2499
|
+
endIndex: node.endIndex
|
|
2500
|
+
};
|
|
2501
|
+
}
|
|
2502
|
+
collectChildrenByType(node, type) {
|
|
2503
|
+
const results = [];
|
|
2504
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
2505
|
+
const child = node.namedChild(i);
|
|
2506
|
+
if (child && child.type === type) results.push(child);
|
|
2507
|
+
}
|
|
2508
|
+
return results;
|
|
2509
|
+
}
|
|
2510
|
+
stripQuotes(text) {
|
|
2511
|
+
if (text.length >= 2) {
|
|
2512
|
+
const first = text.charCodeAt(0);
|
|
2513
|
+
const last = text.charCodeAt(text.length - 1);
|
|
2514
|
+
if ((first === 34 || first === 39) && last === first) return text.slice(1, -1);
|
|
2515
|
+
}
|
|
2516
|
+
return text;
|
|
2517
|
+
}
|
|
2518
|
+
stripBrackets(text) {
|
|
2519
|
+
if (text.length >= 2 && text.charCodeAt(0) === 91 && text.charCodeAt(text.length - 1) === 93) {
|
|
2520
|
+
return text.slice(1, -1);
|
|
2521
|
+
}
|
|
2522
|
+
return text;
|
|
2523
|
+
}
|
|
2524
|
+
findQuoted(node) {
|
|
2525
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
2526
|
+
const child = node.namedChild(i);
|
|
2527
|
+
if (!child) continue;
|
|
2528
|
+
if (child.type === "string_literal" || child.type === "expandable_string_literal") return child;
|
|
2529
|
+
const nested = this.findQuoted(child);
|
|
2530
|
+
if (nested) return nested;
|
|
2531
|
+
}
|
|
2532
|
+
return null;
|
|
2533
|
+
}
|
|
2534
|
+
/**
|
|
2535
|
+
* PS6 — is this node inside a recovery region? A `command` whose name lives
|
|
2536
|
+
* under an ERROR/MISSING node is recovery noise, not a call. bash BD5
|
|
2537
|
+
* established the rule; this lane needed it because its own
|
|
2538
|
+
* `if`-as-expression shape manufactures `command` nodes out of an ERROR
|
|
2539
|
+
* region with `hasError = false`.
|
|
2540
|
+
*/
|
|
2541
|
+
withinRecovery(node) {
|
|
2542
|
+
const st = [node];
|
|
2543
|
+
while (st.length > 0) {
|
|
2544
|
+
const x = st.pop();
|
|
2545
|
+
if (x.type === "ERROR" || x.isMissing) return true;
|
|
2546
|
+
for (let i = 0; i < x.childCount; i++) {
|
|
2547
|
+
const c = x.child(i);
|
|
2548
|
+
if (c) st.push(c);
|
|
2549
|
+
}
|
|
2550
|
+
}
|
|
2551
|
+
return false;
|
|
2552
|
+
}
|
|
2553
|
+
/**
|
|
2554
|
+
* PS6 — does this NAME live inside a recovery region?
|
|
2555
|
+
*
|
|
2556
|
+
* The command branch needs the ANCESTOR question, not the subtree one, because
|
|
2557
|
+
* PS6's wording is about where the name sits: a `command` reconstructed from a
|
|
2558
|
+
* broken line keeps a clean name token and a clean subtree, accompanied by an
|
|
2559
|
+
* ERROR sibling, so a subtree test passes it. The one exemption is the
|
|
2560
|
+
* whole-file wrapper: the grammar wraps a CmdletBinding function's body in one
|
|
2561
|
+
* ERROR node covering the entire file, and the commands inside it are real
|
|
2562
|
+
* (MethodInvocation.Tests.ps1; the oracle expects them).
|
|
2563
|
+
*/
|
|
2564
|
+
nameWithinRecovery(node) {
|
|
2565
|
+
let top = node;
|
|
2566
|
+
while (top.parent) top = top.parent;
|
|
2567
|
+
const topEnd = top.endIndex;
|
|
2568
|
+
let n = node.parent;
|
|
2569
|
+
let d = 0;
|
|
2570
|
+
while (n && d < 24) {
|
|
2571
|
+
if (n.type === "ERROR" || n.isMissing) {
|
|
2572
|
+
const wholeFile = n.startIndex === 0 && n.endIndex === topEnd;
|
|
2573
|
+
if (!wholeFile) return true;
|
|
2574
|
+
}
|
|
2575
|
+
n = n.parent;
|
|
2576
|
+
d++;
|
|
2577
|
+
}
|
|
2578
|
+
return false;
|
|
2579
|
+
}
|
|
2580
|
+
/** The value of a two-token parameter (`-TypeName Foo`) or `-Name:Foo`. */
|
|
2581
|
+
parameterValue(node, flag, out) {
|
|
2582
|
+
const elements = this.findChildByType(node, "command_elements");
|
|
2583
|
+
if (!elements) return null;
|
|
2584
|
+
const wanted = stripLeadingDash(flag).toLowerCase();
|
|
2585
|
+
for (let i = 0; i < elements.namedChildCount; i++) {
|
|
2586
|
+
const c = elements.namedChild(i);
|
|
2587
|
+
if (!c || c.type !== "command_parameter") continue;
|
|
2588
|
+
if (stripLeadingDash(this.stripQuotes(c.text)).toLowerCase() !== wanted) continue;
|
|
2589
|
+
const next = this.valueAfterNode(elements, i);
|
|
2590
|
+
if (next && (next.type === "generic_token" || next.type === "string_literal" || next.type === "expandable_string_literal")) {
|
|
2591
|
+
if (out) out.node = next;
|
|
2592
|
+
return this.stripQuotes(next.text);
|
|
2593
|
+
}
|
|
2594
|
+
}
|
|
2595
|
+
return null;
|
|
2596
|
+
}
|
|
2597
|
+
/**
|
|
2598
|
+
* The value token that FOLLOWS the named child at `fromIndex`, skipping the
|
|
2599
|
+
* grammar's `command_argument_sep` separators.
|
|
2600
|
+
*
|
|
2601
|
+
* This is the whole of the two-token parameter form, and reading
|
|
2602
|
+
* `namedChild(i + 1)` instead is the whole bug: measured shape for
|
|
2603
|
+
* `Import-Module -Name SqlServer -ErrorAction SilentlyContinue` is
|
|
2604
|
+
* `[sep, command_parameter(-Name), sep, generic_token(SqlServer), sep,
|
|
2605
|
+
* command_parameter(-ErrorAction), sep, generic_token(SilentlyContinue)]`, so
|
|
2606
|
+
* the value sits at `i + 3`. Every `i + 1` read found a separator, matched no
|
|
2607
|
+
* branch, and read NO two-token parameter value anywhere in the language.
|
|
2608
|
+
* Also worth recording: this grammar gives `command_parameter` NO `argument`
|
|
2609
|
+
* field, so a probe for one could only ever return null.
|
|
2610
|
+
*/
|
|
2611
|
+
valueAfterNode(elements, fromIndex) {
|
|
2612
|
+
for (let i = fromIndex + 1; i < elements.namedChildCount; i++) {
|
|
2613
|
+
const c = elements.namedChild(i);
|
|
2614
|
+
if (!c) continue;
|
|
2615
|
+
if (c.type === "command_argument_sep") continue;
|
|
2616
|
+
return c;
|
|
2617
|
+
}
|
|
2618
|
+
return null;
|
|
2619
|
+
}
|
|
2620
|
+
/** Every bare string/number token under a node, in source order. */
|
|
2621
|
+
literalTokens(node) {
|
|
2622
|
+
const out = [];
|
|
2623
|
+
const work = [node];
|
|
2624
|
+
while (work.length > 0) {
|
|
2625
|
+
const n = work.pop();
|
|
2626
|
+
if (n.type === "string_literal" || n.type === "expandable_string_literal" || n.type === "generic_token") {
|
|
2627
|
+
out.push(this.stripQuotes(n.text));
|
|
2628
|
+
continue;
|
|
2629
|
+
}
|
|
2630
|
+
for (let i = n.namedChildCount - 1; i >= 0; i--) {
|
|
2631
|
+
const c = n.namedChild(i);
|
|
2632
|
+
if (c) work.push(c);
|
|
2633
|
+
}
|
|
2634
|
+
}
|
|
2635
|
+
return out.reverse();
|
|
2636
|
+
}
|
|
2637
|
+
findChildByType(node, type) {
|
|
2638
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
2639
|
+
const child = node.namedChild(i);
|
|
2640
|
+
if (child && child.type === type) return child;
|
|
2641
|
+
}
|
|
2642
|
+
return null;
|
|
2643
|
+
}
|
|
2644
|
+
findParamList(node) {
|
|
2645
|
+
const block = this.findChildByType(node, "script_block");
|
|
2646
|
+
if (block) {
|
|
2647
|
+
const paramBlock = this.findChildByType(block, "param_block");
|
|
2648
|
+
if (paramBlock) return this.findChildByType(paramBlock, "parameter_list");
|
|
2649
|
+
}
|
|
2650
|
+
return null;
|
|
2651
|
+
}
|
|
2652
|
+
findMethodParamList(node) {
|
|
2653
|
+
const paramDecl = this.findChildByType(node, "class_method_parameter_list");
|
|
2654
|
+
if (!paramDecl) return null;
|
|
2655
|
+
return this.findChildByType(paramDecl, "parameter_list") ?? paramDecl;
|
|
2656
|
+
}
|
|
2657
|
+
// PS27 REMOVED: `classAttributeTexts` had no callers left. It collected EVERY
|
|
2658
|
+
// `class_attribute` on a class member, which is correct for a member and wrong
|
|
2659
|
+
// for the pinned wasm's split — where one of those nodes is the front of the
|
|
2660
|
+
// NAME — and `memberHeaderFacts` replaces it with the position scan. It is
|
|
2661
|
+
// deleted rather than left in place because a private method that reads a
|
|
2662
|
+
// member's modifiers the wrong way is a trap for whoever calls it next, and
|
|
2663
|
+
// `class-inherit-psa2` will not catch a re-introduction: no fixture reads
|
|
2664
|
+
// `isStatic` off a split-shaped member.
|
|
2665
|
+
/**
|
|
2666
|
+
* PS11 — every attribute attached to a declaration, from BOTH places
|
|
2667
|
+
* PowerShell allows one: directly on the declaration, and on its `param_block`
|
|
2668
|
+
* (which is where `[CmdletBinding()]` and `[OutputType()]` actually sit).
|
|
2669
|
+
* Scoped to a parameter/attribute-list chain, so the `path` of a
|
|
2670
|
+
* `command_parameter` is never mistaken for a declaration attribute.
|
|
2671
|
+
*/
|
|
2672
|
+
allAttributeTexts(node) {
|
|
2673
|
+
const out = [];
|
|
2674
|
+
for (const direct of this.collectChildrenByType(node, "attribute")) out.push(direct.text);
|
|
2675
|
+
const blocks = this.collectChildrenByType(node, "param_block");
|
|
2676
|
+
for (const sb of this.collectChildrenByType(node, "script_block")) {
|
|
2677
|
+
blocks.push(...this.collectChildrenByType(sb, "param_block"));
|
|
2678
|
+
}
|
|
2679
|
+
for (const block of blocks) {
|
|
2680
|
+
for (const list of this.collectChildrenByType(block, "attribute_list")) {
|
|
2681
|
+
for (const attr of this.collectChildrenByType(list, "attribute")) out.push(attr.text);
|
|
2682
|
+
}
|
|
2683
|
+
}
|
|
2684
|
+
for (const list of this.collectChildrenByType(node, "attribute_list")) {
|
|
2685
|
+
for (const attr of this.collectChildrenByType(list, "attribute")) out.push(attr.text);
|
|
2686
|
+
}
|
|
2687
|
+
return out;
|
|
2688
|
+
}
|
|
2689
|
+
/** An `attribute_list`'s `attribute` children, as raw text. */
|
|
2690
|
+
attributeTexts(node) {
|
|
2691
|
+
const lists = this.collectChildrenByType(node, "attribute_list");
|
|
2692
|
+
if (lists.length === 0) return void 0;
|
|
2693
|
+
const texts = [];
|
|
2694
|
+
for (const list of lists) {
|
|
2695
|
+
for (const attr of this.collectChildrenByType(list, "attribute")) texts.push(attr.text);
|
|
2696
|
+
}
|
|
2697
|
+
return texts.length > 0 ? texts : void 0;
|
|
2698
|
+
}
|
|
2699
|
+
/**
|
|
2700
|
+
* PS23 — the comment-based help block immediately above a declaration. A
|
|
2701
|
+
* PowerShell function's documentation IS its `.SYNOPSIS`/`.DESCRIPTION`
|
|
2702
|
+
* comment block (`about_Comment_Based_Help`), and the pre-wave lane read no
|
|
2703
|
+
* comment but `#requires`, so `docstring` was `null` for every symbol in the
|
|
2704
|
+
* language. `row` is the 0-based row of the declaration's NAME.
|
|
2705
|
+
*/
|
|
2706
|
+
docstringAbove(row, lines) {
|
|
2707
|
+
let i = row - 1;
|
|
2708
|
+
if (i < 0 || lines[i] === void 0 || lines[i].trim() === "") return void 0;
|
|
2709
|
+
if (lines[i].trim().endsWith("#>")) {
|
|
2710
|
+
let open = -1;
|
|
2711
|
+
for (let k = i; k >= 0; k--) {
|
|
2712
|
+
if (lines[k].includes("<#")) {
|
|
2713
|
+
open = k;
|
|
2714
|
+
break;
|
|
2715
|
+
}
|
|
2716
|
+
}
|
|
2717
|
+
if (open < 0) return void 0;
|
|
2718
|
+
const last = i;
|
|
2719
|
+
const body = lines.slice(open, last + 1).map((l, k) => k === 0 ? stripOpenCommentDelim(l) : k === last - open ? stripCloseCommentDelim(l) : l).join("\n").trim();
|
|
2720
|
+
return body.length > 0 ? body : void 0;
|
|
2721
|
+
}
|
|
2722
|
+
const collected = [];
|
|
2723
|
+
while (i >= 0) {
|
|
2724
|
+
const raw = lines[i];
|
|
2725
|
+
if (raw === void 0) break;
|
|
2726
|
+
const trimmed = raw.trim();
|
|
2727
|
+
if (trimmed === "" || !trimmed.startsWith("#") || trimmed.startsWith("#>")) break;
|
|
2728
|
+
collected.unshift(trimmed);
|
|
2729
|
+
i--;
|
|
2730
|
+
}
|
|
2731
|
+
if (collected.length === 0) return void 0;
|
|
2732
|
+
const text = stripCommentMarkerRun(collected.join("\n")).trim();
|
|
2733
|
+
return text.length > 0 ? text : void 0;
|
|
2734
|
+
}
|
|
2735
|
+
/**
|
|
2736
|
+
* PS24 — EVERY module name a `#requires -Module[s]` declares, which is not the
|
|
2737
|
+
* same question `extractRequiresValue` answers.
|
|
2738
|
+
*
|
|
2739
|
+
* The reader above stops at the first space, tab or comma, which is right for
|
|
2740
|
+
* `-Version 7.0` and wrong twice for a module list. Measured on the corpus's own
|
|
2741
|
+
* two files that use the hashtable form
|
|
2742
|
+
* (`ansible/.../win_app_control/files/{New-AnsiblePowerShellSignature,
|
|
2743
|
+
* Set-ManifestSignature}.ps1`, both carrying
|
|
2744
|
+
* `#Requires -Modules @{ ModuleName = 'OpenAuthenticode'; ModuleVersion = '0.5.0' }`):
|
|
2745
|
+
* the old reader returned the literal `@{` as the module name, so `imports[]` stored
|
|
2746
|
+
* a row whose `path` was `@{` and `addImport` published `imports`/`references` edges
|
|
2747
|
+
* to `@{`. Those edges died one stage later — `filterNoiseRelationships` in
|
|
2748
|
+
* `src/ast/pool-worker.ts` drops any target whose leaf contains `{` — which is why
|
|
2749
|
+
* the census reads `imports[] 552` against `imports 550`, and the two rows that
|
|
2750
|
+
* vanish are exactly these two files. A garbage edge killed by shared machinery is
|
|
2751
|
+
* still a garbage row already written, and the module it should have named
|
|
2752
|
+
* (`OpenAuthenticode`) is a dependency the file genuinely declares.
|
|
2753
|
+
*
|
|
2754
|
+
* The comma list is the second miss, and rarer than the hashtable:
|
|
2755
|
+
* `#requires -Modules Pester, PoshRSJob` names two modules and the old reader
|
|
2756
|
+
* returned `Pester` alone, because the loop stopped at the comma. Measured over the
|
|
2757
|
+
* 1,092-file admitted corpus: ONE occurrence of a comma-separated `#requires` module
|
|
2758
|
+
* list — `#Requires -Modules PSScriptAnalyzer, PSSA-PSCustomUseLiteralPath` at
|
|
2759
|
+
* `ansible/test/lib/ansible_test/_util/controller/sanity/pslint/pslint.ps1:2` — so
|
|
2760
|
+
* `PSSA-PSCustomUseLiteralPath` was a declared dependency this lane read and dropped.
|
|
2761
|
+
*
|
|
2762
|
+
* MEASURED DELTA, and the population it is measured over. Only a file carrying a
|
|
2763
|
+
* `#requires -Module(s)` directive can be affected, and 59 of the 1,092 admitted files
|
|
2764
|
+
* do, so those 59 are the whole population and the delta is exact rather than
|
|
2765
|
+
* sampled. Through production `parseFile`, before -> after: `imports[]` records
|
|
2766
|
+
* 101 -> 102, `imports` edges 99 -> 102, `references` edges 105 -> 108. The records /
|
|
2767
|
+
* edges gap of 2 that exists BEFORE the fix is the two `@{` rows: they were written
|
|
2768
|
+
* and then dropped by the pool-worker's brace rule, which is why the whole-corpus
|
|
2769
|
+
* census reads `imports[] 552` against `imports 550`.
|
|
2770
|
+
*
|
|
2771
|
+
* Zero regex, like the reader above, for the reason recorded there. Three shapes are
|
|
2772
|
+
* recognized and nothing else: a bare list, a comma-continued bare list, and a
|
|
2773
|
+
* hashtable whose `ModuleName` key names the module. A hashtable with no
|
|
2774
|
+
* `ModuleName` names no module and yields nothing — `@{ ModuleVersion = '1.0' }` is
|
|
2775
|
+
* not a module declaration, and returning the flag's value for it is how `@{`
|
|
2776
|
+
* reached `imports[]` in the first place.
|
|
2777
|
+
*/
|
|
2778
|
+
extractRequiresModules(text) {
|
|
2779
|
+
const names = [];
|
|
2780
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2781
|
+
let via = "requires_list";
|
|
2782
|
+
const lower = text.toLowerCase();
|
|
2783
|
+
const add = (raw) => {
|
|
2784
|
+
const v = this.stripQuotes(raw.trim());
|
|
2785
|
+
if (v.length === 0 || seen.has(v)) return;
|
|
2786
|
+
seen.add(v);
|
|
2787
|
+
names.push({ name: v, via });
|
|
2788
|
+
};
|
|
2789
|
+
for (let i = 0; i < text.length; i++) {
|
|
2790
|
+
if (text[i] !== "-") continue;
|
|
2791
|
+
let j = i + 1;
|
|
2792
|
+
while (isAsciiLetter(lower[j])) j++;
|
|
2793
|
+
const flag = lower.slice(i + 1, j);
|
|
2794
|
+
if (flag !== "module" && flag !== "modules") continue;
|
|
2795
|
+
while (text[j] === " " || text[j] === " ") j++;
|
|
2796
|
+
if (j >= text.length) break;
|
|
2797
|
+
for (; ; ) {
|
|
2798
|
+
if (text[j] === "@" && text[j + 1] === "{") {
|
|
2799
|
+
const ht = this.readHashtableModuleName(text, j + 1);
|
|
2800
|
+
via = "requires_hashtable";
|
|
2801
|
+
if (ht) add(ht);
|
|
2802
|
+
while (text[j] !== void 0 && text[j] !== "}") j++;
|
|
2803
|
+
if (text[j] === "}") j++;
|
|
2804
|
+
} else {
|
|
2805
|
+
let end = j;
|
|
2806
|
+
while (text[end] !== void 0 && text[end] !== " " && text[end] !== " " && text[end] !== "," && text[end] !== ";") end++;
|
|
2807
|
+
if (end > j) add(text.slice(j, end));
|
|
2808
|
+
j = end;
|
|
2809
|
+
}
|
|
2810
|
+
while (text[j] === " " || text[j] === " ") j++;
|
|
2811
|
+
if (text[j] !== ",") break;
|
|
2812
|
+
j++;
|
|
2813
|
+
while (text[j] === " " || text[j] === " ") j++;
|
|
2814
|
+
if (j >= text.length) break;
|
|
2815
|
+
}
|
|
2816
|
+
i = j - 1;
|
|
2817
|
+
}
|
|
2818
|
+
return names;
|
|
2819
|
+
}
|
|
2820
|
+
/**
|
|
2821
|
+
* PS24 — the `ModuleName` value inside a `#requires -Modules @{ … }` hashtable,
|
|
2822
|
+
* starting at the `{`. Char-scanned and brace-balanced with quote awareness, because
|
|
2823
|
+
* a `}` inside a quoted `ModuleVersion` must not close the hashtable.
|
|
2824
|
+
*
|
|
2825
|
+
* Returns null when the hashtable carries no `ModuleName` key. PowerShell's module
|
|
2826
|
+
* hashtable is `{ ModuleName = 'X'; ModuleVersion | RequiredVersion | GUID = … }`, so
|
|
2827
|
+
* the key is the only thing in it that names a module.
|
|
2828
|
+
*/
|
|
2829
|
+
readHashtableModuleName(text, openBrace) {
|
|
2830
|
+
const lower = text.toLowerCase();
|
|
2831
|
+
let depth = 0;
|
|
2832
|
+
let quote = "";
|
|
2833
|
+
for (let i = openBrace; i < text.length; i++) {
|
|
2834
|
+
const c = text[i];
|
|
2835
|
+
if (quote) {
|
|
2836
|
+
if (c === quote) quote = "";
|
|
2837
|
+
continue;
|
|
2838
|
+
}
|
|
2839
|
+
if (c === "'" || c === '"' || c === "`") {
|
|
2840
|
+
quote = c;
|
|
2841
|
+
continue;
|
|
2842
|
+
}
|
|
2843
|
+
if (c === "{") {
|
|
2844
|
+
depth++;
|
|
2845
|
+
continue;
|
|
2846
|
+
}
|
|
2847
|
+
if (c === "}") {
|
|
2848
|
+
depth--;
|
|
2849
|
+
if (depth === 0) return null;
|
|
2850
|
+
continue;
|
|
2851
|
+
}
|
|
2852
|
+
if (depth !== 1) continue;
|
|
2853
|
+
if (!isAsciiLetter(c) && c !== "_") continue;
|
|
2854
|
+
let e = i;
|
|
2855
|
+
while (isAsciiLetter(lower[e]) || lower[e] === "_") e++;
|
|
2856
|
+
const key = lower.slice(i, e);
|
|
2857
|
+
let v = e;
|
|
2858
|
+
while (text[v] === " " || text[v] === " ") v++;
|
|
2859
|
+
if (text[v] !== "=") {
|
|
2860
|
+
i = e - 1;
|
|
2861
|
+
continue;
|
|
2862
|
+
}
|
|
2863
|
+
v++;
|
|
2864
|
+
while (text[v] === " " || text[v] === " ") v++;
|
|
2865
|
+
let vend = v;
|
|
2866
|
+
let vq = "";
|
|
2867
|
+
while (vend < text.length) {
|
|
2868
|
+
const vc = text[vend];
|
|
2869
|
+
if (vq) {
|
|
2870
|
+
if (vc === vq) vq = "";
|
|
2871
|
+
vend++;
|
|
2872
|
+
continue;
|
|
2873
|
+
}
|
|
2874
|
+
if (vc === "'" || vc === '"' || vc === "`") {
|
|
2875
|
+
vq = vc;
|
|
2876
|
+
vend++;
|
|
2877
|
+
continue;
|
|
2878
|
+
}
|
|
2879
|
+
if (vc === ";" || vc === "}" || vc === " " || vc === " ") break;
|
|
2880
|
+
vend++;
|
|
2881
|
+
}
|
|
2882
|
+
if (key === "modulename") {
|
|
2883
|
+
const val = this.stripQuotes(text.slice(v, vend));
|
|
2884
|
+
return val.length > 0 ? val : null;
|
|
2885
|
+
}
|
|
2886
|
+
i = vend - 1;
|
|
2887
|
+
}
|
|
2888
|
+
return null;
|
|
2889
|
+
}
|
|
2890
|
+
/**
|
|
2891
|
+
* PS22 — a `#requires` value by name, char/index scanned. Comments are opaque
|
|
2892
|
+
* to the grammar, so this one place is text by necessity, and it is declared
|
|
2893
|
+
* as such here and in the M12 regex census (which records zero regex
|
|
2894
|
+
* usages for this lane: no `/…/` literal is used).
|
|
2895
|
+
*/
|
|
2896
|
+
extractRequiresValue(text, name) {
|
|
2897
|
+
const lower = text.toLowerCase();
|
|
2898
|
+
const key = name.toLowerCase();
|
|
2899
|
+
for (let i = 0; i < text.length; i++) {
|
|
2900
|
+
if (text[i] !== "-") continue;
|
|
2901
|
+
if (!lower.slice(i + 1).startsWith(key)) continue;
|
|
2902
|
+
let j = i + 1 + name.length;
|
|
2903
|
+
while (j < text.length && (text[j] === " " || text[j] === " ")) j++;
|
|
2904
|
+
if (j >= text.length) return null;
|
|
2905
|
+
let end = j;
|
|
2906
|
+
while (end < text.length && text[end] !== " " && text[end] !== " " && text[end] !== ",") end++;
|
|
2907
|
+
const value = this.stripQuotes(text.slice(j, end));
|
|
2908
|
+
return value.length > 0 ? value : null;
|
|
2909
|
+
}
|
|
2910
|
+
return null;
|
|
2911
|
+
}
|
|
2912
|
+
};
|
|
2913
|
+
export {
|
|
2914
|
+
RealPowershellAnalyzer
|
|
2915
|
+
};
|