@backbencher/brain 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/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-KY7K552E.js +17 -0
- package/dist/brain-relationship-enrichment-QH7OOHUF.js +558 -0
- package/dist/brain.sql-EIM4WS4D.js +29 -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-2RTN2DFU.js +711 -0
- package/dist/chunk-2WXYNLBA.js +29 -0
- package/dist/chunk-3CTFIJJB.js +17 -0
- package/dist/chunk-3LFTSMIV.js +174 -0
- package/dist/chunk-47HKX4AJ.js +930 -0
- package/dist/chunk-4H22TLOT.js +543 -0
- package/dist/chunk-5SD45FLC.js +818 -0
- package/dist/chunk-66A5ZFYA.js +1758 -0
- package/dist/chunk-6AIRWAO2.js +247 -0
- package/dist/chunk-6WNGD3NN.js +196 -0
- package/dist/chunk-7AN7U5IZ.js +371 -0
- package/dist/chunk-BHB7BYYI.js +626 -0
- package/dist/chunk-BQ3OVQ3P.js +125 -0
- package/dist/chunk-CW4XXSNN.js +1479 -0
- package/dist/chunk-D2Q4Y47A.js +53 -0
- package/dist/chunk-DIYAGQJL.js +34 -0
- package/dist/chunk-DYM4FQEE.js +90 -0
- package/dist/chunk-EWMWK7WN.js +75 -0
- package/dist/chunk-FVVQTSIM.js +3427 -0
- package/dist/chunk-FWSL4VG6.js +1290 -0
- package/dist/chunk-GBI7V7KY.js +69 -0
- package/dist/chunk-GEFFO6O2.js +152 -0
- package/dist/chunk-HOWAQXJO.js +236 -0
- package/dist/chunk-HQSPJDHI.js +613 -0
- package/dist/chunk-IFZNR5UJ.js +2863 -0
- package/dist/chunk-J4PMBXQQ.js +336 -0
- package/dist/chunk-KAET47ZL.js +125 -0
- package/dist/chunk-KCDECRJY.js +440 -0
- package/dist/chunk-KLK3PBTO.js +1119 -0
- package/dist/chunk-L5TDYKYK.js +76 -0
- package/dist/chunk-MLKGABMK.js +9 -0
- package/dist/chunk-OWISBAOF.js +61 -0
- package/dist/chunk-PJTNGD3Z.js +6924 -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-RH7KC5HU.js +12050 -0
- package/dist/chunk-RM3PY6TG.js +376 -0
- package/dist/chunk-ROMM6KMC.js +433 -0
- package/dist/chunk-SK6YVHFZ.js +211 -0
- package/dist/chunk-SM7RFQRO.js +214 -0
- package/dist/chunk-UFE77L67.js +782 -0
- package/dist/chunk-UGR4QIGS.js +67 -0
- package/dist/chunk-UUNHHX4M.js +13 -0
- package/dist/chunk-V44PGPA6.js +2104 -0
- package/dist/chunk-VL52PL2T.js +71 -0
- package/dist/chunk-VURYFIHW.js +201 -0
- package/dist/chunk-XJKJYIPP.js +7436 -0
- package/dist/chunk-YI6FF2ZR.js +340 -0
- package/dist/cli/index.js +37 -0
- package/dist/config-HZAAYDP3.js +10 -0
- package/dist/content-search-642F7E5V.js +593 -0
- package/dist/database-TCTZDLE5.js +9 -0
- package/dist/doctor-GXCKSJOX.js +395 -0
- package/dist/edit-engine-KM7MPFEC.js +51 -0
- package/dist/engine-42TZFNIZ.js +11 -0
- package/dist/engine-LO6L4JLQ.js +18 -0
- package/dist/fingerprint-XDXFRWO6.js +33 -0
- package/dist/framework-intelligence-DWFCKV44.js +860 -0
- package/dist/freshness-3K6VU5OT.js +174 -0
- package/dist/graph-engine-PL5YA7BA.js +21 -0
- package/dist/graph_request-MM4REP6H.js +350 -0
- package/dist/impact_request-WDE47IZ5.js +68 -0
- package/dist/index-space-IUUN5WYV.js +15 -0
- package/dist/index-status-6BD7UBHR.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-4PW3AFXP.js +88 -0
- package/dist/pool-worker.js +1153 -0
- package/dist/project-health-ZS5TS2Z6.js +94 -0
- package/dist/project-index-5SDPHITE.js +57 -0
- package/dist/project-registry-DGZBIZS7.js +355 -0
- package/dist/real-bash-analyzer-SXSV67E2.js +2201 -0
- package/dist/real-c-analyzer-SY7FTZ6W.js +5942 -0
- package/dist/real-clean-analyzer-IGDHHLZK.js +3470 -0
- package/dist/real-clojure-analyzer-4OH7IWLB.js +2818 -0
- package/dist/real-cpp-analyzer-7LTDRJRJ.js +4101 -0
- package/dist/real-csharp-analyzer-KICU3TLH.js +3298 -0
- package/dist/real-css-analyzer-HPU277Q6.js +15 -0
- package/dist/real-dart-analyzer-7LZ5LCMW.js +3944 -0
- package/dist/real-elixir-analyzer-BE3MEVZO.js +2192 -0
- package/dist/real-erlang-analyzer-BZMAKYBK.js +1731 -0
- package/dist/real-fsharp-analyzer-5QLTOV3E.js +2390 -0
- package/dist/real-go-analyzer-POD7MZQR.js +4126 -0
- package/dist/real-graphql-analyzer-MAQRZ6QW.js +1442 -0
- package/dist/real-groovy-analyzer-MEWSUPMJ.js +2285 -0
- package/dist/real-haskell-analyzer-I5L2GR3Y.js +2533 -0
- package/dist/real-hcl-analyzer-DBOPXSWJ.js +725 -0
- package/dist/real-html-analyzer-O63XZLZD.js +1759 -0
- package/dist/real-java-analyzer-CHCW3EKA.js +4677 -0
- package/dist/real-javascript-analyzer-QAAJQYZE.js +14 -0
- package/dist/real-json-analyzer-UGBR7YQN.js +1640 -0
- package/dist/real-julia-analyzer-35Q4GHOZ.js +2085 -0
- package/dist/real-kotlin-analyzer-GRAYCWW2.js +3956 -0
- package/dist/real-lua-analyzer-5BAPDZDF.js +1957 -0
- package/dist/real-makefile-analyzer-YJKAHNAS.js +920 -0
- package/dist/real-markdown-analyzer-UKUQZO4F.js +1215 -0
- package/dist/real-matlab-analyzer-FVIKQZKG.js +480 -0
- package/dist/real-nim-analyzer-M7FAIABG.js +3603 -0
- package/dist/real-objective-c-analyzer-XMYQIGQI.js +1518 -0
- package/dist/real-perl-analyzer-ZLY6M2DZ.js +1865 -0
- package/dist/real-php-analyzer-KI2ZAM7X.js +9368 -0
- package/dist/real-powershell-analyzer-B3T5ZP6U.js +2915 -0
- package/dist/real-protobuf-analyzer-23OZ75XV.js +867 -0
- package/dist/real-python-analyzer-I4TIUB6D.js +4880 -0
- package/dist/real-r-analyzer-FJZJXXS6.js +2596 -0
- package/dist/real-ruby-analyzer-M3UFIVTQ.js +2429 -0
- package/dist/real-rust-analyzer-LVSONPAN.js +3359 -0
- package/dist/real-scala-analyzer-CWPI33Q2.js +3293 -0
- package/dist/real-scss-analyzer-TGRJQ7TH.js +1515 -0
- package/dist/real-solidity-analyzer-4P2VBVWA.js +2608 -0
- package/dist/real-sql-analyzer-J7DYZOZR.js +3840 -0
- package/dist/real-svelte-analyzer-NHEN2SX5.js +2532 -0
- package/dist/real-swift-analyzer-2NSYULWI.js +2311 -0
- package/dist/real-toml-analyzer-L3C66L7Q.js +929 -0
- package/dist/real-typescript-analyzer-FTC6ETRQ.js +14 -0
- package/dist/real-vb-analyzer-ORY7LAQS.js +3145 -0
- package/dist/real-vba-analyzer-JH2TTUFI.js +466 -0
- package/dist/real-vue-analyzer-OO4FUGBV.js +2680 -0
- package/dist/real-xml-analyzer-6CQVR477.js +2405 -0
- package/dist/real-yaml-analyzer-J6MQG3YX.js +3528 -0
- package/dist/real-zig-analyzer-EFKCPJNU.js +2430 -0
- package/dist/regex-search-OPTLKUN5.js +239 -0
- package/dist/rules-I453JTRC.js +152 -0
- package/dist/scoped-path-MJYWJZQX.js +11 -0
- package/dist/setup-show-Y5GB5KXA.js +15 -0
- package/dist/setup-wizard-G5YYR5T6.js +24 -0
- package/dist/symbol-search-KB4MU4SI.js +16 -0
- package/dist/taint-YOTN5X55.js +202 -0
- package/dist/taint-analyzer-N7YLZU3N.js +163 -0
- package/dist/tool-handlers-G4NAGJIH.js +72 -0
- package/dist/tool-registry-BPEJNSZG.js +17 -0
- package/dist/tool-validation-RF675A3C.js +32 -0
- package/dist/type-inference-4Y7WTMFE.js +13 -0
- package/dist/vector-binary-LMY4QGAN.js +19 -0
- package/dist/version-LXGNFO37.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,920 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ASTEngine
|
|
3
|
+
} from "./chunk-V44PGPA6.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-makefile-analyzer.ts
|
|
12
|
+
var GNU_MAKE_NAMESPACE = /* @__PURE__ */ new Set([
|
|
13
|
+
// builtin and implicit variables (the same spellings the analyzer's inner list uses)
|
|
14
|
+
"MAKE",
|
|
15
|
+
"MAKEFILE_LIST",
|
|
16
|
+
"MAKEFILES",
|
|
17
|
+
"MAKEFLAGS",
|
|
18
|
+
"MAKECMDGOALS",
|
|
19
|
+
"MAKELEVEL",
|
|
20
|
+
"CURDIR",
|
|
21
|
+
"SUFFIXES",
|
|
22
|
+
".DEFAULT_GOAL",
|
|
23
|
+
"VPATH",
|
|
24
|
+
"SHELL",
|
|
25
|
+
"MFLAGS",
|
|
26
|
+
"CC",
|
|
27
|
+
"CXX",
|
|
28
|
+
"CPP",
|
|
29
|
+
"AR",
|
|
30
|
+
"ARFLAGS",
|
|
31
|
+
"AS",
|
|
32
|
+
"ASFLAGS",
|
|
33
|
+
"LD",
|
|
34
|
+
"LDFLAGS",
|
|
35
|
+
"LDLIBS",
|
|
36
|
+
"RM",
|
|
37
|
+
"CFLAGS",
|
|
38
|
+
"CXXFLAGS",
|
|
39
|
+
"CPPFLAGS",
|
|
40
|
+
"YFLAGS",
|
|
41
|
+
"LEX",
|
|
42
|
+
"YACC",
|
|
43
|
+
"AWK",
|
|
44
|
+
"FC",
|
|
45
|
+
"GET",
|
|
46
|
+
"PC",
|
|
47
|
+
"TEX",
|
|
48
|
+
"TEXI2DVI",
|
|
49
|
+
"WEAVE",
|
|
50
|
+
"CWEAVE",
|
|
51
|
+
"CTANGLE",
|
|
52
|
+
// automatic variables, as emitted
|
|
53
|
+
"$@",
|
|
54
|
+
"$%",
|
|
55
|
+
"$<",
|
|
56
|
+
"$?",
|
|
57
|
+
"$^",
|
|
58
|
+
"$+",
|
|
59
|
+
"$*",
|
|
60
|
+
"$|",
|
|
61
|
+
"$(@D)",
|
|
62
|
+
"$(@F)",
|
|
63
|
+
"$(%D)",
|
|
64
|
+
"$(%F)",
|
|
65
|
+
"$(<D)",
|
|
66
|
+
"$(<F)",
|
|
67
|
+
"$(?D)",
|
|
68
|
+
"$(?F)",
|
|
69
|
+
"$(^D)",
|
|
70
|
+
"$(^F)",
|
|
71
|
+
"$(+D)",
|
|
72
|
+
"$(+F)",
|
|
73
|
+
"$(*D)",
|
|
74
|
+
"$(*F)",
|
|
75
|
+
"$(|D)",
|
|
76
|
+
"$(|F)"
|
|
77
|
+
]);
|
|
78
|
+
var GNU_MAKE_NAMESPACE_ROOTS = [...GNU_MAKE_NAMESPACE];
|
|
79
|
+
function isBareMakeName(s) {
|
|
80
|
+
if (s.length === 0) return false;
|
|
81
|
+
const first = s.charCodeAt(0);
|
|
82
|
+
if (!(first >= 65 && first <= 90 || first >= 97 && first <= 122 || first === 95)) return false;
|
|
83
|
+
for (let i = 1; i < s.length; i++) {
|
|
84
|
+
const c = s.charCodeAt(i);
|
|
85
|
+
if (!(c >= 65 && c <= 90 || c >= 97 && c <= 122 || c >= 48 && c <= 57 || c === 95)) return false;
|
|
86
|
+
}
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
function makeExternalClaim(kind, target, evidenceClass) {
|
|
90
|
+
if (kind !== ASTEngine.RelationshipType.Uses) return {};
|
|
91
|
+
if (GNU_MAKE_NAMESPACE.has(target)) return { languageNamespace: GNU_MAKE_NAMESPACE_ROOTS };
|
|
92
|
+
if (evidenceClass === "external" && isBareMakeName(target)) return { isExternal: true };
|
|
93
|
+
return {};
|
|
94
|
+
}
|
|
95
|
+
var RealMakefileAnalyzer = class {
|
|
96
|
+
analyze(rootNode, content, filePath) {
|
|
97
|
+
const symbols = [];
|
|
98
|
+
const relationships = [];
|
|
99
|
+
const imports = [];
|
|
100
|
+
const exports = [];
|
|
101
|
+
const errors = [];
|
|
102
|
+
const patterns = [];
|
|
103
|
+
const moduleName = this.moduleName(filePath);
|
|
104
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
105
|
+
const relSeen = /* @__PURE__ */ new Set();
|
|
106
|
+
const declared = /* @__PURE__ */ new Set();
|
|
107
|
+
const localBinders = /* @__PURE__ */ new Set();
|
|
108
|
+
const declaredTargets = /* @__PURE__ */ new Set();
|
|
109
|
+
const valuesByName = /* @__PURE__ */ new Map();
|
|
110
|
+
const edgeCount = { references: 0, uses: 0, calls: 0, contains: 0, exports: 0, imports: 0 };
|
|
111
|
+
let conditionals = 0;
|
|
112
|
+
const declareSymbol = (name, kind, node, metadata) => {
|
|
113
|
+
if (!name) return null;
|
|
114
|
+
declared.add(name);
|
|
115
|
+
const key = `${name}\0${kind}`;
|
|
116
|
+
const existing = byKey.get(key);
|
|
117
|
+
if (existing) {
|
|
118
|
+
const m = existing.metadata;
|
|
119
|
+
if (metadata.value !== void 0) m.value = metadata.value;
|
|
120
|
+
if (metadata.operator !== void 0) m.operator = metadata.operator;
|
|
121
|
+
if (metadata.redeclared) m.redeclared = true;
|
|
122
|
+
return existing;
|
|
123
|
+
}
|
|
124
|
+
const sym = {
|
|
125
|
+
name,
|
|
126
|
+
kind,
|
|
127
|
+
range: this.nodeToRange(node),
|
|
128
|
+
visibility: "global",
|
|
129
|
+
metadata
|
|
130
|
+
};
|
|
131
|
+
byKey.set(key, sym);
|
|
132
|
+
symbols.push(sym);
|
|
133
|
+
return sym;
|
|
134
|
+
};
|
|
135
|
+
const addRel = (source, type, target, line, metadata) => {
|
|
136
|
+
if (!source || !target || source === target) return;
|
|
137
|
+
const key = `${source}\0${type}\0${target}\0${line}`;
|
|
138
|
+
if (relSeen.has(key)) return;
|
|
139
|
+
relSeen.add(key);
|
|
140
|
+
edgeCount[type] = (edgeCount[type] ?? 0) + 1;
|
|
141
|
+
relationships.push({
|
|
142
|
+
source,
|
|
143
|
+
target,
|
|
144
|
+
type,
|
|
145
|
+
confidence: 1,
|
|
146
|
+
metadata: metadata ?? {},
|
|
147
|
+
line_number: line,
|
|
148
|
+
line
|
|
149
|
+
});
|
|
150
|
+
};
|
|
151
|
+
const moduleSymbol = declareSymbol(moduleName, ASTEngine.SymbolKind.Module, rootNode, {
|
|
152
|
+
nodeType: "makefile",
|
|
153
|
+
isMakeModule: true,
|
|
154
|
+
filePath
|
|
155
|
+
});
|
|
156
|
+
const module = moduleName;
|
|
157
|
+
const staged = [];
|
|
158
|
+
const BUILTIN_VARIABLES = /* @__PURE__ */ new Set([
|
|
159
|
+
"MAKE",
|
|
160
|
+
"MAKEFILE_LIST",
|
|
161
|
+
"MAKEFILES",
|
|
162
|
+
"MAKEFLAGS",
|
|
163
|
+
"MAKECMDGOALS",
|
|
164
|
+
"MAKELEVEL",
|
|
165
|
+
"CURDIR",
|
|
166
|
+
"SUFFIXES",
|
|
167
|
+
".DEFAULT_GOAL",
|
|
168
|
+
"VPATH",
|
|
169
|
+
"SHELL",
|
|
170
|
+
"MFLAGS",
|
|
171
|
+
"CC",
|
|
172
|
+
"CXX",
|
|
173
|
+
"CPP",
|
|
174
|
+
"AR",
|
|
175
|
+
"ARFLAGS",
|
|
176
|
+
"AS",
|
|
177
|
+
"ASFLAGS",
|
|
178
|
+
"LD",
|
|
179
|
+
"LDFLAGS",
|
|
180
|
+
"LDLIBS",
|
|
181
|
+
"RM",
|
|
182
|
+
"CFLAGS",
|
|
183
|
+
"CXXFLAGS",
|
|
184
|
+
"CPPFLAGS",
|
|
185
|
+
"YFLAGS",
|
|
186
|
+
"LEX",
|
|
187
|
+
"YACC",
|
|
188
|
+
"AWK",
|
|
189
|
+
"FC",
|
|
190
|
+
"GET",
|
|
191
|
+
"PC",
|
|
192
|
+
"TEX",
|
|
193
|
+
"TEXI2DVI",
|
|
194
|
+
"WEAVE",
|
|
195
|
+
"CWEAVE",
|
|
196
|
+
"CTANGLE"
|
|
197
|
+
]);
|
|
198
|
+
const BUILTIN_FUNCTIONS = /* @__PURE__ */ new Set([
|
|
199
|
+
"subst",
|
|
200
|
+
"patsubst",
|
|
201
|
+
"strip",
|
|
202
|
+
"findstring",
|
|
203
|
+
"filter",
|
|
204
|
+
"filter-out",
|
|
205
|
+
"sort",
|
|
206
|
+
"word",
|
|
207
|
+
"wordlist",
|
|
208
|
+
"words",
|
|
209
|
+
"firstword",
|
|
210
|
+
"lastword",
|
|
211
|
+
"dir",
|
|
212
|
+
"notdir",
|
|
213
|
+
"suffix",
|
|
214
|
+
"basename",
|
|
215
|
+
"addsuffix",
|
|
216
|
+
"addprefix",
|
|
217
|
+
"join",
|
|
218
|
+
"wildcard",
|
|
219
|
+
"realpath",
|
|
220
|
+
"abspath",
|
|
221
|
+
"if",
|
|
222
|
+
"or",
|
|
223
|
+
"and",
|
|
224
|
+
"foreach",
|
|
225
|
+
"file",
|
|
226
|
+
"call",
|
|
227
|
+
"value",
|
|
228
|
+
"eval",
|
|
229
|
+
"origin",
|
|
230
|
+
"flavor",
|
|
231
|
+
"shell",
|
|
232
|
+
"error",
|
|
233
|
+
"warning",
|
|
234
|
+
"info"
|
|
235
|
+
]);
|
|
236
|
+
const KEYWORDS = /* @__PURE__ */ new Set([
|
|
237
|
+
"export",
|
|
238
|
+
"unexport",
|
|
239
|
+
"override",
|
|
240
|
+
"private",
|
|
241
|
+
"define",
|
|
242
|
+
"endef",
|
|
243
|
+
"include",
|
|
244
|
+
"-include",
|
|
245
|
+
"sinclude",
|
|
246
|
+
"vpath",
|
|
247
|
+
"undefine",
|
|
248
|
+
"ifeq",
|
|
249
|
+
"ifneq",
|
|
250
|
+
"ifdef",
|
|
251
|
+
"ifndef",
|
|
252
|
+
"else",
|
|
253
|
+
"endif",
|
|
254
|
+
"silent",
|
|
255
|
+
"quiet"
|
|
256
|
+
]);
|
|
257
|
+
const stack = [{ node: rootNode, owner: module }];
|
|
258
|
+
const trimOperator = (raw) => {
|
|
259
|
+
let end = raw.length;
|
|
260
|
+
while (end > 0) {
|
|
261
|
+
const c = raw.charCodeAt(end - 1);
|
|
262
|
+
if (c === 63 || c === 43 || c === 61 || c === 33 || c === 58) end--;
|
|
263
|
+
else break;
|
|
264
|
+
}
|
|
265
|
+
return raw.slice(0, end).trim();
|
|
266
|
+
};
|
|
267
|
+
const normaliseExpression = (raw) => {
|
|
268
|
+
const t = raw.trim();
|
|
269
|
+
const braced = t.length > 3 && t.startsWith("$(") && t.endsWith(")");
|
|
270
|
+
const curly = t.length > 3 && t.startsWith("${") && t.endsWith("}");
|
|
271
|
+
if (braced || curly) {
|
|
272
|
+
const inner = t.slice(2, -1).trim();
|
|
273
|
+
if (inner.length > 0 && this.isPlainIdentifier(inner)) return { name: inner, isExpression: false };
|
|
274
|
+
return { name: inner, isExpression: true };
|
|
275
|
+
}
|
|
276
|
+
return { name: t, isExpression: false };
|
|
277
|
+
};
|
|
278
|
+
const declaredName = (node) => {
|
|
279
|
+
const w = this.firstChildOfType(node, /* @__PURE__ */ new Set(["word"]));
|
|
280
|
+
if (!w) return "";
|
|
281
|
+
const raw = w.text.trim();
|
|
282
|
+
if (!raw) return "";
|
|
283
|
+
const trimmed = trimOperator(raw);
|
|
284
|
+
if (!trimmed || KEYWORDS.has(trimmed)) return "";
|
|
285
|
+
return trimmed;
|
|
286
|
+
};
|
|
287
|
+
const markExported = (name, node, via) => {
|
|
288
|
+
if (!name) return;
|
|
289
|
+
const existing = symbols.find((s) => s.name === name);
|
|
290
|
+
if (existing) existing.metadata.isExported = true;
|
|
291
|
+
exports.push({
|
|
292
|
+
name,
|
|
293
|
+
kind: existing?.kind ?? ASTEngine.SymbolKind.Constant,
|
|
294
|
+
range: this.nodeToRange(node),
|
|
295
|
+
metadata: { via }
|
|
296
|
+
});
|
|
297
|
+
addRel(module, ASTEngine.RelationshipType.Exports, name, node.startPosition.row + 1, { via, evidenceClass: "export_directive" });
|
|
298
|
+
};
|
|
299
|
+
const deferred = [];
|
|
300
|
+
const declaredAtLines = /* @__PURE__ */ new Map();
|
|
301
|
+
const overrideSites = [];
|
|
302
|
+
while (stack.length > 0) {
|
|
303
|
+
const frame = stack.pop();
|
|
304
|
+
const node = frame.node;
|
|
305
|
+
const owner = frame.owner;
|
|
306
|
+
const type = node.type;
|
|
307
|
+
const line = node.startPosition.row + 1;
|
|
308
|
+
let childOwner = owner;
|
|
309
|
+
switch (type) {
|
|
310
|
+
// ---- variables --------------------------------------------------
|
|
311
|
+
case "variable_assignment":
|
|
312
|
+
case "shell_assignment": {
|
|
313
|
+
const name = declaredName(node);
|
|
314
|
+
if (name) {
|
|
315
|
+
const valueNode = this.firstChildOfType(node, /* @__PURE__ */ new Set(["text", "shell_command"]));
|
|
316
|
+
const value = valueNode?.text?.trim() ?? "";
|
|
317
|
+
if (!valuesByName.has(name)) valuesByName.set(name, value);
|
|
318
|
+
declareSymbol(name, ASTEngine.SymbolKind.Constant, node, {
|
|
319
|
+
nodeType: type,
|
|
320
|
+
isMakeVariable: true,
|
|
321
|
+
operator: this.assignmentOperator(node),
|
|
322
|
+
value: value || void 0
|
|
323
|
+
});
|
|
324
|
+
patterns.push("Make variables");
|
|
325
|
+
addRel(module, ASTEngine.RelationshipType.Contains, name, line, { role: "declaration", evidenceClass: "declared_in_file" });
|
|
326
|
+
declaredAtLines.set(name, [...declaredAtLines.get(name) ?? [], line]);
|
|
327
|
+
childOwner = name;
|
|
328
|
+
}
|
|
329
|
+
break;
|
|
330
|
+
}
|
|
331
|
+
// A macro is a FUNCTION, not a constant: `$(call NAME)` is a call, and a
|
|
332
|
+
// call that resolves to a Constant is a callee that cannot be called.
|
|
333
|
+
case "define_directive": {
|
|
334
|
+
const name = declaredName(node);
|
|
335
|
+
if (name) {
|
|
336
|
+
const body = this.firstChildOfType(node, /* @__PURE__ */ new Set(["raw_text"]));
|
|
337
|
+
if (!valuesByName.has(name)) valuesByName.set(name, body?.text ?? "");
|
|
338
|
+
declareSymbol(name, ASTEngine.SymbolKind.Function, node, {
|
|
339
|
+
nodeType: type,
|
|
340
|
+
isMakeMacro: true,
|
|
341
|
+
operator: "define",
|
|
342
|
+
value: body?.text?.trim() || void 0
|
|
343
|
+
});
|
|
344
|
+
patterns.push("Make macros");
|
|
345
|
+
addRel(module, ASTEngine.RelationshipType.Contains, name, line, { role: "declaration", evidenceClass: "declared_in_file" });
|
|
346
|
+
childOwner = name;
|
|
347
|
+
}
|
|
348
|
+
break;
|
|
349
|
+
}
|
|
350
|
+
// ---- rules ------------------------------------------------------
|
|
351
|
+
case "rule": {
|
|
352
|
+
const targetsNode = this.firstChildOfType(node, /* @__PURE__ */ new Set(["targets"]));
|
|
353
|
+
const literalTargets = [];
|
|
354
|
+
if (targetsNode) {
|
|
355
|
+
for (let i = 0; i < targetsNode.namedChildCount; i++) {
|
|
356
|
+
const child = targetsNode.namedChild(i);
|
|
357
|
+
if (!child) continue;
|
|
358
|
+
const raw = child.text.trim();
|
|
359
|
+
if (!raw) continue;
|
|
360
|
+
if (child.type === "word") {
|
|
361
|
+
if (KEYWORDS.has(raw)) continue;
|
|
362
|
+
if (raw.startsWith(".")) continue;
|
|
363
|
+
literalTargets.push(raw);
|
|
364
|
+
declaredTargets.add(raw);
|
|
365
|
+
declareSymbol(raw, ASTEngine.SymbolKind.Function, node, {
|
|
366
|
+
nodeType: "rule",
|
|
367
|
+
isMakeTarget: true,
|
|
368
|
+
targetName: raw
|
|
369
|
+
});
|
|
370
|
+
addRel(module, ASTEngine.RelationshipType.Contains, raw, line, { role: "declaration", evidenceClass: "declared_in_file" });
|
|
371
|
+
patterns.push("Make rules");
|
|
372
|
+
} else {
|
|
373
|
+
this.collectFrom(
|
|
374
|
+
child,
|
|
375
|
+
(refName, intent, refLine) => {
|
|
376
|
+
const { name: norm } = normaliseExpression(refName);
|
|
377
|
+
staged.push({ source: module, target: norm || refName, line: refLine, intent, role: "computed_target", node: child });
|
|
378
|
+
},
|
|
379
|
+
(binder) => localBinders.add(binder)
|
|
380
|
+
);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
385
|
+
const child = node.namedChild(i);
|
|
386
|
+
if (child?.type !== "prerequisites") continue;
|
|
387
|
+
for (let j = 0; j < child.namedChildCount; j++) {
|
|
388
|
+
const prereq = child.namedChild(j);
|
|
389
|
+
if (!prereq) continue;
|
|
390
|
+
const raw = prereq.text.trim();
|
|
391
|
+
if (!raw) continue;
|
|
392
|
+
const { name } = normaliseExpression(raw);
|
|
393
|
+
if (!name || KEYWORDS.has(name)) continue;
|
|
394
|
+
for (const t of literalTargets.length ? literalTargets : [module]) {
|
|
395
|
+
staged.push({ source: t, target: name, line: prereq.startPosition.row + 1, intent: "ref", role: "prerequisite", node: prereq });
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
if (literalTargets.length > 0) childOwner = literalTargets[0];
|
|
400
|
+
break;
|
|
401
|
+
}
|
|
402
|
+
// ---- includes ---------------------------------------------------
|
|
403
|
+
case "include_directive": {
|
|
404
|
+
const isOptional = node.text.trim().startsWith("-") || node.text.trim().startsWith("s");
|
|
405
|
+
const listNode = this.firstChildOfType(node, /* @__PURE__ */ new Set(["list"]));
|
|
406
|
+
if (listNode) {
|
|
407
|
+
for (let i = 0; i < listNode.namedChildCount; i++) {
|
|
408
|
+
const child = listNode.namedChild(i);
|
|
409
|
+
if (!child) continue;
|
|
410
|
+
if (child.type === "word") {
|
|
411
|
+
const lit = child.text.trim();
|
|
412
|
+
if (lit && !KEYWORDS.has(lit)) {
|
|
413
|
+
const litLine = child.startPosition.row + 1;
|
|
414
|
+
imports.push({ module: lit, items: [], importType: "make_include", lineNumber: litLine, line: litLine, range: this.nodeToRange(node), isExternal: false, isOptional });
|
|
415
|
+
addRel(module, ASTEngine.RelationshipType.Imports, lit, litLine, { isMakeInclude: true, isOptional });
|
|
416
|
+
this.addIncludePartOf(addRel, module, lit, litLine, { isOptional, evidenceClass: "include_literal" });
|
|
417
|
+
}
|
|
418
|
+
continue;
|
|
419
|
+
}
|
|
420
|
+
if (child.type === "function_call" || child.type === "variable_reference" || child.type === "shell_function") {
|
|
421
|
+
const expr = child.text.trim();
|
|
422
|
+
const eLine = child.startPosition.row + 1;
|
|
423
|
+
if (expr) {
|
|
424
|
+
imports.push({ module: expr, items: [], importType: "make_include", lineNumber: eLine, line: eLine, range: this.nodeToRange(node), isExternal: false, isOptional, isComputed: true });
|
|
425
|
+
addRel(module, ASTEngine.RelationshipType.Imports, expr, eLine, { isMakeInclude: true, isOptional, isComputed: true, evidenceClass: "include_computed" });
|
|
426
|
+
}
|
|
427
|
+
continue;
|
|
428
|
+
}
|
|
429
|
+
this.collectFrom(child, (refName, _intent, refLine) => {
|
|
430
|
+
const mod = refName.trim();
|
|
431
|
+
if (!mod || KEYWORDS.has(mod)) return;
|
|
432
|
+
imports.push({
|
|
433
|
+
module: mod,
|
|
434
|
+
items: [],
|
|
435
|
+
importType: "make_include",
|
|
436
|
+
lineNumber: refLine,
|
|
437
|
+
line: refLine,
|
|
438
|
+
range: this.nodeToRange(node),
|
|
439
|
+
isExternal: false,
|
|
440
|
+
isOptional
|
|
441
|
+
});
|
|
442
|
+
addRel(module, ASTEngine.RelationshipType.Imports, mod, refLine, { isMakeInclude: true, isOptional, evidenceClass: "include_literal" });
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
} else {
|
|
446
|
+
this.collectFrom(node, () => {
|
|
447
|
+
});
|
|
448
|
+
const expr = listNode ? "" : node.text.trim();
|
|
449
|
+
if (expr) {
|
|
450
|
+
const eLine = node.startPosition.row + 1;
|
|
451
|
+
imports.push({ module: expr, items: [], importType: "make_include", lineNumber: eLine, line: eLine, range: this.nodeToRange(node), isExternal: false, isOptional, isComputed: true });
|
|
452
|
+
addRel(module, ASTEngine.RelationshipType.Imports, expr, eLine, { isMakeInclude: true, isOptional, isComputed: true });
|
|
453
|
+
}
|
|
454
|
+
this.collectFrom(node, (refName, _intent, refLine) => {
|
|
455
|
+
const mod = refName.trim();
|
|
456
|
+
if (!mod || KEYWORDS.has(mod)) return;
|
|
457
|
+
imports.push({ module: mod, items: [], importType: "make_include", lineNumber: refLine, line: refLine, range: this.nodeToRange(node), isExternal: false, isOptional });
|
|
458
|
+
addRel(module, ASTEngine.RelationshipType.Imports, mod, refLine, { isMakeInclude: true, isOptional });
|
|
459
|
+
});
|
|
460
|
+
}
|
|
461
|
+
patterns.push("Make includes");
|
|
462
|
+
break;
|
|
463
|
+
}
|
|
464
|
+
// ---- export -----------------------------------------------------
|
|
465
|
+
// `export FOO = 1` wraps an assignment (the generic walk already reaches
|
|
466
|
+
// it); `export FOO` wraps a `list` and is the ONLY place that name
|
|
467
|
+
// appears, so without this branch the export is invisible entirely.
|
|
468
|
+
case "export_directive": {
|
|
469
|
+
const listNode = this.firstChildOfType(node, /* @__PURE__ */ new Set(["list"]));
|
|
470
|
+
if (listNode) {
|
|
471
|
+
for (let i = 0; i < listNode.namedChildCount; i++) {
|
|
472
|
+
const w = listNode.namedChild(i);
|
|
473
|
+
if (!w) continue;
|
|
474
|
+
const name = trimOperator(w.text.trim());
|
|
475
|
+
if (!name || KEYWORDS.has(name)) continue;
|
|
476
|
+
if (!declared.has(name)) {
|
|
477
|
+
declareSymbol(name, ASTEngine.SymbolKind.Constant, w, { nodeType: "export_directive", isMakeVariable: true, declaredBy: "export" });
|
|
478
|
+
patterns.push("Make variables");
|
|
479
|
+
addRel(module, ASTEngine.RelationshipType.Contains, name, w.startPosition.row + 1, { role: "declaration", evidenceClass: "declared_in_file" });
|
|
480
|
+
}
|
|
481
|
+
markExported(name, w, "export_directive");
|
|
482
|
+
}
|
|
483
|
+
} else {
|
|
484
|
+
const inner = this.firstChildOfType(node, /* @__PURE__ */ new Set(["variable_assignment", "shell_assignment"]));
|
|
485
|
+
const name = inner ? declaredName(inner) : "";
|
|
486
|
+
if (name) markExported(name, node, "export_directive");
|
|
487
|
+
}
|
|
488
|
+
break;
|
|
489
|
+
}
|
|
490
|
+
case "unexport_directive": {
|
|
491
|
+
const listNode = this.firstChildOfType(node, /* @__PURE__ */ new Set(["list"]));
|
|
492
|
+
for (let i = 0; listNode && i < listNode.namedChildCount; i++) {
|
|
493
|
+
const w = listNode.namedChild(i);
|
|
494
|
+
if (!w) continue;
|
|
495
|
+
const name = trimOperator(w.text.trim());
|
|
496
|
+
if (!name) continue;
|
|
497
|
+
const s = symbols.find((x) => x.name === name);
|
|
498
|
+
if (s) s.metadata.isExported = false;
|
|
499
|
+
for (let k = exports.length - 1; k >= 0; k--) {
|
|
500
|
+
if (exports[k].name === name) exports.splice(k, 1);
|
|
501
|
+
}
|
|
502
|
+
for (let k = relationships.length - 1; k >= 0; k--) {
|
|
503
|
+
const r = relationships[k];
|
|
504
|
+
if (r.type === ASTEngine.RelationshipType.Exports && r.target === name) {
|
|
505
|
+
relationships.splice(k, 1);
|
|
506
|
+
edgeCount[ASTEngine.RelationshipType.Exports] = (edgeCount[ASTEngine.RelationshipType.Exports] ?? 1) - 1;
|
|
507
|
+
relSeen.delete(`${r.source}\0${r.type}\0${r.target}\0${r.line_number}`);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
break;
|
|
512
|
+
}
|
|
513
|
+
case "vpath_directive": {
|
|
514
|
+
patterns.push("Make vpath");
|
|
515
|
+
break;
|
|
516
|
+
}
|
|
517
|
+
// MAKE-G1, closed. `VPATH = src:include` is the ASSIGNMENT spelling of the
|
|
518
|
+
// search path; the sibling `vpath %.c src` DIRECTIVE has been recorded all
|
|
519
|
+
// along, so the two spellings of one feature were deliberately asymmetric
|
|
520
|
+
// pending corpus evidence, and the corpus had none (0 occurrences in the
|
|
521
|
+
// 100 pinned files).
|
|
522
|
+
//
|
|
523
|
+
// What was actually lost is not a "coverage" gap in the abstract: because
|
|
524
|
+
// `VPATH` was never declared, a reference to it fell to `uses` — the class
|
|
525
|
+
// this lane reserves for names nothing can declare — even though THIS file
|
|
526
|
+
// is the thing that declares it. `exports := $(VPATH)` in a file that sets
|
|
527
|
+
// `VPATH = src` was published as a read of something undeclared.
|
|
528
|
+
//
|
|
529
|
+
// So it is declared, as a `constant`: `VPATH` is a GNU Make builtin whose
|
|
530
|
+
// value a makefile may set, exactly like `CURDIR` or `MAKEFILE_LIST`, and
|
|
531
|
+
// recording it as an ordinary assignment is what makes the reference to it
|
|
532
|
+
// a `references` edge. The search directories are kept in metadata as a
|
|
533
|
+
// list, because the value text is colon-separated and a consumer asking
|
|
534
|
+
// "where does this makefile look for `foo.o`?" wants the list, not the
|
|
535
|
+
// string it was written as.
|
|
536
|
+
case "VPATH_assignment": {
|
|
537
|
+
const pathsNode = this.firstChildOfType(node, /* @__PURE__ */ new Set(["paths"]));
|
|
538
|
+
const dirs = [];
|
|
539
|
+
if (pathsNode) {
|
|
540
|
+
for (let i = 0; i < pathsNode.namedChildCount; i++) {
|
|
541
|
+
const w = pathsNode.namedChild(i);
|
|
542
|
+
if (w?.type === "word") {
|
|
543
|
+
const d = w.text.trim();
|
|
544
|
+
if (d) dirs.push(d);
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
const value = dirs.join(":");
|
|
549
|
+
if (!declared.has("VPATH")) {
|
|
550
|
+
declareSymbol("VPATH", ASTEngine.SymbolKind.Constant, node, {
|
|
551
|
+
nodeType: "VPATH_assignment",
|
|
552
|
+
isMakeVariable: true,
|
|
553
|
+
isMakeSearchPath: true,
|
|
554
|
+
operator: this.assignmentOperator(node),
|
|
555
|
+
value: value || void 0,
|
|
556
|
+
searchPaths: dirs.length ? dirs : void 0
|
|
557
|
+
});
|
|
558
|
+
patterns.push("Make vpath");
|
|
559
|
+
addRel(module, ASTEngine.RelationshipType.Contains, "VPATH", node.startPosition.row + 1, {
|
|
560
|
+
role: "declaration",
|
|
561
|
+
evidenceClass: "declared_in_file"
|
|
562
|
+
});
|
|
563
|
+
}
|
|
564
|
+
break;
|
|
565
|
+
}
|
|
566
|
+
// ---- control flow ------------------------------------------------
|
|
567
|
+
case "conditional":
|
|
568
|
+
case "ifeq_directive":
|
|
569
|
+
case "ifneq_directive":
|
|
570
|
+
case "ifdef_directive":
|
|
571
|
+
case "ifndef_directive":
|
|
572
|
+
case "else_directive":
|
|
573
|
+
case "elsif_directive": {
|
|
574
|
+
conditionals++;
|
|
575
|
+
break;
|
|
576
|
+
}
|
|
577
|
+
case "override_directive":
|
|
578
|
+
case "private_directive":
|
|
579
|
+
case "undefine_directive": {
|
|
580
|
+
let name = "";
|
|
581
|
+
if (type === "undefine_directive") {
|
|
582
|
+
const operand = node.childForFieldName("variable");
|
|
583
|
+
name = operand ? trimOperator(operand.text.trim()) : "";
|
|
584
|
+
} else {
|
|
585
|
+
const inner = this.firstChildOfType(node, /* @__PURE__ */ new Set(["variable_assignment", "shell_assignment"]));
|
|
586
|
+
name = inner ? declaredName(inner) : "";
|
|
587
|
+
}
|
|
588
|
+
if (name) {
|
|
589
|
+
const key = type === "private_directive" ? "isPrivate" : type === "override_directive" ? "isOverride" : "isUndefine";
|
|
590
|
+
deferred.push({ name, key, line: node.startPosition.row + 1 });
|
|
591
|
+
if (type === "override_directive") overrideSites.push({ name, line: node.startPosition.row + 1 });
|
|
592
|
+
const s = symbols.find((x) => x.name === name);
|
|
593
|
+
if (s) s.metadata[key] = true;
|
|
594
|
+
}
|
|
595
|
+
childOwner = name || owner;
|
|
596
|
+
break;
|
|
597
|
+
}
|
|
598
|
+
// ---- references --------------------------------------------------
|
|
599
|
+
// Handled wherever they appear, not only beneath an assignment or a
|
|
600
|
+
// rule. A bare top-level `$(foreach t,… $(eval $(call mk,$(t))))` — the
|
|
601
|
+
// idiom nomad/GNUmakefile uses to reify its package targets — is a
|
|
602
|
+
// `function_call` directly under `makefile`, and because the old walk
|
|
603
|
+
// only asked for references from two branches, that line contributed
|
|
604
|
+
// nothing at all.
|
|
605
|
+
case "variable_reference":
|
|
606
|
+
case "substitution_reference":
|
|
607
|
+
case "automatic_variable":
|
|
608
|
+
case "function_call": {
|
|
609
|
+
const parentType = this.parentType(node);
|
|
610
|
+
if (parentType !== "prerequisites" && parentType !== "targets") {
|
|
611
|
+
this.collectFrom(
|
|
612
|
+
node,
|
|
613
|
+
(refName, intent, refLine) => {
|
|
614
|
+
staged.push({ source: owner, target: refName, line: refLine, intent, role: type, node });
|
|
615
|
+
},
|
|
616
|
+
(binder) => localBinders.add(binder)
|
|
617
|
+
);
|
|
618
|
+
}
|
|
619
|
+
break;
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
for (let i = node.namedChildCount - 1; i >= 0; i--) {
|
|
623
|
+
const child = node.namedChild(i);
|
|
624
|
+
if (child) stack.push({ node: child, owner: childOwner });
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
for (const d of deferred) {
|
|
628
|
+
const s = symbols.find((x) => x.name === d.name);
|
|
629
|
+
if (s) s.metadata[d.key] = true;
|
|
630
|
+
}
|
|
631
|
+
for (const site of overrideSites) {
|
|
632
|
+
const base = (declaredAtLines.get(site.name) ?? []).filter((l) => l !== site.line);
|
|
633
|
+
if (base.length === 0) continue;
|
|
634
|
+
addRel(module, ASTEngine.RelationshipType.Overrides, site.name, site.line, {
|
|
635
|
+
role: "redefinition",
|
|
636
|
+
via: "override_directive",
|
|
637
|
+
overrideLine: site.line,
|
|
638
|
+
baseLine: Math.min(...base),
|
|
639
|
+
evidenceClass: "redefinition"
|
|
640
|
+
});
|
|
641
|
+
}
|
|
642
|
+
for (const e of staged) {
|
|
643
|
+
if (!e.target) continue;
|
|
644
|
+
const isBuiltinVar = BUILTIN_VARIABLES.has(e.target);
|
|
645
|
+
let kind;
|
|
646
|
+
let label;
|
|
647
|
+
if (e.intent === "call") {
|
|
648
|
+
kind = ASTEngine.RelationshipType.Calls;
|
|
649
|
+
label = "call";
|
|
650
|
+
} else if (declared.has(e.target)) {
|
|
651
|
+
kind = ASTEngine.RelationshipType.References;
|
|
652
|
+
label = "reference";
|
|
653
|
+
} else {
|
|
654
|
+
kind = ASTEngine.RelationshipType.Uses;
|
|
655
|
+
label = e.target.startsWith("$$") ? "shell_variable" : e.role === "automatic_variable" ? "automatic" : localBinders.has(e.target) ? "local_binding" : isBuiltinVar ? "builtin" : e.role === "prerequisite" ? "external_prerequisite" : "external";
|
|
656
|
+
}
|
|
657
|
+
addRel(e.source, kind, e.target, e.line, {
|
|
658
|
+
role: e.role,
|
|
659
|
+
evidenceClass: label,
|
|
660
|
+
isBuiltin: isBuiltinVar,
|
|
661
|
+
...makeExternalClaim(kind, e.target, label)
|
|
662
|
+
});
|
|
663
|
+
}
|
|
664
|
+
for (const t of declaredTargets) {
|
|
665
|
+
if (symbols.some((s) => s.name === t && s.metadata?.isExported === true)) continue;
|
|
666
|
+
const sym = symbols.find((s) => s.name === t);
|
|
667
|
+
exports.push({ name: t, kind: ASTEngine.SymbolKind.Function, range: sym?.range, metadata: { via: "target" } });
|
|
668
|
+
addRel(module, ASTEngine.RelationshipType.Exports, t, sym?.range?.startLine ?? 1, { via: "target", evidenceClass: "export_target" });
|
|
669
|
+
}
|
|
670
|
+
return {
|
|
671
|
+
language: "make",
|
|
672
|
+
symbols,
|
|
673
|
+
relationships,
|
|
674
|
+
imports,
|
|
675
|
+
exports,
|
|
676
|
+
errors,
|
|
677
|
+
metadata: {
|
|
678
|
+
// A Make-specific, RELATIVE complexity: buildable targets plus decision
|
|
679
|
+
// points. Absolute timings are meaningless across 99 files of very
|
|
680
|
+
// different sizes, and this is only ever compared within the analyzer
|
|
681
|
+
// family, so it is documented as a count and not as a cost.
|
|
682
|
+
complexity: declaredTargets.size + conditionals,
|
|
683
|
+
linesOfCode: content.split("\n").length,
|
|
684
|
+
patterns: [...new Set(patterns)],
|
|
685
|
+
framework: "Make",
|
|
686
|
+
variableCount: valuesByName.size,
|
|
687
|
+
targetCount: declaredTargets.size,
|
|
688
|
+
conditionalCount: conditionals,
|
|
689
|
+
moduleName,
|
|
690
|
+
edgeCount
|
|
691
|
+
}
|
|
692
|
+
};
|
|
693
|
+
}
|
|
694
|
+
/** The assignment operator between name and value, from node positions. */
|
|
695
|
+
assignmentOperator(node) {
|
|
696
|
+
const nameNode = this.firstChildOfType(node, /* @__PURE__ */ new Set(["word"]));
|
|
697
|
+
const valueNode = this.firstChildOfType(node, /* @__PURE__ */ new Set(["text", "shell_command"]));
|
|
698
|
+
if (nameNode && valueNode) {
|
|
699
|
+
const text = node.text ?? "";
|
|
700
|
+
const gap = text.slice(nameNode.endIndex - node.startIndex, valueNode.startIndex - node.startIndex).trim();
|
|
701
|
+
if (gap) return gap;
|
|
702
|
+
}
|
|
703
|
+
return "=";
|
|
704
|
+
}
|
|
705
|
+
/**
|
|
706
|
+
* Every reference inside `node`, as (name, intent, line).
|
|
707
|
+
*
|
|
708
|
+
* Handles the four reference-bearing node types the grammar produces and
|
|
709
|
+
* nothing else. `substitution_reference` is a sibling of `variable_reference`,
|
|
710
|
+
* not a child of it, so reading only the latter loses `$(VAR:.c=.o)`.
|
|
711
|
+
*/
|
|
712
|
+
collectFrom(node, emit, onForeachBinder = () => {
|
|
713
|
+
}) {
|
|
714
|
+
const BUILTIN_FUNCTIONS = /* @__PURE__ */ new Set([
|
|
715
|
+
"subst",
|
|
716
|
+
"patsubst",
|
|
717
|
+
"strip",
|
|
718
|
+
"findstring",
|
|
719
|
+
"filter",
|
|
720
|
+
"filter-out",
|
|
721
|
+
"sort",
|
|
722
|
+
"word",
|
|
723
|
+
"wordlist",
|
|
724
|
+
"words",
|
|
725
|
+
"firstword",
|
|
726
|
+
"lastword",
|
|
727
|
+
"dir",
|
|
728
|
+
"notdir",
|
|
729
|
+
"suffix",
|
|
730
|
+
"basename",
|
|
731
|
+
"addsuffix",
|
|
732
|
+
"addprefix",
|
|
733
|
+
"join",
|
|
734
|
+
"wildcard",
|
|
735
|
+
"realpath",
|
|
736
|
+
"abspath",
|
|
737
|
+
"if",
|
|
738
|
+
"or",
|
|
739
|
+
"and",
|
|
740
|
+
"foreach",
|
|
741
|
+
"file",
|
|
742
|
+
"call",
|
|
743
|
+
"value",
|
|
744
|
+
"eval",
|
|
745
|
+
"origin",
|
|
746
|
+
"flavor",
|
|
747
|
+
"shell",
|
|
748
|
+
"error",
|
|
749
|
+
"warning",
|
|
750
|
+
"info"
|
|
751
|
+
]);
|
|
752
|
+
const stack = [node];
|
|
753
|
+
while (stack.length > 0) {
|
|
754
|
+
const n = stack.pop();
|
|
755
|
+
const line = n.startPosition.row + 1;
|
|
756
|
+
if (n.type === "variable_reference" || n.type === "substitution_reference") {
|
|
757
|
+
if (n.text.startsWith("$$")) {
|
|
758
|
+
for (let i = n.namedChildCount - 1; i >= 0; i--) {
|
|
759
|
+
const c = n.namedChild(i);
|
|
760
|
+
if (c) stack.push(c);
|
|
761
|
+
}
|
|
762
|
+
continue;
|
|
763
|
+
}
|
|
764
|
+
const w = this.firstChildOfType(n, /* @__PURE__ */ new Set(["word"]));
|
|
765
|
+
const name = w?.text?.trim() ?? "";
|
|
766
|
+
if (name) {
|
|
767
|
+
if (!this.isAllDigits(name)) emit(name, "ref", line);
|
|
768
|
+
} else {
|
|
769
|
+
const cat = this.firstChildOfType(n, /* @__PURE__ */ new Set(["concatenation"]));
|
|
770
|
+
const catName = cat?.text?.trim() ?? "";
|
|
771
|
+
if (catName && !this.isAllDigits(catName)) emit(catName, "ref", line);
|
|
772
|
+
}
|
|
773
|
+
} else if (n.type === "automatic_variable") {
|
|
774
|
+
const w = this.firstChildOfType(n, /* @__PURE__ */ new Set(["word"]));
|
|
775
|
+
emit(w?.text?.trim() || n.text.trim(), "ref", line);
|
|
776
|
+
} else if (n.type === "function_call") {
|
|
777
|
+
const argsNode = this.firstChildOfType(n, /* @__PURE__ */ new Set(["arguments"]));
|
|
778
|
+
const nameEnd = argsNode ? argsNode.startIndex - n.startIndex : Math.max(0, n.text.length - 1);
|
|
779
|
+
const name = this.firstToken(n.text.slice(2, nameEnd).trim());
|
|
780
|
+
if (name === "foreach" && argsNode) {
|
|
781
|
+
const binder = (argsNode.text ?? "").split(",")[0]?.trim() ?? "";
|
|
782
|
+
if (binder) onForeachBinder(binder);
|
|
783
|
+
}
|
|
784
|
+
if (name === "call") {
|
|
785
|
+
const callee = (argsNode?.text ?? "").split(",")[0]?.trim() ?? "";
|
|
786
|
+
if (callee && !BUILTIN_FUNCTIONS.has(callee) && !this.isAllDigits(callee)) emit(callee, "call", line);
|
|
787
|
+
} else if (name && !BUILTIN_FUNCTIONS.has(name)) {
|
|
788
|
+
emit(name, "ref", line);
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
for (let i = n.namedChildCount - 1; i >= 0; i--) {
|
|
792
|
+
const c = n.namedChild(i);
|
|
793
|
+
if (c) stack.push(c);
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
parentType(node) {
|
|
798
|
+
const p = node.parent;
|
|
799
|
+
return p ? String(p.type) : "";
|
|
800
|
+
}
|
|
801
|
+
/**
|
|
802
|
+
* The module symbol's name: the file's own name.
|
|
803
|
+
*
|
|
804
|
+
* The path is split by CHARACTER CODE, not by a pattern. This function used
|
|
805
|
+
* to end in a `String.prototype.split` on a two-character regular expression,
|
|
806
|
+
* which was the only regex literal in the file, and
|
|
807
|
+
* `script/m12/make-contract-probe.ts`'s "analyzer source has no regex
|
|
808
|
+
* literals" check found it — a gate that existed, ran, and had simply never
|
|
809
|
+
* been executed for this lane. A rule that is never run is not a rule.
|
|
810
|
+
*
|
|
811
|
+
* (The comment above used to quote the old expression verbatim, which made the
|
|
812
|
+
* detector fire on the COMMENT. The detector only exempts `//` line comments,
|
|
813
|
+
* and that strictness is correct: a regex quoted in a doc comment reads to the
|
|
814
|
+
* next maintainer as a regex in the file.)
|
|
815
|
+
*/
|
|
816
|
+
moduleName(filePath) {
|
|
817
|
+
if (!filePath) return "makefile";
|
|
818
|
+
let end = filePath.length;
|
|
819
|
+
while (end > 0) {
|
|
820
|
+
const c = filePath.charCodeAt(end - 1);
|
|
821
|
+
if (c === 47 || c === 92) break;
|
|
822
|
+
end--;
|
|
823
|
+
}
|
|
824
|
+
return filePath.slice(end) || "makefile";
|
|
825
|
+
}
|
|
826
|
+
/**
|
|
827
|
+
* The module-symbol name an included path would carry, or "" when the path's
|
|
828
|
+
* last segment is an expression or syntax rather than a name.
|
|
829
|
+
*
|
|
830
|
+
* Every makefile is given ONE module symbol named for its file (the same
|
|
831
|
+
* spelling `moduleName()` produces), so the symbol-shaped target for an
|
|
832
|
+
* include is the INCLUDED FILE'S BASENAME — never the written path, which
|
|
833
|
+
* the resolver has no space for. The path is kept in `metadata.partPath`
|
|
834
|
+
* where a reader can check it against the source line. A glob (`*.mk`) or a
|
|
835
|
+
* path whose last segment contains `$`, quotes or shell metacharacters names
|
|
836
|
+
* no file this parse can know, so it is refused rather than guessed at.
|
|
837
|
+
*/
|
|
838
|
+
includePartSymbol(pathText) {
|
|
839
|
+
const slash = pathText.lastIndexOf("/");
|
|
840
|
+
const base = slash >= 0 ? pathText.slice(slash + 1) : pathText;
|
|
841
|
+
if (base.length === 0) return "";
|
|
842
|
+
for (let i = 0; i < base.length; i++) {
|
|
843
|
+
const c = base.charCodeAt(i);
|
|
844
|
+
if (c <= 32 || c === 34 || c === 39 || c === 96 || c === 36 || c === 40 || c === 41 || c === 123 || c === 125 || c === 91 || c === 93 || c === 42 || c === 63 || c === 59 || c === 124 || c === 38 || c === 60 || c === 62 || c === 92 || c === 126) {
|
|
845
|
+
return "";
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
return base;
|
|
849
|
+
}
|
|
850
|
+
/**
|
|
851
|
+
* `include` IS the textual-inclusion construct: make splices the included
|
|
852
|
+
* file's text into THIS make instance's scope — the same single-shared-scope
|
|
853
|
+
* fact bash's `source` asserts — so a literal include earns a `part_of` edge
|
|
854
|
+
* from this file's module to the INCLUDED file's module symbol.
|
|
855
|
+
*
|
|
856
|
+
* Computed includes (`include $(shell …)`) and glob operands name no file
|
|
857
|
+
* this parse can know, so they earn no edge here: the `imports` row already
|
|
858
|
+
* records the expression, and a `part_of` whose target is an expression
|
|
859
|
+
* string would be an edge to nothing.
|
|
860
|
+
*/
|
|
861
|
+
addIncludePartOf(addRel, source, pathText, line, extra) {
|
|
862
|
+
const partSymbol = this.includePartSymbol(pathText);
|
|
863
|
+
if (!partSymbol) return;
|
|
864
|
+
addRel(source, ASTEngine.RelationshipType.PartOf, partSymbol, line, {
|
|
865
|
+
// MANDATORY on every `part_of` row: the direction is the directive's own,
|
|
866
|
+
// and this file carries it and names the part.
|
|
867
|
+
partRole: "library",
|
|
868
|
+
partPath: pathText,
|
|
869
|
+
partSymbol,
|
|
870
|
+
isMakeIncludePart: true,
|
|
871
|
+
...extra
|
|
872
|
+
});
|
|
873
|
+
}
|
|
874
|
+
isPlainIdentifier(s) {
|
|
875
|
+
if (s.length === 0) return false;
|
|
876
|
+
const first = s.charCodeAt(0);
|
|
877
|
+
const okFirst = (c) => c >= 65 && c <= 90 || c >= 97 && c <= 122 || c === 95;
|
|
878
|
+
if (!okFirst(first)) return false;
|
|
879
|
+
for (let i = 1; i < s.length; i++) {
|
|
880
|
+
const c = s.charCodeAt(i);
|
|
881
|
+
if (!okFirst(c) && !(c >= 48 && c <= 57) && c !== 46 && c !== 45 && c !== 47) return false;
|
|
882
|
+
}
|
|
883
|
+
return true;
|
|
884
|
+
}
|
|
885
|
+
firstChildOfType(node, types) {
|
|
886
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
887
|
+
const child = node.namedChild(i);
|
|
888
|
+
if (child && types.has(child.type)) return child;
|
|
889
|
+
}
|
|
890
|
+
return null;
|
|
891
|
+
}
|
|
892
|
+
isAllDigits(name) {
|
|
893
|
+
if (name.length === 0) return false;
|
|
894
|
+
for (let i = 0; i < name.length; i++) {
|
|
895
|
+
const c = name.charCodeAt(i);
|
|
896
|
+
if (c < 48 || c > 57) return false;
|
|
897
|
+
}
|
|
898
|
+
return true;
|
|
899
|
+
}
|
|
900
|
+
firstToken(raw) {
|
|
901
|
+
for (let i = 0; i < raw.length; i++) {
|
|
902
|
+
const c = raw[i];
|
|
903
|
+
if (c === " " || c === " " || c === "," || c === "(") return raw.slice(0, i);
|
|
904
|
+
}
|
|
905
|
+
return raw;
|
|
906
|
+
}
|
|
907
|
+
nodeToRange(node) {
|
|
908
|
+
return {
|
|
909
|
+
startLine: node.startPosition.row + 1,
|
|
910
|
+
startColumn: node.startPosition.column,
|
|
911
|
+
endLine: node.endPosition.row + 1,
|
|
912
|
+
endColumn: node.endPosition.column,
|
|
913
|
+
startIndex: node.startIndex,
|
|
914
|
+
endIndex: node.endIndex
|
|
915
|
+
};
|
|
916
|
+
}
|
|
917
|
+
};
|
|
918
|
+
export {
|
|
919
|
+
RealMakefileAnalyzer
|
|
920
|
+
};
|