@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,4101 @@
|
|
|
1
|
+
import {
|
|
2
|
+
buildResolutionTables
|
|
3
|
+
} from "./chunk-2DE27DVC.js";
|
|
4
|
+
import {
|
|
5
|
+
ASTEngine
|
|
6
|
+
} from "./chunk-3ZTOHHSW.js";
|
|
7
|
+
import "./chunk-BQ3OVQ3P.js";
|
|
8
|
+
import "./chunk-SK6YVHFZ.js";
|
|
9
|
+
import "./chunk-2BSKD7O3.js";
|
|
10
|
+
import "./chunk-KAET47ZL.js";
|
|
11
|
+
import "./chunk-KCDECRJY.js";
|
|
12
|
+
import "./chunk-MLKGABMK.js";
|
|
13
|
+
|
|
14
|
+
// src/ast/real-cpp-analyzer.ts
|
|
15
|
+
var CPP_CITATION_STDLIB_ROOTS = /* @__PURE__ */ new Set([
|
|
16
|
+
"move",
|
|
17
|
+
"string",
|
|
18
|
+
"max",
|
|
19
|
+
"infinity",
|
|
20
|
+
"min",
|
|
21
|
+
"quiet_NaN",
|
|
22
|
+
"make_pair",
|
|
23
|
+
"isnan",
|
|
24
|
+
"signaling_NaN",
|
|
25
|
+
"swap",
|
|
26
|
+
"make_tuple",
|
|
27
|
+
"sort",
|
|
28
|
+
"to_string",
|
|
29
|
+
"forward",
|
|
30
|
+
"make_shared",
|
|
31
|
+
"make_unique",
|
|
32
|
+
"size_t",
|
|
33
|
+
"vector",
|
|
34
|
+
"map",
|
|
35
|
+
"unordered_map",
|
|
36
|
+
"set",
|
|
37
|
+
"pair",
|
|
38
|
+
"tuple",
|
|
39
|
+
"optional",
|
|
40
|
+
"variant",
|
|
41
|
+
"function",
|
|
42
|
+
"shared_ptr",
|
|
43
|
+
"unique_ptr",
|
|
44
|
+
"string_view",
|
|
45
|
+
"numeric_limits",
|
|
46
|
+
"find",
|
|
47
|
+
"begin",
|
|
48
|
+
"end",
|
|
49
|
+
"size",
|
|
50
|
+
"copy",
|
|
51
|
+
"fill",
|
|
52
|
+
"count",
|
|
53
|
+
"abs",
|
|
54
|
+
"fabs",
|
|
55
|
+
"sqrt",
|
|
56
|
+
"pow",
|
|
57
|
+
"floor",
|
|
58
|
+
"ceil",
|
|
59
|
+
"round",
|
|
60
|
+
"log",
|
|
61
|
+
"exp"
|
|
62
|
+
]);
|
|
63
|
+
function cppCitationFacts(qualified, leaf) {
|
|
64
|
+
if (process.env.BB_CPP_CITATION !== "1") return {};
|
|
65
|
+
if (leaf === "" || !CPP_CITATION_STDLIB_ROOTS.has(leaf)) return {};
|
|
66
|
+
let t = String(qualified ?? "").trim();
|
|
67
|
+
if (t.startsWith("::")) t = t.slice(2);
|
|
68
|
+
const lt = t.indexOf("<");
|
|
69
|
+
if (lt > 0) t = t.slice(0, lt);
|
|
70
|
+
if (t.split("::")[0]?.trim() !== "std") return {};
|
|
71
|
+
return { languageNamespace: [leaf], languageNamespacePolicy: "strict-absent" };
|
|
72
|
+
}
|
|
73
|
+
function qualifiedOwnerSegment(qualified, leaf) {
|
|
74
|
+
if (qualified === "" || leaf === "") return null;
|
|
75
|
+
let depth = 0;
|
|
76
|
+
let flat = "";
|
|
77
|
+
for (let i = 0; i < qualified.length; i++) {
|
|
78
|
+
const ch = qualified[i];
|
|
79
|
+
if (ch === "<") depth++;
|
|
80
|
+
else if (ch === ">") depth = Math.max(0, depth - 1);
|
|
81
|
+
else if (depth === 0) flat += ch;
|
|
82
|
+
}
|
|
83
|
+
const t = flat.trim();
|
|
84
|
+
const cut = t.lastIndexOf("::");
|
|
85
|
+
if (cut <= 0) return null;
|
|
86
|
+
const segs = t.split("::").map((x) => x.trim());
|
|
87
|
+
for (const seg of segs) if (!isIdentifierShapeWithDollar(seg)) return null;
|
|
88
|
+
if (segs[segs.length - 1] !== leaf) return null;
|
|
89
|
+
return segs.length >= 2 ? segs[segs.length - 2] : null;
|
|
90
|
+
}
|
|
91
|
+
function isIdentifierShapeWithDollar(s) {
|
|
92
|
+
if (s.length === 0) return false;
|
|
93
|
+
const c0 = s.charCodeAt(0);
|
|
94
|
+
const headOk = c0 >= 65 && c0 <= 90 || c0 >= 97 && c0 <= 122 || c0 === 95 || c0 === 36;
|
|
95
|
+
if (!headOk) return false;
|
|
96
|
+
for (let i = 1; i < s.length; i++) {
|
|
97
|
+
const c = s.charCodeAt(i);
|
|
98
|
+
const ok = c >= 65 && c <= 90 || c >= 97 && c <= 122 || c >= 48 && c <= 57 || c === 95 || c === 36;
|
|
99
|
+
if (!ok) return false;
|
|
100
|
+
}
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
function collapseTypeNoise(t) {
|
|
104
|
+
let out = "";
|
|
105
|
+
let pending = false;
|
|
106
|
+
for (let i = 0; i < t.length; i++) {
|
|
107
|
+
const c = t[i];
|
|
108
|
+
const isNoise = c === "*" || c === "&" || c === " " || c === " " || c === "\n" || c === "\r" || c === "\f" || c === "\v";
|
|
109
|
+
if (isNoise) {
|
|
110
|
+
if (out.length > 0) pending = true;
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
if (pending) {
|
|
114
|
+
out += " ";
|
|
115
|
+
pending = false;
|
|
116
|
+
}
|
|
117
|
+
out += c;
|
|
118
|
+
}
|
|
119
|
+
return out.trim();
|
|
120
|
+
}
|
|
121
|
+
function stringIncludes(str, val) {
|
|
122
|
+
return str.indexOf(val) !== -1;
|
|
123
|
+
}
|
|
124
|
+
function stripIncludeChars(text) {
|
|
125
|
+
let out = "";
|
|
126
|
+
for (let i = 0; i < text.length; i++) {
|
|
127
|
+
const c = text[i];
|
|
128
|
+
if (c !== "<" && c !== ">" && c !== '"' && c !== "'") out += c;
|
|
129
|
+
}
|
|
130
|
+
return out;
|
|
131
|
+
}
|
|
132
|
+
function swapChar(text, from, to) {
|
|
133
|
+
let out = "";
|
|
134
|
+
for (let i = 0; i < text.length; i++) out += text[i] === from ? to : text[i];
|
|
135
|
+
return out;
|
|
136
|
+
}
|
|
137
|
+
function cppPackageScopeFacts(leaf, qualified, includeRoots, declaredPackages) {
|
|
138
|
+
if (process.env.BB_CPP_PACKAGE_SCOPE !== "1") return {};
|
|
139
|
+
if (leaf === "" || qualified === "" || includeRoots.size === 0 || declaredPackages.length === 0) return {};
|
|
140
|
+
const cut = qualified.lastIndexOf("::");
|
|
141
|
+
if (cut <= 0) return {};
|
|
142
|
+
const ns = qualified.slice(0, cut).trim().toLowerCase();
|
|
143
|
+
if (ns.length === 0) return {};
|
|
144
|
+
const sep = ns.indexOf("::");
|
|
145
|
+
const root = (sep >= 0 ? ns.slice(0, sep) : ns).trim();
|
|
146
|
+
if (root.length === 0) return {};
|
|
147
|
+
if (!includeRoots.has(root)) return {};
|
|
148
|
+
const matched = [];
|
|
149
|
+
for (const raw of declaredPackages) {
|
|
150
|
+
const pkg = String(raw ?? "").trim();
|
|
151
|
+
if (pkg.length === 0) continue;
|
|
152
|
+
const lower = pkg.toLowerCase();
|
|
153
|
+
if (root === lower || root === swapChar(lower, "_", "-") || root === swapChar(lower, "-", "_")) {
|
|
154
|
+
matched.push(pkg);
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
if (root.length >= 3 && lower.startsWith(root)) {
|
|
158
|
+
const next = lower.charAt(root.length);
|
|
159
|
+
if (next === "_" || next === "-" || next === "." || next === "/") matched.push(pkg);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
if (matched.length !== 1) return {};
|
|
163
|
+
return { packageScope: { package: matched[0], name: leaf } };
|
|
164
|
+
}
|
|
165
|
+
function cppExternalIncludeRoots(root) {
|
|
166
|
+
const roots = /* @__PURE__ */ new Set();
|
|
167
|
+
const stack = [root];
|
|
168
|
+
while (stack.length > 0) {
|
|
169
|
+
const n = stack.pop();
|
|
170
|
+
if (!n) continue;
|
|
171
|
+
if (n.type === "preproc_include") {
|
|
172
|
+
const pathNode = n.childForFieldName("path");
|
|
173
|
+
const raw = (pathNode?.text ?? "").trim();
|
|
174
|
+
if (raw.startsWith("<")) {
|
|
175
|
+
const p = stripIncludeChars(raw).trim();
|
|
176
|
+
const slash = p.indexOf("/");
|
|
177
|
+
if (slash > 0) {
|
|
178
|
+
const seg = p.slice(0, slash).toLowerCase();
|
|
179
|
+
if (seg.length > 0) roots.add(seg);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
for (let i = 0; i < n.childCount; i++) {
|
|
184
|
+
const c = n.child(i);
|
|
185
|
+
if (c) stack.push(c);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return roots;
|
|
189
|
+
}
|
|
190
|
+
function cppPartTargetPath(filePath, specifier) {
|
|
191
|
+
const dir = filePath.includes("/") ? filePath.slice(0, filePath.lastIndexOf("/")) : "";
|
|
192
|
+
const joined = dir === "" ? specifier : `${dir}/${specifier}`;
|
|
193
|
+
const absolute = joined.startsWith("/");
|
|
194
|
+
const out = [];
|
|
195
|
+
for (const seg of joined.split("/")) {
|
|
196
|
+
if (seg === "" || seg === ".") continue;
|
|
197
|
+
if (seg === "..") {
|
|
198
|
+
if (out.length > 0 && out[out.length - 1] !== "..") out.pop();
|
|
199
|
+
else out.push("..");
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
out.push(seg);
|
|
203
|
+
}
|
|
204
|
+
return `${absolute ? "/" : ""}${out.join("/")}`;
|
|
205
|
+
}
|
|
206
|
+
function cppPartSymbol(specifier) {
|
|
207
|
+
const slash = specifier.lastIndexOf("/");
|
|
208
|
+
const base = slash >= 0 ? specifier.slice(slash + 1) : specifier;
|
|
209
|
+
const dot = base.lastIndexOf(".");
|
|
210
|
+
const stem = dot > 0 ? base.slice(0, dot) : base;
|
|
211
|
+
return stem.trim();
|
|
212
|
+
}
|
|
213
|
+
function stripAccessSpecifier(text) {
|
|
214
|
+
const lower = text.toLowerCase();
|
|
215
|
+
if (lower.startsWith("public")) return text.slice("public".length).trim();
|
|
216
|
+
if (lower.startsWith("protected")) return text.slice("protected".length).trim();
|
|
217
|
+
if (lower.startsWith("private")) return text.slice("private".length).trim();
|
|
218
|
+
return text.trim();
|
|
219
|
+
}
|
|
220
|
+
function lastScopeSegment(text) {
|
|
221
|
+
let start = 0;
|
|
222
|
+
let out = "";
|
|
223
|
+
for (let i = 0; i <= text.length; i++) {
|
|
224
|
+
const atEnd = i === text.length;
|
|
225
|
+
const c = atEnd ? ":" : text[i];
|
|
226
|
+
const isSep = c === ":" || c === " " || c === " " || c === "\n" || c === "\r";
|
|
227
|
+
if (isSep) {
|
|
228
|
+
if (i > start) out = text.slice(start, i);
|
|
229
|
+
start = i + 1;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
return out;
|
|
233
|
+
}
|
|
234
|
+
function collapseSpaces(text) {
|
|
235
|
+
let out = "";
|
|
236
|
+
let pendingSpace = false;
|
|
237
|
+
for (let i = 0; i < text.length; i++) {
|
|
238
|
+
const c = text[i];
|
|
239
|
+
const isWs = c === " " || c === " " || c === "\n" || c === "\r" || c === "\f" || c === "\v";
|
|
240
|
+
if (isWs) {
|
|
241
|
+
if (out) pendingSpace = true;
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
244
|
+
if (pendingSpace) {
|
|
245
|
+
out += " ";
|
|
246
|
+
pendingSpace = false;
|
|
247
|
+
}
|
|
248
|
+
out += c;
|
|
249
|
+
}
|
|
250
|
+
return out;
|
|
251
|
+
}
|
|
252
|
+
function sameExtent(a, b) {
|
|
253
|
+
if (!a || !b) return false;
|
|
254
|
+
return a.startIndex === b.startIndex && a.endIndex === b.endIndex;
|
|
255
|
+
}
|
|
256
|
+
function isMacroDecoratedClassFunction(node) {
|
|
257
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
258
|
+
const c = node.namedChild(i);
|
|
259
|
+
if (c && macroDecoratedClassDecl(c)) return true;
|
|
260
|
+
}
|
|
261
|
+
return false;
|
|
262
|
+
}
|
|
263
|
+
function isMacroSpelling(s) {
|
|
264
|
+
if (s.length < 1) return false;
|
|
265
|
+
const first = s.charCodeAt(0);
|
|
266
|
+
if (first < 65 || first > 90) return false;
|
|
267
|
+
for (let i = 1; i < s.length; i++) {
|
|
268
|
+
const c = s.charCodeAt(i);
|
|
269
|
+
const upper = c >= 65 && c <= 90;
|
|
270
|
+
const digit = c >= 48 && c <= 57;
|
|
271
|
+
if (!upper && !digit && c !== 95) return false;
|
|
272
|
+
}
|
|
273
|
+
return true;
|
|
274
|
+
}
|
|
275
|
+
var CPP_ANONYMOUS = "anonymous";
|
|
276
|
+
function macroDecoratedClassDecl(spec) {
|
|
277
|
+
if (spec.type !== "class_specifier" && spec.type !== "struct_specifier" && spec.type !== "union_specifier") return null;
|
|
278
|
+
for (let i = 0; i < spec.namedChildCount; i++) {
|
|
279
|
+
const c = spec.namedChild(i);
|
|
280
|
+
if (c && c.type === "field_declaration_list") return null;
|
|
281
|
+
}
|
|
282
|
+
const nameField = spec.childForFieldName?.("name")?.text?.trim();
|
|
283
|
+
if (!nameField || !isMacroSpelling(nameField)) return null;
|
|
284
|
+
const parent = spec.parent;
|
|
285
|
+
if (!parent || parent.type !== "function_definition") return null;
|
|
286
|
+
let body = null;
|
|
287
|
+
for (let i = 0; i < parent.namedChildCount; i++) {
|
|
288
|
+
const c = parent.namedChild(i);
|
|
289
|
+
if (c && c.type === "compound_statement") {
|
|
290
|
+
body = c;
|
|
291
|
+
break;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
if (!body) return null;
|
|
295
|
+
const siblings = [];
|
|
296
|
+
let prevWasMacro = false;
|
|
297
|
+
for (let i = 0; i < parent.namedChildCount; i++) {
|
|
298
|
+
const c = parent.namedChild(i);
|
|
299
|
+
if (!c) continue;
|
|
300
|
+
if (sameExtent(c, spec) || body && sameExtent(c, body)) continue;
|
|
301
|
+
if (c.endIndex > body.startIndex) continue;
|
|
302
|
+
if (c.type === "ERROR") {
|
|
303
|
+
let firstNamed = null;
|
|
304
|
+
let named = 0;
|
|
305
|
+
for (let k = 0; k < c.namedChildCount; k++) {
|
|
306
|
+
const kc = c.namedChild(k);
|
|
307
|
+
if (!kc || !kc.isNamed) continue;
|
|
308
|
+
named++;
|
|
309
|
+
if (firstNamed === null) firstNamed = kc;
|
|
310
|
+
}
|
|
311
|
+
let hasColon = false;
|
|
312
|
+
let colonAt = -1;
|
|
313
|
+
for (let k = 0; k < c.childCount; k++) {
|
|
314
|
+
const kc = c.child(k);
|
|
315
|
+
if (kc && kc.type === ":") {
|
|
316
|
+
hasColon = true;
|
|
317
|
+
colonAt = kc.startIndex;
|
|
318
|
+
break;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
const colonFirst = hasColon && firstNamed !== null && colonAt >= 0 && colonAt < firstNamed.startIndex;
|
|
322
|
+
siblings.push({
|
|
323
|
+
node: c,
|
|
324
|
+
error: true,
|
|
325
|
+
errorName: firstNamed && firstNamed.type === "identifier" ? firstNamed : null,
|
|
326
|
+
hasColon,
|
|
327
|
+
colonFirst,
|
|
328
|
+
prevWasMacro,
|
|
329
|
+
namedCount: named
|
|
330
|
+
});
|
|
331
|
+
prevWasMacro = false;
|
|
332
|
+
continue;
|
|
333
|
+
}
|
|
334
|
+
siblings.push({ node: c, error: false, errorName: null, hasColon: false, colonFirst: false, prevWasMacro, namedCount: 0 });
|
|
335
|
+
prevWasMacro = c.type === "identifier" && isMacroSpelling(c.text.trim());
|
|
336
|
+
}
|
|
337
|
+
let nameNode = null;
|
|
338
|
+
const bases = [];
|
|
339
|
+
const colonArm = siblings.find((s) => s.error && s.errorName && s.hasColon && !s.colonFirst);
|
|
340
|
+
if (colonArm) {
|
|
341
|
+
nameNode = colonArm.errorName;
|
|
342
|
+
const at = siblings.indexOf(colonArm);
|
|
343
|
+
for (let i = at + 1; i < siblings.length; i++) {
|
|
344
|
+
const c = siblings[i].node;
|
|
345
|
+
if (c.type === "identifier" || c.type === "qualified_identifier" || c.type === "template_type") bases.push(c);
|
|
346
|
+
}
|
|
347
|
+
} else {
|
|
348
|
+
const attrArm = siblings.find((s) => s.error && s.errorName && s.prevWasMacro);
|
|
349
|
+
if (attrArm) {
|
|
350
|
+
nameNode = attrArm.errorName;
|
|
351
|
+
} else {
|
|
352
|
+
const names = siblings.filter((s) => !s.error && (s.node.type === "identifier" || s.node.type === "template_type"));
|
|
353
|
+
const nonMacro = names.find((s) => !isMacroSpelling(s.node.text.trim()));
|
|
354
|
+
nameNode = (nonMacro ?? names[0])?.node ?? null;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
if (!colonArm) for (const s of siblings) {
|
|
358
|
+
if (!s.error || !s.colonFirst) continue;
|
|
359
|
+
for (let k = 0; k < s.node.namedChildCount; k++) {
|
|
360
|
+
const kc = s.node.namedChild(k);
|
|
361
|
+
if (kc && (kc.type === "identifier" || kc.type === "qualified_identifier" || kc.type === "template_type")) bases.push(kc);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
if (!nameNode) return null;
|
|
365
|
+
const name = nameNode.text.trim();
|
|
366
|
+
if (!isCppSpelledName(name)) return null;
|
|
367
|
+
return { name, nameNode, body, owner: parent, bases };
|
|
368
|
+
}
|
|
369
|
+
function enclosingFriendDeclaration(node) {
|
|
370
|
+
let cur = node.parent;
|
|
371
|
+
let guard = 0;
|
|
372
|
+
while (cur && guard++ < 64) {
|
|
373
|
+
if (cur.type === "friend_declaration") return cur;
|
|
374
|
+
cur = cur.parent;
|
|
375
|
+
}
|
|
376
|
+
return null;
|
|
377
|
+
}
|
|
378
|
+
function friendBodyOwner(scopeStack, fallback) {
|
|
379
|
+
for (let i = scopeStack.length - 1; i >= 0; i--) {
|
|
380
|
+
const scope = scopeStack[i];
|
|
381
|
+
if (scope.kind === ASTEngine.SymbolKind.Class || scope.kind === ASTEngine.SymbolKind.Struct || scope.kind === ASTEngine.SymbolKind.Union) {
|
|
382
|
+
continue;
|
|
383
|
+
}
|
|
384
|
+
return scope;
|
|
385
|
+
}
|
|
386
|
+
return { node: fallback, name: "global", kind: ASTEngine.SymbolKind.Function };
|
|
387
|
+
}
|
|
388
|
+
function templateParameterNames(list) {
|
|
389
|
+
const names = /* @__PURE__ */ new Set();
|
|
390
|
+
for (let j = 0; j < list.namedChildCount; j++) {
|
|
391
|
+
const param = list.namedChild(j);
|
|
392
|
+
if (!param) continue;
|
|
393
|
+
let declared = null;
|
|
394
|
+
for (let k = 0; k < param.namedChildCount; k++) {
|
|
395
|
+
const c = param.namedChild(k);
|
|
396
|
+
if (c && (c.type === "identifier" || c.type === "type_identifier")) declared = c;
|
|
397
|
+
}
|
|
398
|
+
if (!declared) {
|
|
399
|
+
const stack = [param];
|
|
400
|
+
while (stack.length > 0) {
|
|
401
|
+
const n = stack.pop();
|
|
402
|
+
if (n.type === "identifier" || n.type === "type_identifier") declared = n;
|
|
403
|
+
for (let k = 0; k < n.namedChildCount; k++) {
|
|
404
|
+
const c = n.namedChild(k);
|
|
405
|
+
if (c) stack.push(c);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
if (declared?.text) names.add(declared.text);
|
|
410
|
+
}
|
|
411
|
+
return names;
|
|
412
|
+
}
|
|
413
|
+
function isTemplateParameterListFrame(node) {
|
|
414
|
+
if (node.childCount < 3) return false;
|
|
415
|
+
if (node.child(0)?.type !== "<" || node.child(node.childCount - 1)?.type !== ">") return false;
|
|
416
|
+
for (let i = 1; i < node.childCount - 1; i++) {
|
|
417
|
+
const t = node.child(i)?.type;
|
|
418
|
+
if (t === "typename" || t === "class") return true;
|
|
419
|
+
}
|
|
420
|
+
return false;
|
|
421
|
+
}
|
|
422
|
+
function extendsTargetName(baseText, className) {
|
|
423
|
+
const leafOf = (raw) => {
|
|
424
|
+
let t = stripAccessSpecifier(raw);
|
|
425
|
+
const lt = t.indexOf("<");
|
|
426
|
+
if (lt !== -1) t = t.slice(0, lt).trim();
|
|
427
|
+
return lastScopeSegment(t);
|
|
428
|
+
};
|
|
429
|
+
const leaf = leafOf(baseText);
|
|
430
|
+
if (!leaf) return null;
|
|
431
|
+
if (leaf === leafOf(className)) return null;
|
|
432
|
+
return leaf;
|
|
433
|
+
}
|
|
434
|
+
var CPP_ORACLE_CONTRACT = [
|
|
435
|
+
"macroDeclaredBody",
|
|
436
|
+
// #919: a macro-declared BODY is a function body
|
|
437
|
+
"isCppSpelledName",
|
|
438
|
+
// D1: one set, three gates, one impossibility
|
|
439
|
+
"extendsTargetName",
|
|
440
|
+
// D2: a class definition is never a base
|
|
441
|
+
"qualifiedDeclaratorLeaf",
|
|
442
|
+
// the qualified declarator's LEAF is the name
|
|
443
|
+
"templateWrappedDeclaratorName",
|
|
444
|
+
// CPPD15/16: a wrapped declarator declares
|
|
445
|
+
"recoveredDeclaredName",
|
|
446
|
+
// D4: the name beside an error-recovered declarator
|
|
447
|
+
"specifierDeclaredName",
|
|
448
|
+
// D7: a specifier's name is its leaf
|
|
449
|
+
"attributeDisplacedEnumName"
|
|
450
|
+
// D7: an enum name the attribute displaced
|
|
451
|
+
];
|
|
452
|
+
function macroDeclaredBody(body) {
|
|
453
|
+
if (!body || body.type !== "compound_statement") return null;
|
|
454
|
+
let heading = body.previousSibling;
|
|
455
|
+
for (let i = 0; heading && i < 2 && (heading.isMissing || heading.type === ";"); i++) {
|
|
456
|
+
heading = heading.previousSibling;
|
|
457
|
+
}
|
|
458
|
+
if (!heading || heading.type !== "expression_statement") return null;
|
|
459
|
+
let missing = false;
|
|
460
|
+
for (let i = 0; i < heading.childCount; i++) {
|
|
461
|
+
const c = heading.child(i);
|
|
462
|
+
if (c && c.isMissing) {
|
|
463
|
+
missing = true;
|
|
464
|
+
break;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
if (!missing) return null;
|
|
468
|
+
const call = heading.namedChild(0);
|
|
469
|
+
if (!call || call.type !== "call_expression") return null;
|
|
470
|
+
const macro = call.childForFieldName("function");
|
|
471
|
+
if (!macro || macro.type !== "identifier") return null;
|
|
472
|
+
const macroName = macro.text.trim();
|
|
473
|
+
if (!macroName) return null;
|
|
474
|
+
return { heading, macroName, nameNode: macro };
|
|
475
|
+
}
|
|
476
|
+
function isIdentifierChar(code) {
|
|
477
|
+
return code >= 48 && code <= 57 || code >= 65 && code <= 90 || code >= 97 && code <= 122 || code === 95;
|
|
478
|
+
}
|
|
479
|
+
function joinHeadingLines(text) {
|
|
480
|
+
let out = "";
|
|
481
|
+
let i = 0;
|
|
482
|
+
while (i < text.length) {
|
|
483
|
+
const c = text[i];
|
|
484
|
+
if (c === "\n") {
|
|
485
|
+
while (out.length > 0 && (out[out.length - 1] === " " || out[out.length - 1] === " " || out[out.length - 1] === "\r")) out = out.slice(0, -1);
|
|
486
|
+
out += " ";
|
|
487
|
+
i++;
|
|
488
|
+
while (i < text.length && (text[i] === " " || text[i] === " " || text[i] === "\r")) i++;
|
|
489
|
+
continue;
|
|
490
|
+
}
|
|
491
|
+
out += c;
|
|
492
|
+
i++;
|
|
493
|
+
}
|
|
494
|
+
return out.trim();
|
|
495
|
+
}
|
|
496
|
+
function phantomQualifierType(nameField) {
|
|
497
|
+
if (!nameField || nameField.type !== "qualified_identifier") return void 0;
|
|
498
|
+
const leaf = qualifiedDeclaratorLeaf(nameField);
|
|
499
|
+
if (!leaf || leaf.scopeQid !== null) return void 0;
|
|
500
|
+
const type = collapseSpaces(nameField.text.slice(0, leaf.nameNode.startIndex - nameField.startIndex));
|
|
501
|
+
return type || void 0;
|
|
502
|
+
}
|
|
503
|
+
function specifierTypeText(specifier) {
|
|
504
|
+
const parts = [];
|
|
505
|
+
for (let i = 0; i < specifier.namedChildCount; i++) {
|
|
506
|
+
const c = specifier.namedChild(i);
|
|
507
|
+
if (c && c.type !== "macro_specifier") parts.push(c.text);
|
|
508
|
+
}
|
|
509
|
+
return collapseSpaces(parts.join(" ")) || void 0;
|
|
510
|
+
}
|
|
511
|
+
var CPP_BUILTIN_TYPE_KEYWORDS = /* @__PURE__ */ new Set([
|
|
512
|
+
"void",
|
|
513
|
+
"bool",
|
|
514
|
+
"char",
|
|
515
|
+
"char8_t",
|
|
516
|
+
"char16_t",
|
|
517
|
+
"char32_t",
|
|
518
|
+
"wchar_t",
|
|
519
|
+
"short",
|
|
520
|
+
"int",
|
|
521
|
+
"long",
|
|
522
|
+
"float",
|
|
523
|
+
"double",
|
|
524
|
+
"signed",
|
|
525
|
+
"unsigned",
|
|
526
|
+
"auto"
|
|
527
|
+
]);
|
|
528
|
+
var CPP_NON_CLASS_STANDARD_TYPES = /* @__PURE__ */ new Set([
|
|
529
|
+
// <cstdint> exact-width
|
|
530
|
+
"int8_t",
|
|
531
|
+
"int16_t",
|
|
532
|
+
"int32_t",
|
|
533
|
+
"int64_t",
|
|
534
|
+
"uint8_t",
|
|
535
|
+
"uint16_t",
|
|
536
|
+
"uint32_t",
|
|
537
|
+
"uint64_t",
|
|
538
|
+
// <cstdint> least/fast variants
|
|
539
|
+
"int_least8_t",
|
|
540
|
+
"int_least16_t",
|
|
541
|
+
"int_least32_t",
|
|
542
|
+
"int_least64_t",
|
|
543
|
+
"uint_least8_t",
|
|
544
|
+
"uint_least16_t",
|
|
545
|
+
"uint_least32_t",
|
|
546
|
+
"uint_least64_t",
|
|
547
|
+
"int_fast8_t",
|
|
548
|
+
"int_fast16_t",
|
|
549
|
+
"int_fast32_t",
|
|
550
|
+
"int_fast64_t",
|
|
551
|
+
"uint_fast8_t",
|
|
552
|
+
"uint_fast16_t",
|
|
553
|
+
"uint_fast32_t",
|
|
554
|
+
"uint_fast64_t",
|
|
555
|
+
// <cstdint> pointer-width and widest
|
|
556
|
+
"intptr_t",
|
|
557
|
+
"uintptr_t",
|
|
558
|
+
"intmax_t",
|
|
559
|
+
"uintmax_t",
|
|
560
|
+
// <cstddef>
|
|
561
|
+
"size_t",
|
|
562
|
+
"ptrdiff_t",
|
|
563
|
+
"nullptr_t"
|
|
564
|
+
]);
|
|
565
|
+
var CPP_KEYWORDS = /* @__PURE__ */ new Set([
|
|
566
|
+
// declarations, types, specifiers
|
|
567
|
+
"alignas",
|
|
568
|
+
"auto",
|
|
569
|
+
"bool",
|
|
570
|
+
"char",
|
|
571
|
+
"char8_t",
|
|
572
|
+
"char16_t",
|
|
573
|
+
"char32_t",
|
|
574
|
+
"class",
|
|
575
|
+
"const",
|
|
576
|
+
"consteval",
|
|
577
|
+
"constexpr",
|
|
578
|
+
"constinit",
|
|
579
|
+
"decltype",
|
|
580
|
+
"double",
|
|
581
|
+
"enum",
|
|
582
|
+
"explicit",
|
|
583
|
+
"export",
|
|
584
|
+
"extern",
|
|
585
|
+
"float",
|
|
586
|
+
"friend",
|
|
587
|
+
"inline",
|
|
588
|
+
"int",
|
|
589
|
+
"long",
|
|
590
|
+
"mutable",
|
|
591
|
+
"namespace",
|
|
592
|
+
"register",
|
|
593
|
+
"short",
|
|
594
|
+
"signed",
|
|
595
|
+
"static",
|
|
596
|
+
"struct",
|
|
597
|
+
"template",
|
|
598
|
+
"thread_local",
|
|
599
|
+
"typedef",
|
|
600
|
+
"typeid",
|
|
601
|
+
"typename",
|
|
602
|
+
"union",
|
|
603
|
+
"unsigned",
|
|
604
|
+
"using",
|
|
605
|
+
"virtual",
|
|
606
|
+
"void",
|
|
607
|
+
"volatile",
|
|
608
|
+
"wchar_t",
|
|
609
|
+
// control flow
|
|
610
|
+
"break",
|
|
611
|
+
"case",
|
|
612
|
+
"catch",
|
|
613
|
+
"co_await",
|
|
614
|
+
"co_return",
|
|
615
|
+
"co_yield",
|
|
616
|
+
"continue",
|
|
617
|
+
"default",
|
|
618
|
+
"do",
|
|
619
|
+
"else",
|
|
620
|
+
"for",
|
|
621
|
+
"goto",
|
|
622
|
+
"if",
|
|
623
|
+
"return",
|
|
624
|
+
"switch",
|
|
625
|
+
"throw",
|
|
626
|
+
"try",
|
|
627
|
+
"while",
|
|
628
|
+
// expressions — the CAST family is what #924's type-only set missed
|
|
629
|
+
"const_cast",
|
|
630
|
+
"delete",
|
|
631
|
+
"dynamic_cast",
|
|
632
|
+
"new",
|
|
633
|
+
"noexcept",
|
|
634
|
+
"nullptr",
|
|
635
|
+
"reinterpret_cast",
|
|
636
|
+
"sizeof",
|
|
637
|
+
"static_assert",
|
|
638
|
+
"static_cast",
|
|
639
|
+
"this",
|
|
640
|
+
"true",
|
|
641
|
+
"false",
|
|
642
|
+
"operator",
|
|
643
|
+
"requires",
|
|
644
|
+
"concept",
|
|
645
|
+
// access
|
|
646
|
+
"private",
|
|
647
|
+
"protected",
|
|
648
|
+
"public",
|
|
649
|
+
// alternative tokens (ISO 14882 [lex.digraph], [lex.alternative])
|
|
650
|
+
"and",
|
|
651
|
+
"and_eq",
|
|
652
|
+
"bitand",
|
|
653
|
+
"bitor",
|
|
654
|
+
"compl",
|
|
655
|
+
"not",
|
|
656
|
+
"not_eq",
|
|
657
|
+
"or",
|
|
658
|
+
"or_eq",
|
|
659
|
+
"xor",
|
|
660
|
+
"xor_eq",
|
|
661
|
+
// implementation-reserved spellings and the standard preprocessor operators.
|
|
662
|
+
// None of these is a user-spellable identifier either, and each was measured
|
|
663
|
+
// producing an edge on the corpora (`defined` 34, `__has_include` 42,
|
|
664
|
+
// `__has_cpp_attribute` 6).
|
|
665
|
+
"defined",
|
|
666
|
+
"__has_include",
|
|
667
|
+
"__has_cpp_attribute",
|
|
668
|
+
"asm",
|
|
669
|
+
"__asm",
|
|
670
|
+
"__asm__",
|
|
671
|
+
"__declspec",
|
|
672
|
+
"__attribute__",
|
|
673
|
+
"__attribute",
|
|
674
|
+
"__inline",
|
|
675
|
+
"__inline__",
|
|
676
|
+
"__forceinline",
|
|
677
|
+
"__restrict",
|
|
678
|
+
"__restrict__",
|
|
679
|
+
"__extension__",
|
|
680
|
+
"__typeof__",
|
|
681
|
+
"__typeof",
|
|
682
|
+
"__cdecl",
|
|
683
|
+
"__stdcall",
|
|
684
|
+
"__thiscall",
|
|
685
|
+
"__vectorcall",
|
|
686
|
+
"__int8",
|
|
687
|
+
"__int16",
|
|
688
|
+
"__int32",
|
|
689
|
+
"__int64",
|
|
690
|
+
"__forceinline__",
|
|
691
|
+
"__declspec__",
|
|
692
|
+
"__builtin_offsetof",
|
|
693
|
+
"__builtin_va_arg",
|
|
694
|
+
"__builtin_types_compatible_p",
|
|
695
|
+
"__func__",
|
|
696
|
+
"__FUNCTION__",
|
|
697
|
+
"__PRETTY_FUNCTION__"
|
|
698
|
+
]);
|
|
699
|
+
function isCppSpelledName(text) {
|
|
700
|
+
if (!text) return false;
|
|
701
|
+
return !CPP_KEYWORDS.has(text);
|
|
702
|
+
}
|
|
703
|
+
function isPublishableFunctionName(name) {
|
|
704
|
+
if (!name) return false;
|
|
705
|
+
if (name.startsWith("operator")) return true;
|
|
706
|
+
const isStart = (c) => c >= 65 && c <= 90 || c >= 97 && c <= 122 || c === 95 || c === 126;
|
|
707
|
+
const isContinue = (c) => isStart(c) || c >= 48 && c <= 57;
|
|
708
|
+
if (!isStart(name.charCodeAt(0))) return false;
|
|
709
|
+
for (let i = 1; i < name.length; i++) if (!isContinue(name.charCodeAt(i))) return false;
|
|
710
|
+
return true;
|
|
711
|
+
}
|
|
712
|
+
function sameTsNode(a, b) {
|
|
713
|
+
if (!a || !b) return false;
|
|
714
|
+
return a.type === b.type && a.startIndex === b.startIndex && a.endIndex === b.endIndex;
|
|
715
|
+
}
|
|
716
|
+
function cppPureVirtualDeclaration(node) {
|
|
717
|
+
if (!node) return false;
|
|
718
|
+
if (node.type !== "declaration" && node.type !== "field_declaration" && node.type !== "function_definition") return false;
|
|
719
|
+
const direct = node.childForFieldName?.("declarator");
|
|
720
|
+
if (direct?.type === "function_declarator" && hasZeroLiteralChild(node)) return true;
|
|
721
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
722
|
+
const c = node.namedChild(i);
|
|
723
|
+
if (!c || c.type !== "init_declarator") continue;
|
|
724
|
+
const value = c.childForFieldName?.("value");
|
|
725
|
+
const inner = c.childForFieldName?.("declarator");
|
|
726
|
+
if (value?.type === "number_literal" && value.text.trim() === "0" && inner?.type === "function_declarator") return true;
|
|
727
|
+
}
|
|
728
|
+
return false;
|
|
729
|
+
}
|
|
730
|
+
function hasZeroLiteralChild(node) {
|
|
731
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
732
|
+
const child = node.child(i);
|
|
733
|
+
if (child && child.type === "number_literal" && child.text.trim() === "0") return true;
|
|
734
|
+
}
|
|
735
|
+
return false;
|
|
736
|
+
}
|
|
737
|
+
var CPP_EXPORT_MARKER_COMPONENTS = /* @__PURE__ */ new Set([
|
|
738
|
+
"EXPORT",
|
|
739
|
+
"EXPORTS",
|
|
740
|
+
"EXPORTABLE",
|
|
741
|
+
"DLLEXPORT",
|
|
742
|
+
"API",
|
|
743
|
+
"VISIBILITY",
|
|
744
|
+
"PUBLIC_API"
|
|
745
|
+
]);
|
|
746
|
+
function identifierComponents(text) {
|
|
747
|
+
const out = [];
|
|
748
|
+
let cur = "";
|
|
749
|
+
for (let i = 0; i <= text.length; i++) {
|
|
750
|
+
const c = i < text.length ? text.charCodeAt(i) : 95;
|
|
751
|
+
if (c === 95) {
|
|
752
|
+
if (cur) out.push(cur);
|
|
753
|
+
cur = "";
|
|
754
|
+
continue;
|
|
755
|
+
}
|
|
756
|
+
cur += text[i];
|
|
757
|
+
}
|
|
758
|
+
return out;
|
|
759
|
+
}
|
|
760
|
+
function cppExportMarker(macro) {
|
|
761
|
+
if (!macro) return false;
|
|
762
|
+
const comps = identifierComponents(macro.trim());
|
|
763
|
+
if (comps.length === 0) return false;
|
|
764
|
+
for (const c of comps) if (CPP_EXPORT_MARKER_COMPONENTS.has(c)) return true;
|
|
765
|
+
return false;
|
|
766
|
+
}
|
|
767
|
+
function recoveredDeclaredName(declNode) {
|
|
768
|
+
if (!declNode.hasError) return null;
|
|
769
|
+
const declared = declNode.childForFieldName?.("declarator") ?? null;
|
|
770
|
+
if (!declared) return null;
|
|
771
|
+
let next = declared.nextSibling;
|
|
772
|
+
while (next && (next.isMissing || !next.isNamed)) next = next.nextSibling;
|
|
773
|
+
if (!next || next.type !== "ERROR" || next.namedChildCount !== 1) return null;
|
|
774
|
+
const inner = next.namedChild(0);
|
|
775
|
+
if (!inner || inner.type !== "identifier") return null;
|
|
776
|
+
if (inner.startIndex <= declared.startIndex) return null;
|
|
777
|
+
if (!isCppSpelledName(inner.text)) return null;
|
|
778
|
+
return inner;
|
|
779
|
+
}
|
|
780
|
+
function specifierDeclaredName(spec) {
|
|
781
|
+
const leafOf = (n, depth = 0) => {
|
|
782
|
+
if (!n || depth > 8) return null;
|
|
783
|
+
if (n.type === "type_identifier" || n.type === "identifier") {
|
|
784
|
+
return isCppSpelledName(n.text) ? n.text : null;
|
|
785
|
+
}
|
|
786
|
+
if (n.type === "template_type") {
|
|
787
|
+
const tn = n.childForFieldName?.("name") ?? null;
|
|
788
|
+
if (tn) return leafOf(tn, depth + 1);
|
|
789
|
+
const lt = n.text.indexOf("<");
|
|
790
|
+
return lt > 0 ? n.text.slice(0, lt).trim() : null;
|
|
791
|
+
}
|
|
792
|
+
if (n.type === "qualified_identifier") {
|
|
793
|
+
return leafOf(n.namedChild(n.namedChildCount - 1), depth + 1);
|
|
794
|
+
}
|
|
795
|
+
return null;
|
|
796
|
+
};
|
|
797
|
+
const nameField = spec.childForFieldName?.("name") ?? null;
|
|
798
|
+
if (nameField) {
|
|
799
|
+
if (nameField.type === "type_identifier" || nameField.type === "identifier") {
|
|
800
|
+
return isCppSpelledName(nameField.text) ? nameField.text : null;
|
|
801
|
+
}
|
|
802
|
+
if (nameField.type === "qualified_identifier" || nameField.type === "template_type") {
|
|
803
|
+
return leafOf(nameField);
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
return null;
|
|
807
|
+
}
|
|
808
|
+
function attributeDisplacedEnumName(enumSpec) {
|
|
809
|
+
const decl = enumSpec.parent ?? null;
|
|
810
|
+
if (!decl || decl.type !== "declaration" && decl.type !== "field_declaration") return null;
|
|
811
|
+
const outer = decl.childForFieldName?.("declarator") ?? null;
|
|
812
|
+
if (!outer || outer.type !== "init_declarator") return null;
|
|
813
|
+
const id = outer.childForFieldName?.("declarator") ?? null;
|
|
814
|
+
if (!id || id.type !== "identifier") return null;
|
|
815
|
+
if (id.startIndex <= enumSpec.endIndex) return null;
|
|
816
|
+
return isCppSpelledName(id.text) ? id.text : null;
|
|
817
|
+
}
|
|
818
|
+
function qualifiedDeclaratorLeaf(qid) {
|
|
819
|
+
let leaf = qid;
|
|
820
|
+
while (true) {
|
|
821
|
+
const last = leaf.namedChild(leaf.namedChildCount - 1);
|
|
822
|
+
if (last && last.type === "qualified_identifier") leaf = last;
|
|
823
|
+
else break;
|
|
824
|
+
}
|
|
825
|
+
const nameNode = leaf.namedChild(leaf.namedChildCount - 1);
|
|
826
|
+
if (!nameNode) return null;
|
|
827
|
+
let realJoin = false;
|
|
828
|
+
for (let i = 0; i < leaf.childCount; i++) {
|
|
829
|
+
const c = leaf.child(i);
|
|
830
|
+
if (c && !c.isNamed && c.type === "::") {
|
|
831
|
+
realJoin = c.text === "::";
|
|
832
|
+
break;
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
return { nameNode, scopeQid: realJoin ? leaf : null };
|
|
836
|
+
}
|
|
837
|
+
function templateWrappedDeclaratorName(nameNode) {
|
|
838
|
+
const p = nameNode.parent;
|
|
839
|
+
if (!p) return false;
|
|
840
|
+
if (p.type !== "template_function" && p.type !== "template_method" && p.type !== "dependent_name") return false;
|
|
841
|
+
const gp = p.parent;
|
|
842
|
+
if (!gp || gp.type !== "function_declarator" && gp.type !== "abstract_function_declarator") return false;
|
|
843
|
+
const decl = gp.childForFieldName?.("declarator");
|
|
844
|
+
return !!decl && decl.startIndex === p.startIndex && decl.endIndex === p.endIndex;
|
|
845
|
+
}
|
|
846
|
+
function macroInvocationText(content, nameStart) {
|
|
847
|
+
const CAP = 4e3;
|
|
848
|
+
let i = nameStart;
|
|
849
|
+
while (i < content.length && isIdentifierChar(content.charCodeAt(i))) i++;
|
|
850
|
+
const end = Math.min(content.length, i + CAP);
|
|
851
|
+
let open = -1;
|
|
852
|
+
while (i < end) {
|
|
853
|
+
const c = content[i];
|
|
854
|
+
if (c === "(") {
|
|
855
|
+
open = i;
|
|
856
|
+
break;
|
|
857
|
+
}
|
|
858
|
+
if (c === "/" && content[i + 1] === "/") {
|
|
859
|
+
const nl = content.indexOf("\n", i);
|
|
860
|
+
if (nl < 0) return null;
|
|
861
|
+
i = nl + 1;
|
|
862
|
+
continue;
|
|
863
|
+
}
|
|
864
|
+
if (c === "/" && content[i + 1] === "*") {
|
|
865
|
+
const cl = content.indexOf("*/", i);
|
|
866
|
+
if (cl < 0) return null;
|
|
867
|
+
i = cl + 2;
|
|
868
|
+
continue;
|
|
869
|
+
}
|
|
870
|
+
if (c === "{" || c === "}" || c === ";") return null;
|
|
871
|
+
i++;
|
|
872
|
+
}
|
|
873
|
+
if (open < 0) return null;
|
|
874
|
+
let depth = 0;
|
|
875
|
+
for (let k = open; k < end; k++) {
|
|
876
|
+
const c = content[k];
|
|
877
|
+
if (c === '"' || c === "'") {
|
|
878
|
+
k++;
|
|
879
|
+
while (k < end) {
|
|
880
|
+
if (content[k] === "\\") {
|
|
881
|
+
k += 2;
|
|
882
|
+
continue;
|
|
883
|
+
}
|
|
884
|
+
if (content[k] === c) break;
|
|
885
|
+
k++;
|
|
886
|
+
}
|
|
887
|
+
continue;
|
|
888
|
+
}
|
|
889
|
+
if (c === "/" && content[k + 1] === "/") {
|
|
890
|
+
const nl = content.indexOf("\n", k);
|
|
891
|
+
if (nl < 0) return null;
|
|
892
|
+
k = nl;
|
|
893
|
+
continue;
|
|
894
|
+
}
|
|
895
|
+
if (c === "/" && content[k + 1] === "*") {
|
|
896
|
+
const cl = content.indexOf("*/", k);
|
|
897
|
+
if (cl < 0) return null;
|
|
898
|
+
k = cl + 1;
|
|
899
|
+
continue;
|
|
900
|
+
}
|
|
901
|
+
if (c === "(") depth++;
|
|
902
|
+
else if (c === ")") {
|
|
903
|
+
depth--;
|
|
904
|
+
if (depth === 0) {
|
|
905
|
+
const text = joinHeadingLines(content.slice(nameStart, k + 1));
|
|
906
|
+
return text || null;
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
return null;
|
|
911
|
+
}
|
|
912
|
+
var RealCppAnalyzer = class {
|
|
913
|
+
analyze(rootNode, content, filePath, context) {
|
|
914
|
+
const symbols = [];
|
|
915
|
+
const relationships = [];
|
|
916
|
+
const imports = [];
|
|
917
|
+
const exports = [];
|
|
918
|
+
const errors = [];
|
|
919
|
+
const patterns = {};
|
|
920
|
+
const frameworks = {};
|
|
921
|
+
let memorySafetyScore = 100;
|
|
922
|
+
const usesSeen = /* @__PURE__ */ new Set();
|
|
923
|
+
const referencesSeen = /* @__PURE__ */ new Set();
|
|
924
|
+
const exportsSeen = /* @__PURE__ */ new Set();
|
|
925
|
+
const macroNames = /* @__PURE__ */ new Set();
|
|
926
|
+
let fileDecisions = 0;
|
|
927
|
+
const classVirtuals = /* @__PURE__ */ new Map();
|
|
928
|
+
const pendingOverrides = [];
|
|
929
|
+
const classBases = /* @__PURE__ */ new Map();
|
|
930
|
+
const classPureVirtuals = /* @__PURE__ */ new Map();
|
|
931
|
+
const pendingImplements = [];
|
|
932
|
+
const pendingExports = [];
|
|
933
|
+
const PACKAGE_SCOPE = process.env.BB_CPP_PACKAGE_SCOPE === "1";
|
|
934
|
+
const packageScopePackages = PACKAGE_SCOPE && Array.isArray(context?.cppPackageScopePackages) ? context.cppPackageScopePackages.map((x) => String(x ?? "").trim()).filter((x) => x.length > 0) : [];
|
|
935
|
+
const packageScopeRoots = PACKAGE_SCOPE && packageScopePackages.length > 0 ? cppExternalIncludeRoots(rootNode) : /* @__PURE__ */ new Set();
|
|
936
|
+
const INDEX_CONSUME = process.env.BB_CPP_INDEX_CONSUME === "1";
|
|
937
|
+
const indexCandidate = INDEX_CONSUME ? context?.projectIndex : void 0;
|
|
938
|
+
const indexUsable = !!indexCandidate && indexCandidate.complete === true;
|
|
939
|
+
const indexSymbols = indexUsable && indexCandidate.symbolNames instanceof Set ? indexCandidate.symbolNames : /* @__PURE__ */ new Set();
|
|
940
|
+
const indexCounts = indexUsable && indexCandidate.symbolCounts instanceof Map ? indexCandidate.symbolCounts : /* @__PURE__ */ new Map();
|
|
941
|
+
const indexTypeKinds = indexUsable && indexCandidate.typeKinds instanceof Map ? indexCandidate.typeKinds : /* @__PURE__ */ new Map();
|
|
942
|
+
const indexTypeBases = indexUsable && indexCandidate.typeBases instanceof Map ? indexCandidate.typeBases : /* @__PURE__ */ new Map();
|
|
943
|
+
let indexResolution = null;
|
|
944
|
+
if (indexUsable) {
|
|
945
|
+
try {
|
|
946
|
+
indexResolution = buildResolutionTables(indexCandidate);
|
|
947
|
+
} catch {
|
|
948
|
+
indexResolution = null;
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
const indexCallFacts = (targetName) => {
|
|
952
|
+
if (!indexUsable) return {};
|
|
953
|
+
const out = {};
|
|
954
|
+
if (indexSymbols.has(targetName)) out.declaredInProject = true;
|
|
955
|
+
const count = indexCounts.get(targetName);
|
|
956
|
+
if (count !== void 0) out.indexDeclarationCount = count;
|
|
957
|
+
const owners = indexResolution?.memberOwners.get(targetName);
|
|
958
|
+
if (owners && owners.size === 1) out.memberOfProjectType = [...owners][0];
|
|
959
|
+
return out;
|
|
960
|
+
};
|
|
961
|
+
const indexBaseFacts = (targetName) => {
|
|
962
|
+
if (!indexUsable) return {};
|
|
963
|
+
const kind = indexTypeKinds.get(targetName);
|
|
964
|
+
return kind === void 0 ? {} : { indexBaseKind: String(kind) };
|
|
965
|
+
};
|
|
966
|
+
const indexOwnerFacts = (qualified, leaf) => {
|
|
967
|
+
if (!indexUsable) return {};
|
|
968
|
+
const owner = qualifiedOwnerSegment(qualified, leaf);
|
|
969
|
+
if (owner === null) return {};
|
|
970
|
+
const kind = indexTypeKinds.get(owner);
|
|
971
|
+
if (kind !== "class" && kind !== "struct" && kind !== "union") return {};
|
|
972
|
+
const ownMembers = indexCandidate.typeMembers instanceof Map ? indexCandidate.typeMembers.get(owner) : void 0;
|
|
973
|
+
if (!(ownMembers instanceof Set) || !ownMembers.has(leaf)) return {};
|
|
974
|
+
return { owner };
|
|
975
|
+
};
|
|
976
|
+
const indexOverridesFacts = (base, method) => {
|
|
977
|
+
if (!indexUsable || indexResolution === null) return {};
|
|
978
|
+
const declares = indexResolution.memberOwners.get(method)?.has(base) === true;
|
|
979
|
+
return declares ? { indexDeclaresMethod: true } : {};
|
|
980
|
+
};
|
|
981
|
+
void indexTypeBases;
|
|
982
|
+
const fileScopeValues = /* @__PURE__ */ new Set();
|
|
983
|
+
const namespaceValues = /* @__PURE__ */ new Map();
|
|
984
|
+
const classMembers = /* @__PURE__ */ new Map();
|
|
985
|
+
const usedNamespaces = /* @__PURE__ */ new Set();
|
|
986
|
+
const pendingQualified = [];
|
|
987
|
+
{
|
|
988
|
+
const pre = [
|
|
989
|
+
{ n: rootNode, ns: [], inFn: false, inClass: null }
|
|
990
|
+
];
|
|
991
|
+
const addValue = (name, ns, cls) => {
|
|
992
|
+
if (!isCppSpelledName(name)) return;
|
|
993
|
+
if (!name) return;
|
|
994
|
+
if (cls) {
|
|
995
|
+
let s = classMembers.get(cls);
|
|
996
|
+
if (!s) {
|
|
997
|
+
s = /* @__PURE__ */ new Set();
|
|
998
|
+
classMembers.set(cls, s);
|
|
999
|
+
}
|
|
1000
|
+
s.add(name);
|
|
1001
|
+
return;
|
|
1002
|
+
}
|
|
1003
|
+
if (ns.length > 0) {
|
|
1004
|
+
const top = ns[ns.length - 1];
|
|
1005
|
+
let s = namespaceValues.get(top);
|
|
1006
|
+
if (!s) {
|
|
1007
|
+
s = /* @__PURE__ */ new Set();
|
|
1008
|
+
namespaceValues.set(top, s);
|
|
1009
|
+
}
|
|
1010
|
+
s.add(name);
|
|
1011
|
+
return;
|
|
1012
|
+
}
|
|
1013
|
+
fileScopeValues.add(name);
|
|
1014
|
+
};
|
|
1015
|
+
const leftmostId = (n) => {
|
|
1016
|
+
let cur = n;
|
|
1017
|
+
while (cur) {
|
|
1018
|
+
if (cur.type === "identifier" || cur.type === "field_identifier") {
|
|
1019
|
+
return isCppSpelledName(cur.text) ? cur.text : null;
|
|
1020
|
+
}
|
|
1021
|
+
cur = cur.childForFieldName("declarator") || cur.namedChild(0);
|
|
1022
|
+
}
|
|
1023
|
+
return null;
|
|
1024
|
+
};
|
|
1025
|
+
while (pre.length > 0) {
|
|
1026
|
+
const { n, ns, inFn, inClass } = pre.pop();
|
|
1027
|
+
const t = n.type;
|
|
1028
|
+
if (t === "namespace_definition") {
|
|
1029
|
+
const nm = this.childText(n, "namespace_identifier");
|
|
1030
|
+
if (nm && nm !== CPP_ANONYMOUS) {
|
|
1031
|
+
for (let i = n.namedChildCount - 1; i >= 0; i--) {
|
|
1032
|
+
const c = n.namedChild(i);
|
|
1033
|
+
if (c) pre.push({ n: c, ns: [...ns, nm], inFn, inClass });
|
|
1034
|
+
}
|
|
1035
|
+
continue;
|
|
1036
|
+
}
|
|
1037
|
+
} else if (t === "class_specifier" || t === "struct_specifier" || t === "union_specifier") {
|
|
1038
|
+
const nm = this.childText(n, "type_identifier") || (() => {
|
|
1039
|
+
const tt = this.findChild(n, "template_type");
|
|
1040
|
+
if (!tt) return null;
|
|
1041
|
+
const tn = tt.childForFieldName?.("name") || this.findChild(tt, "type_identifier") || this.findChild(tt, "qualified_identifier");
|
|
1042
|
+
if (tn?.text) return tn.text;
|
|
1043
|
+
const lt = tt.text.indexOf("<");
|
|
1044
|
+
return lt > 0 ? tt.text.slice(0, lt).trim() : null;
|
|
1045
|
+
})();
|
|
1046
|
+
const body = this.findChild(n, "field_declaration_list");
|
|
1047
|
+
if (nm && body) {
|
|
1048
|
+
for (let i = n.namedChildCount - 1; i >= 0; i--) {
|
|
1049
|
+
const c = n.namedChild(i);
|
|
1050
|
+
if (c) pre.push({ n: c, ns, inFn, inClass: nm });
|
|
1051
|
+
}
|
|
1052
|
+
continue;
|
|
1053
|
+
}
|
|
1054
|
+
} else if (t === "function_definition") {
|
|
1055
|
+
const det = this.getFunctionDetails(n);
|
|
1056
|
+
if (det && !det.isDestructor && !det.name.startsWith("operator") && !this.isMacroFunctionShape(n, det.name)) {
|
|
1057
|
+
const isCtor = inClass !== null && det.name === inClass || det.scope !== void 0 && det.name === det.scope;
|
|
1058
|
+
if (!isCtor) {
|
|
1059
|
+
if (det.scope) pendingQualified.push({ name: det.name, scope: det.scope });
|
|
1060
|
+
else if (!inFn) addValue(det.name, ns, inClass);
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
for (let i = n.namedChildCount - 1; i >= 0; i--) {
|
|
1064
|
+
const c = n.namedChild(i);
|
|
1065
|
+
if (c) pre.push({ n: c, ns, inFn: true, inClass });
|
|
1066
|
+
}
|
|
1067
|
+
continue;
|
|
1068
|
+
} else if (t === "compound_statement" && macroDeclaredBody(n)) {
|
|
1069
|
+
for (let i = n.namedChildCount - 1; i >= 0; i--) {
|
|
1070
|
+
const c = n.namedChild(i);
|
|
1071
|
+
if (c) pre.push({ n: c, ns, inFn: true, inClass });
|
|
1072
|
+
}
|
|
1073
|
+
continue;
|
|
1074
|
+
} else if (!inFn && (t === "declaration" || t === "field_declaration")) {
|
|
1075
|
+
for (let i = 0; i < n.namedChildCount; i++) {
|
|
1076
|
+
const c = n.namedChild(i);
|
|
1077
|
+
if (!c) continue;
|
|
1078
|
+
if (c.type === "init_declarator" && c.namedChild(0)?.type === "structured_binding_declarator") {
|
|
1079
|
+
const sb = c.namedChild(0);
|
|
1080
|
+
for (let k = 0; k < sb.namedChildCount; k++) {
|
|
1081
|
+
const b = sb.namedChild(k);
|
|
1082
|
+
if (b && (b.type === "identifier" || b.type === "field_identifier")) addValue(b.text, ns, inClass);
|
|
1083
|
+
}
|
|
1084
|
+
} else if (c.type === "init_declarator" || c.type === "identifier" || c.type === "field_identifier" || c.type === "pointer_declarator" || c.type === "reference_declarator" || c.type === "array_declarator") {
|
|
1085
|
+
const nm = leftmostId(c);
|
|
1086
|
+
if (nm) addValue(nm, ns, inClass);
|
|
1087
|
+
} else if (c.type === "function_declarator") {
|
|
1088
|
+
const nm = leftmostId(c);
|
|
1089
|
+
if (nm) addValue(nm, ns, inClass);
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
} else if (!inFn && t === "enumerator") {
|
|
1093
|
+
const nm = this.childText(n, "identifier");
|
|
1094
|
+
if (nm) addValue(nm, [], null);
|
|
1095
|
+
} else if (t === "using_declaration" && !inFn && !inClass) {
|
|
1096
|
+
const txt = n.text;
|
|
1097
|
+
if (txt.indexOf("using namespace") !== -1) {
|
|
1098
|
+
const tgt = this.findChild(n, "qualified_identifier") || this.findChild(n, "identifier");
|
|
1099
|
+
if (tgt) usedNamespaces.add(tgt.text.split("::").filter(Boolean).join("::"));
|
|
1100
|
+
} else {
|
|
1101
|
+
const leaf = this.findChild(n, "qualified_identifier") || this.findChild(n, "identifier");
|
|
1102
|
+
if (leaf) {
|
|
1103
|
+
const segs = leaf.text.split("::").filter(Boolean);
|
|
1104
|
+
if (segs.length > 0) addValue(segs[segs.length - 1], [], null);
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
for (let i = n.namedChildCount - 1; i >= 0; i--) {
|
|
1109
|
+
const c = n.namedChild(i);
|
|
1110
|
+
if (c) pre.push({ n: c, ns, inFn, inClass });
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
for (const q of pendingQualified) {
|
|
1114
|
+
if (classMembers.has(q.scope)) addValue(q.name, [], q.scope);
|
|
1115
|
+
else if (namespaceValues.has(q.scope)) addValue(q.name, [q.scope], null);
|
|
1116
|
+
else addValue(q.name, [], null);
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
const scopeStack = [];
|
|
1120
|
+
const stack = [{ node: rootNode }];
|
|
1121
|
+
const DECL_PARENT_TYPES = /* @__PURE__ */ new Set([
|
|
1122
|
+
"declaration",
|
|
1123
|
+
"field_declaration",
|
|
1124
|
+
"parameter_declaration",
|
|
1125
|
+
"init_declarator",
|
|
1126
|
+
"function_declarator",
|
|
1127
|
+
"pointer_declarator",
|
|
1128
|
+
"reference_declarator",
|
|
1129
|
+
"array_declarator",
|
|
1130
|
+
"abstract_pointer_declarator",
|
|
1131
|
+
"abstract_array_declarator",
|
|
1132
|
+
"abstract_function_declarator",
|
|
1133
|
+
"enumerator",
|
|
1134
|
+
"using_declaration",
|
|
1135
|
+
"alias_declaration",
|
|
1136
|
+
"optional_parameter_declaration",
|
|
1137
|
+
"type_parameter_declaration",
|
|
1138
|
+
// CP6 (second review): `auto [x, y]` bindings declare (see the
|
|
1139
|
+
// carve-out in isDeclarationPosition — all names, not first-only).
|
|
1140
|
+
"structured_binding_declarator",
|
|
1141
|
+
// A DESTRUCTOR's name is declared, never read: `~json_sax()` puts the
|
|
1142
|
+
// class name inside a `destructor_name` node, and without this entry the
|
|
1143
|
+
// guard below returned false and the class name read as a value use. The
|
|
1144
|
+
// analyzer's behaviour was already correct — the destructor branch reads
|
|
1145
|
+
// the name to BUILD the symbol and never emitted a `uses` for it — so
|
|
1146
|
+
// this entry changes no output here; it is here so the SET matches the
|
|
1147
|
+
// F5 oracle's, because the oracle was the side that got it wrong and the
|
|
1148
|
+
// whole point of a shared set is that neither side can drift alone.
|
|
1149
|
+
// Measured on the oracle: 27 `uses` truth rows for names that are all
|
|
1150
|
+
// constructor/destructor declarations in nlohmann's ABI test header
|
|
1151
|
+
// (`json_sax`, `lexer`, `binary_reader`, `iter_impl`, `nonesuch`,
|
|
1152
|
+
// `file_input_adapter`, `input_stream_adapter`, `json_sax_dom_parser`) —
|
|
1153
|
+
// the analyzer published ZERO `uses` for every one of them.
|
|
1154
|
+
"destructor_name"
|
|
1155
|
+
// F6 audit: `template_type_parameter` / `template_value_parameter` are
|
|
1156
|
+
// NOT types in the pinned tree-sitter-cpp grammar — template parameters
|
|
1157
|
+
// parse as `type_parameter_declaration` (`typename T` / `class T`) and
|
|
1158
|
+
// `parameter_declaration` / `optional_parameter_declaration`
|
|
1159
|
+
// (`int N`, `int N = M`), all live in this set above. Removed.
|
|
1160
|
+
]);
|
|
1161
|
+
const isDeclaringChainLeftmost = (node) => {
|
|
1162
|
+
const p = node.parent;
|
|
1163
|
+
if (!p || !p.type.endsWith("declarator")) return false;
|
|
1164
|
+
let cur = p;
|
|
1165
|
+
while (cur) {
|
|
1166
|
+
if (cur.type === "identifier" || cur.type === "field_identifier") {
|
|
1167
|
+
return cur.startIndex === node.startIndex;
|
|
1168
|
+
}
|
|
1169
|
+
let next = null;
|
|
1170
|
+
for (let k = 0; k < cur.namedChildCount; k++) {
|
|
1171
|
+
const c = cur.namedChild(k);
|
|
1172
|
+
if (c && (c.type.endsWith("declarator") || c.type === "identifier" || c.type === "field_identifier")) {
|
|
1173
|
+
next = c;
|
|
1174
|
+
break;
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
cur = next;
|
|
1178
|
+
}
|
|
1179
|
+
return false;
|
|
1180
|
+
};
|
|
1181
|
+
const isDeclarationPosition = (node) => {
|
|
1182
|
+
const p = node.parent;
|
|
1183
|
+
if (!p) return false;
|
|
1184
|
+
if (templateWrappedDeclaratorName(node)) return true;
|
|
1185
|
+
if (p.type === "optional_parameter_declaration" || p.type === "type_parameter_declaration") {
|
|
1186
|
+
for (let k = 0; k < p.namedChildCount; k++) {
|
|
1187
|
+
const c = p.namedChild(k);
|
|
1188
|
+
if (c && (c.type === "identifier" || c.type === "field_identifier")) {
|
|
1189
|
+
return c.startIndex === node.startIndex;
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
return false;
|
|
1193
|
+
}
|
|
1194
|
+
if (p.type === "field_declaration" || p.type === "enumerator") {
|
|
1195
|
+
let fresh = true;
|
|
1196
|
+
let foundFirst = false;
|
|
1197
|
+
for (let k = 0; k < p.childCount; k++) {
|
|
1198
|
+
const c = p.child(k);
|
|
1199
|
+
if (!c) continue;
|
|
1200
|
+
if (!c.isNamed) {
|
|
1201
|
+
if (c.type === "," || c.type === ";") {
|
|
1202
|
+
fresh = true;
|
|
1203
|
+
foundFirst = false;
|
|
1204
|
+
} else if (c.type === "=" || c.type === ":" || c.type === "{") fresh = false;
|
|
1205
|
+
continue;
|
|
1206
|
+
}
|
|
1207
|
+
if (c.type === "identifier" || c.type === "field_identifier") {
|
|
1208
|
+
if (!foundFirst && fresh) {
|
|
1209
|
+
if (c.startIndex === node.startIndex && c.endIndex === node.endIndex) return true;
|
|
1210
|
+
foundFirst = true;
|
|
1211
|
+
} else {
|
|
1212
|
+
if (c.startIndex === node.startIndex && c.endIndex === node.endIndex) return false;
|
|
1213
|
+
}
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1216
|
+
return false;
|
|
1217
|
+
}
|
|
1218
|
+
if (!DECL_PARENT_TYPES.has(p.type)) return false;
|
|
1219
|
+
if (p.type === "structured_binding_declarator") return true;
|
|
1220
|
+
if (p.type.endsWith("declarator") || p.type === "init_declarator") {
|
|
1221
|
+
return isDeclaringChainLeftmost(node);
|
|
1222
|
+
}
|
|
1223
|
+
return true;
|
|
1224
|
+
};
|
|
1225
|
+
const isPreprocText = (node) => {
|
|
1226
|
+
let cur = node.parent;
|
|
1227
|
+
while (cur) {
|
|
1228
|
+
const t = cur.type;
|
|
1229
|
+
if (t.includes("statement") || t.includes("declaration") || t.includes("declarator") || t.includes("specifier") || t.includes("enumerator") || t.includes("compound") || t.includes("argument_list") || t.includes("init") || t.includes("expression") || t.includes("function_definition") || t.includes("field_declaration_list")) return false;
|
|
1230
|
+
if (t.startsWith("preproc_")) return true;
|
|
1231
|
+
cur = cur.parent;
|
|
1232
|
+
}
|
|
1233
|
+
return false;
|
|
1234
|
+
};
|
|
1235
|
+
const hasQualifiedAncestor = (node) => {
|
|
1236
|
+
let cur = node.parent;
|
|
1237
|
+
while (cur) {
|
|
1238
|
+
const t = cur.type;
|
|
1239
|
+
if (t === "qualified_identifier") return true;
|
|
1240
|
+
if (t.includes("statement") || t.includes("declaration") || t.includes("declarator") || t.includes("expression") || t.includes("function_definition") || t.includes("field_declaration_list") || t.includes("argument_list")) return false;
|
|
1241
|
+
cur = cur.parent;
|
|
1242
|
+
}
|
|
1243
|
+
return false;
|
|
1244
|
+
};
|
|
1245
|
+
const hasAttributeAncestor = (node) => {
|
|
1246
|
+
let cur = node.parent;
|
|
1247
|
+
while (cur) {
|
|
1248
|
+
const t = cur.type;
|
|
1249
|
+
if (t === "attribute" || t === "attribute_declaration" || t === "attribute_specifier") return true;
|
|
1250
|
+
if (t.includes("statement") || t.includes("declaration") || t.includes("function_definition") || t.includes("expression") || t.includes("field_declaration_list")) return false;
|
|
1251
|
+
cur = cur.parent;
|
|
1252
|
+
}
|
|
1253
|
+
return false;
|
|
1254
|
+
};
|
|
1255
|
+
const memberLeafTop = (node) => {
|
|
1256
|
+
let top = node;
|
|
1257
|
+
let p = node.parent;
|
|
1258
|
+
while (p && (p.type === "template_function" || p.type === "template_method" || p.type === "dependent_name")) {
|
|
1259
|
+
top = p;
|
|
1260
|
+
p = p.parent;
|
|
1261
|
+
}
|
|
1262
|
+
return top;
|
|
1263
|
+
};
|
|
1264
|
+
const isMemberLeaf = (node) => {
|
|
1265
|
+
const top = memberLeafTop(node);
|
|
1266
|
+
const p = top.parent;
|
|
1267
|
+
if (!p || p.type !== "field_expression") return false;
|
|
1268
|
+
const field = p.childForFieldName("field");
|
|
1269
|
+
return !!field && field.startIndex === top.startIndex;
|
|
1270
|
+
};
|
|
1271
|
+
const isCallCallee = (node) => {
|
|
1272
|
+
let top = node;
|
|
1273
|
+
let p = node.parent;
|
|
1274
|
+
while (p && (p.type === "template_function" || p.type === "template_method" || p.type === "dependent_name" || p.type === "parenthesized_expression")) {
|
|
1275
|
+
top = p;
|
|
1276
|
+
p = p.parent;
|
|
1277
|
+
}
|
|
1278
|
+
if (!p || p.type !== "call_expression") return false;
|
|
1279
|
+
const fnField = p.childForFieldName("function");
|
|
1280
|
+
if (fnField) return fnField.startIndex === top.startIndex && fnField.endIndex >= top.endIndex;
|
|
1281
|
+
const first = p.namedChild(0);
|
|
1282
|
+
return !!first && first.startIndex === top.startIndex && first.endIndex >= top.endIndex;
|
|
1283
|
+
};
|
|
1284
|
+
const isFunctionOwnName = (node) => {
|
|
1285
|
+
let up = node.parent;
|
|
1286
|
+
while (up) {
|
|
1287
|
+
if (up.type === "parameter_list" || up.type === "parameter_declaration") return false;
|
|
1288
|
+
if (up.type === "function_declarator" && up.parent && up.parent.type === "function_definition") return true;
|
|
1289
|
+
up = up.parent;
|
|
1290
|
+
}
|
|
1291
|
+
return false;
|
|
1292
|
+
};
|
|
1293
|
+
const enclosingFunctionScope = () => {
|
|
1294
|
+
for (let i = scopeStack.length - 1; i >= 0; i--) {
|
|
1295
|
+
const s = scopeStack[i];
|
|
1296
|
+
if (s.kind === ASTEngine.SymbolKind.Function || s.kind === ASTEngine.SymbolKind.Method || s.kind === ASTEngine.SymbolKind.Constructor) return s;
|
|
1297
|
+
}
|
|
1298
|
+
return null;
|
|
1299
|
+
};
|
|
1300
|
+
const ownerIsFunctionScope = (ctx) => !!ctx && (ctx.kind === ASTEngine.SymbolKind.Function || ctx.kind === ASTEngine.SymbolKind.Method || ctx.kind === ASTEngine.SymbolKind.Constructor);
|
|
1301
|
+
const isResolvableRead = (name) => {
|
|
1302
|
+
if (fileScopeValues.has(name)) return true;
|
|
1303
|
+
const enclosingNs = [];
|
|
1304
|
+
for (let i = scopeStack.length - 1; i >= 0; i--) {
|
|
1305
|
+
const s = scopeStack[i];
|
|
1306
|
+
if (s.kind === ASTEngine.SymbolKind.Class || s.kind === ASTEngine.SymbolKind.Struct) {
|
|
1307
|
+
const mm = classMembers.get(s.name);
|
|
1308
|
+
if (mm && mm.has(name)) return true;
|
|
1309
|
+
}
|
|
1310
|
+
if ((s.kind === ASTEngine.SymbolKind.Function || s.kind === ASTEngine.SymbolKind.Method || s.kind === ASTEngine.SymbolKind.Constructor) && s.classScope) {
|
|
1311
|
+
const mm = classMembers.get(s.classScope);
|
|
1312
|
+
if (mm && mm.has(name)) return true;
|
|
1313
|
+
}
|
|
1314
|
+
if (s.kind === ASTEngine.SymbolKind.Namespace && s.name !== CPP_ANONYMOUS) enclosingNs.push(s.name);
|
|
1315
|
+
}
|
|
1316
|
+
for (const ns of enclosingNs) {
|
|
1317
|
+
const nm = namespaceValues.get(ns);
|
|
1318
|
+
if (nm && nm.has(name)) return true;
|
|
1319
|
+
}
|
|
1320
|
+
for (const used of usedNamespaces) {
|
|
1321
|
+
const nm = namespaceValues.get(used);
|
|
1322
|
+
if (nm && nm.has(name)) return true;
|
|
1323
|
+
}
|
|
1324
|
+
return false;
|
|
1325
|
+
};
|
|
1326
|
+
const declaredNameNode = (node) => {
|
|
1327
|
+
let cur = node;
|
|
1328
|
+
while (cur) {
|
|
1329
|
+
if (cur.type === "identifier" || cur.type === "field_identifier") {
|
|
1330
|
+
if (!isCppSpelledName(cur.text)) return null;
|
|
1331
|
+
return cur;
|
|
1332
|
+
}
|
|
1333
|
+
if (cur.type === "function_declarator" || cur.type === "abstract_function_declarator") {
|
|
1334
|
+
const inner = cur.childForFieldName?.("declarator") ?? null;
|
|
1335
|
+
if (!inner || inner.type === "identifier" || inner.type === "field_identifier" || inner.type === "qualified_identifier" || inner.type === "operator_name" || inner.type === "destructor_name" || inner.type === "type_identifier") {
|
|
1336
|
+
return null;
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
const viaField = cur.childForFieldName?.("declarator") ?? null;
|
|
1340
|
+
let next = viaField;
|
|
1341
|
+
if (!next) {
|
|
1342
|
+
for (let k = 0; k < cur.namedChildCount; k++) {
|
|
1343
|
+
const c = cur.namedChild(k);
|
|
1344
|
+
if (c && (c.type.endsWith("declarator") || c.type === "identifier" || c.type === "field_identifier")) {
|
|
1345
|
+
next = c;
|
|
1346
|
+
break;
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1349
|
+
}
|
|
1350
|
+
cur = next;
|
|
1351
|
+
}
|
|
1352
|
+
return null;
|
|
1353
|
+
};
|
|
1354
|
+
const declaredValueName = (node) => {
|
|
1355
|
+
const decl = node?.childForFieldName?.("declarator") ?? null;
|
|
1356
|
+
if (!decl) return null;
|
|
1357
|
+
if (decl.type === "function_declarator" || decl.type === "abstract_function_declarator") {
|
|
1358
|
+
const inner = decl.childForFieldName?.("declarator") ?? null;
|
|
1359
|
+
if (!inner || inner.type === "identifier" || inner.type === "field_identifier" || inner.type === "qualified_identifier" || inner.type === "operator_name" || inner.type === "destructor_name") {
|
|
1360
|
+
return null;
|
|
1361
|
+
}
|
|
1362
|
+
}
|
|
1363
|
+
return declaredNameNode(decl);
|
|
1364
|
+
};
|
|
1365
|
+
const declaratorParamNames = (decl) => {
|
|
1366
|
+
const out = [];
|
|
1367
|
+
if (!decl) return out;
|
|
1368
|
+
const walk = [decl];
|
|
1369
|
+
while (walk.length > 0) {
|
|
1370
|
+
const m = walk.pop();
|
|
1371
|
+
if (m.type === "parameter_declaration") {
|
|
1372
|
+
const nm = declaredNameNode(m);
|
|
1373
|
+
if (nm) out.push(nm.text);
|
|
1374
|
+
continue;
|
|
1375
|
+
}
|
|
1376
|
+
for (let k = 0; k < m.namedChildCount; k++) {
|
|
1377
|
+
const c = m.namedChild(k);
|
|
1378
|
+
if (c) walk.push(c);
|
|
1379
|
+
}
|
|
1380
|
+
}
|
|
1381
|
+
return out;
|
|
1382
|
+
};
|
|
1383
|
+
const declaratorParameters = (decl) => {
|
|
1384
|
+
const out = [];
|
|
1385
|
+
if (!decl) return out;
|
|
1386
|
+
const paramsNode = decl.childForFieldName?.("parameters") ?? this.findChild(decl, "parameter_list") ?? null;
|
|
1387
|
+
if (!paramsNode) return out;
|
|
1388
|
+
for (let i = 0; i < paramsNode.namedChildCount; i++) {
|
|
1389
|
+
const p = paramsNode.namedChild(i);
|
|
1390
|
+
if (!p) continue;
|
|
1391
|
+
if (p.type === "comment") continue;
|
|
1392
|
+
if (p.type === "variadic_parameter_declaration") continue;
|
|
1393
|
+
if (p.type !== "parameter_declaration" && p.type !== "optional_parameter_declaration") continue;
|
|
1394
|
+
const nameNode = declaredNameNode(p);
|
|
1395
|
+
if (!nameNode) continue;
|
|
1396
|
+
const cut = nameNode.startIndex - p.startIndex;
|
|
1397
|
+
const type = cut > 0 ? p.text.slice(0, cut).trim() : "";
|
|
1398
|
+
const param = { name: nameNode.text };
|
|
1399
|
+
if (type) param.type = type;
|
|
1400
|
+
const defaultNode = p.childForFieldName?.("default_value") ?? null;
|
|
1401
|
+
if (defaultNode || p.type === "optional_parameter_declaration") {
|
|
1402
|
+
if (defaultNode) param.defaultValue = defaultNode.text;
|
|
1403
|
+
param.isOptional = true;
|
|
1404
|
+
}
|
|
1405
|
+
out.push(param);
|
|
1406
|
+
}
|
|
1407
|
+
return out;
|
|
1408
|
+
};
|
|
1409
|
+
const buildParamSig = (parametersNode) => {
|
|
1410
|
+
if (!parametersNode) return "";
|
|
1411
|
+
const parts = [];
|
|
1412
|
+
for (let i = 0; i < parametersNode.namedChildCount; i++) {
|
|
1413
|
+
const pc = parametersNode.namedChild(i);
|
|
1414
|
+
if (!pc) continue;
|
|
1415
|
+
if (pc.type === "comment") continue;
|
|
1416
|
+
if (pc.type === "variadic_parameter_declaration") {
|
|
1417
|
+
parts.push("...");
|
|
1418
|
+
continue;
|
|
1419
|
+
}
|
|
1420
|
+
if (pc.type !== "parameter_declaration" && pc.type !== "optional_parameter_declaration") {
|
|
1421
|
+
continue;
|
|
1422
|
+
}
|
|
1423
|
+
const eqAt = pc.text.indexOf("=");
|
|
1424
|
+
const nameNode = declaredNameNode(pc);
|
|
1425
|
+
let t = pc.text;
|
|
1426
|
+
if (nameNode) {
|
|
1427
|
+
const cut = nameNode.startIndex - pc.startIndex;
|
|
1428
|
+
if (cut > 0) t = t.slice(0, cut);
|
|
1429
|
+
} else if (eqAt !== -1) {
|
|
1430
|
+
t = t.slice(0, eqAt);
|
|
1431
|
+
}
|
|
1432
|
+
t = t.trim();
|
|
1433
|
+
if (t) parts.push(t);
|
|
1434
|
+
}
|
|
1435
|
+
return parts.join(",");
|
|
1436
|
+
};
|
|
1437
|
+
const declaredReturnType = (node, declarator) => {
|
|
1438
|
+
if (declarator) {
|
|
1439
|
+
const tail = this.findChild(declarator, "trailing_return_type") ?? null;
|
|
1440
|
+
const td = tail ? this.findChild(tail, "type_descriptor") ?? tail.namedChild(0) : null;
|
|
1441
|
+
const t = td?.text?.trim();
|
|
1442
|
+
if (t) return t;
|
|
1443
|
+
}
|
|
1444
|
+
const typeField = node?.childForFieldName?.("type") ?? null;
|
|
1445
|
+
if (!typeField) return void 0;
|
|
1446
|
+
if (typeField.type === "macro_prefixed_type_specifier") return specifierTypeText(typeField);
|
|
1447
|
+
const stated = collapseSpaces(typeField.text);
|
|
1448
|
+
if (!stated) return void 0;
|
|
1449
|
+
if (!isMacroSpelling(stated)) return stated;
|
|
1450
|
+
const rejoined = phantomQualifierType(declarator?.childForFieldName?.("declarator") ?? null);
|
|
1451
|
+
if (rejoined) return rejoined;
|
|
1452
|
+
if (!node) return stated;
|
|
1453
|
+
const displaced = this.displacedReturnType(node, declarator);
|
|
1454
|
+
if (displaced) return displaced;
|
|
1455
|
+
return this.recoveredDecoratorHead(node, declarator) ? void 0 : stated;
|
|
1456
|
+
};
|
|
1457
|
+
const buildFunctionSymbol = (node, declarator, parts) => {
|
|
1458
|
+
const parameters = declaratorParameters(declarator);
|
|
1459
|
+
const returnType = declaredReturnType(node, declarator);
|
|
1460
|
+
const ownerName = parts.ownerName === "global" ? void 0 : parts.ownerName;
|
|
1461
|
+
const decorators = parts.decorators ?? [];
|
|
1462
|
+
return {
|
|
1463
|
+
name: parts.name,
|
|
1464
|
+
kind: parts.kind,
|
|
1465
|
+
range: this.nodeToRange(node),
|
|
1466
|
+
complexity: this.methodComplexityCpp(node),
|
|
1467
|
+
visibility: parts.visibility || "public",
|
|
1468
|
+
// Absent for a construct with no linkage of its own (a friend definition),
|
|
1469
|
+
// present-and-false for every member declaration: the store reads the KEY.
|
|
1470
|
+
...parts.isStatic === void 0 ? {} : { isStatic: parts.isStatic },
|
|
1471
|
+
ownerName,
|
|
1472
|
+
ownerKind: ownerName === void 0 ? void 0 : parts.ownerKind,
|
|
1473
|
+
...parameters.length > 0 ? { parameters } : {},
|
|
1474
|
+
...returnType ? { returnType } : {},
|
|
1475
|
+
...decorators.length > 0 ? { decorators } : {},
|
|
1476
|
+
metadata: { isCppFunction: true, ...parts.metadata }
|
|
1477
|
+
};
|
|
1478
|
+
};
|
|
1479
|
+
const INDEX_CLASS_KINDS = /* @__PURE__ */ new Set(["class", "struct", "union"]);
|
|
1480
|
+
const enclosingClassForOwner = () => {
|
|
1481
|
+
for (let i = scopeStack.length - 1; i >= 0; i--) {
|
|
1482
|
+
const sc = scopeStack[i];
|
|
1483
|
+
if (sc.kind === ASTEngine.SymbolKind.Class || sc.kind === ASTEngine.SymbolKind.Struct || sc.kind === ASTEngine.SymbolKind.Union) return sc.name;
|
|
1484
|
+
if ((sc.kind === ASTEngine.SymbolKind.Function || sc.kind === ASTEngine.SymbolKind.Method || sc.kind === ASTEngine.SymbolKind.Constructor) && sc.classScope) return sc.classScope;
|
|
1485
|
+
}
|
|
1486
|
+
return "";
|
|
1487
|
+
};
|
|
1488
|
+
const indexDeclaresMemberForOwner = (owner, leaf) => {
|
|
1489
|
+
if (!indexUsable || indexResolution === null) return false;
|
|
1490
|
+
const kind = indexTypeKinds.get(owner);
|
|
1491
|
+
if (typeof kind !== "string" || !INDEX_CLASS_KINDS.has(kind)) return false;
|
|
1492
|
+
return indexResolution.allMembers.get(owner)?.has(leaf) === true;
|
|
1493
|
+
};
|
|
1494
|
+
const writtenTypeCache = /* @__PURE__ */ new Map();
|
|
1495
|
+
const simpleDeclName = (decl, depth = 0) => {
|
|
1496
|
+
if (!decl || depth > 6) return "";
|
|
1497
|
+
const t = String(decl.type);
|
|
1498
|
+
if (t === "identifier" || t === "field_identifier") return decl.text.trim();
|
|
1499
|
+
if (t === "init_declarator" || t === "pointer_declarator" || t === "reference_declarator" || t === "array_declarator" || t === "parenthesized_declarator" || t === "attributed_declarator") {
|
|
1500
|
+
const inner = decl.childForFieldName?.("declarator") ?? decl.namedChild(Math.max(0, decl.namedChildCount - 1));
|
|
1501
|
+
return simpleDeclName(inner, depth + 1);
|
|
1502
|
+
}
|
|
1503
|
+
return "";
|
|
1504
|
+
};
|
|
1505
|
+
const writtenTypesOf = (fnNode) => {
|
|
1506
|
+
const cached = writtenTypeCache.get(fnNode.id);
|
|
1507
|
+
if (cached !== void 0) return cached;
|
|
1508
|
+
const table = /* @__PURE__ */ new Map();
|
|
1509
|
+
const add = (typeNode, declNode) => {
|
|
1510
|
+
if (!typeNode) return;
|
|
1511
|
+
const nm = simpleDeclName(declNode);
|
|
1512
|
+
if (nm === "" || !isIdentifierShapeWithDollar(nm)) return;
|
|
1513
|
+
if (!table.has(nm)) table.set(nm, String(typeNode.text ?? ""));
|
|
1514
|
+
};
|
|
1515
|
+
const scan = (n, depth = 0) => {
|
|
1516
|
+
if (!n || depth > 30) return;
|
|
1517
|
+
const t = String(n.type);
|
|
1518
|
+
if (t === "parameter" || t === "parameter_declaration" || t === "optional_parameter_declaration") {
|
|
1519
|
+
add(n.childForFieldName?.("type"), n.childForFieldName?.("declarator"));
|
|
1520
|
+
return;
|
|
1521
|
+
}
|
|
1522
|
+
if (t === "declaration") {
|
|
1523
|
+
const typeNode = n.childForFieldName?.("type");
|
|
1524
|
+
const firstDecl = n.childForFieldName?.("declarator");
|
|
1525
|
+
add(typeNode, firstDecl);
|
|
1526
|
+
for (let i = 0; i < n.namedChildCount; i++) {
|
|
1527
|
+
const c = n.namedChild(i);
|
|
1528
|
+
if (sameTsNode(c, typeNode) || sameTsNode(c, firstDecl)) continue;
|
|
1529
|
+
add(typeNode, c);
|
|
1530
|
+
}
|
|
1531
|
+
return;
|
|
1532
|
+
}
|
|
1533
|
+
if (t === "for_range_loop") {
|
|
1534
|
+
const typeField = n.childForFieldName?.("type");
|
|
1535
|
+
const declField = n.childForFieldName?.("declarator");
|
|
1536
|
+
let written = null;
|
|
1537
|
+
let declared = null;
|
|
1538
|
+
for (let i = 0; i < n.namedChildCount; i++) {
|
|
1539
|
+
const c = n.namedChild(i);
|
|
1540
|
+
if (sameTsNode(c, typeField) || sameTsNode(c, declField)) continue;
|
|
1541
|
+
if (!written) {
|
|
1542
|
+
written = c;
|
|
1543
|
+
continue;
|
|
1544
|
+
}
|
|
1545
|
+
if (!declared) {
|
|
1546
|
+
declared = c;
|
|
1547
|
+
break;
|
|
1548
|
+
}
|
|
1549
|
+
}
|
|
1550
|
+
add(written, declared);
|
|
1551
|
+
}
|
|
1552
|
+
for (let i = 0; i < n.namedChildCount; i++) scan(n.namedChild(i), depth + 1);
|
|
1553
|
+
};
|
|
1554
|
+
const decl = fnNode.childForFieldName?.("declarator");
|
|
1555
|
+
if (decl) scan(decl);
|
|
1556
|
+
const body = fnNode.childForFieldName?.("body") ?? fnNode.namedChild(fnNode.namedChildCount - 1);
|
|
1557
|
+
if (body) scan(body);
|
|
1558
|
+
writtenTypeCache.set(fnNode.id, table);
|
|
1559
|
+
return table;
|
|
1560
|
+
};
|
|
1561
|
+
const writtenTypeOfReceiver = (name) => {
|
|
1562
|
+
const fnScope = enclosingFunctionScope();
|
|
1563
|
+
const fnNode = fnScope?.node;
|
|
1564
|
+
if (!fnNode) return "";
|
|
1565
|
+
return writtenTypesOf(fnNode).get(name) ?? "";
|
|
1566
|
+
};
|
|
1567
|
+
const typeLeafOf = (typeText) => {
|
|
1568
|
+
let t = String(typeText ?? "");
|
|
1569
|
+
const lt = t.indexOf("<");
|
|
1570
|
+
if (lt > 0) t = t.slice(0, lt);
|
|
1571
|
+
for (const kw of ["const", "volatile", "static", "typename", "struct", "class", "union", "enum"]) t = t.split(kw).join(" ");
|
|
1572
|
+
t = collapseTypeNoise(t);
|
|
1573
|
+
const cut = Math.max(t.lastIndexOf("::"), t.lastIndexOf(" "));
|
|
1574
|
+
const leaf = (cut >= 0 ? t.slice(cut + 1) : t).trim();
|
|
1575
|
+
return isIdentifierShapeWithDollar(leaf) ? leaf : "";
|
|
1576
|
+
};
|
|
1577
|
+
const templatePointeeOf = (typeText) => {
|
|
1578
|
+
const lt = typeText.indexOf("<");
|
|
1579
|
+
const gt = typeText.lastIndexOf(">");
|
|
1580
|
+
if (lt < 0 || gt <= lt) return "";
|
|
1581
|
+
return typeLeafOf(typeText.slice(lt + 1, gt).split(",")[0] ?? "");
|
|
1582
|
+
};
|
|
1583
|
+
const OWNER_WRAPPER_TYPES = /* @__PURE__ */ new Set(["unique_ptr", "shared_ptr", "weak_ptr", "auto_ptr", "optional", "reference_wrapper", "atomic"]);
|
|
1584
|
+
const ownerCallLeafName = (n, depth = 0) => {
|
|
1585
|
+
if (!n || depth > 6) return "";
|
|
1586
|
+
const t = String(n.type);
|
|
1587
|
+
if (t === "identifier" || t === "field_identifier" || t === "type_identifier") return n.text.trim();
|
|
1588
|
+
if (t === "qualified_identifier") {
|
|
1589
|
+
const parts = String(n.text).split("::");
|
|
1590
|
+
return parts[parts.length - 1].trim();
|
|
1591
|
+
}
|
|
1592
|
+
if (t === "template_function" || t === "template_method") return ownerCallLeafName(n.namedChild(0), depth + 1);
|
|
1593
|
+
if (t === "field_expression") return ownerCallLeafName(n.childForFieldName?.("field"), depth + 1);
|
|
1594
|
+
return "";
|
|
1595
|
+
};
|
|
1596
|
+
const indexReceiverOwnerFacts = (callee, leaf) => {
|
|
1597
|
+
if (!indexUsable || leaf === "" || !callee) return {};
|
|
1598
|
+
if (callee.type === "field_expression") {
|
|
1599
|
+
const obj = callee.childForFieldName?.("argument") ?? callee.namedChild(0);
|
|
1600
|
+
if (!obj) return {};
|
|
1601
|
+
if (obj.type === "call_expression") {
|
|
1602
|
+
const innerCallee = obj.childForFieldName?.("function") ?? obj.namedChild(0);
|
|
1603
|
+
const owner = ownerCallLeafName(innerCallee);
|
|
1604
|
+
return owner !== "" && indexDeclaresMemberForOwner(owner, leaf) ? { owner } : {};
|
|
1605
|
+
}
|
|
1606
|
+
if (obj.type === "new_expression") {
|
|
1607
|
+
const typeNode = obj.childForFieldName?.("type") ?? null;
|
|
1608
|
+
const owner = typeNode ? typeLeafOf(String(typeNode.text ?? "")) : "";
|
|
1609
|
+
return owner !== "" && indexDeclaresMemberForOwner(owner, leaf) ? { owner } : {};
|
|
1610
|
+
}
|
|
1611
|
+
if (obj.type === "identifier") {
|
|
1612
|
+
const written = writtenTypeOfReceiver(obj.text.trim());
|
|
1613
|
+
if (written === "") return {};
|
|
1614
|
+
const holder = typeLeafOf(written);
|
|
1615
|
+
if (OWNER_WRAPPER_TYPES.has(holder)) {
|
|
1616
|
+
const pointee = templatePointeeOf(written);
|
|
1617
|
+
return pointee !== "" && indexDeclaresMemberForOwner(pointee, leaf) ? { owner: pointee } : {};
|
|
1618
|
+
}
|
|
1619
|
+
return holder !== "" && indexDeclaresMemberForOwner(holder, leaf) ? { owner: holder } : {};
|
|
1620
|
+
}
|
|
1621
|
+
return {};
|
|
1622
|
+
}
|
|
1623
|
+
if (callee.type === "identifier") {
|
|
1624
|
+
if (enclosingFunctionScope() === null) return {};
|
|
1625
|
+
const enclosing = enclosingClassForOwner();
|
|
1626
|
+
return enclosing !== "" && indexDeclaresMemberForOwner(enclosing, leaf) ? { owner: enclosing } : {};
|
|
1627
|
+
}
|
|
1628
|
+
return {};
|
|
1629
|
+
};
|
|
1630
|
+
while (stack.length > 0) {
|
|
1631
|
+
const item = stack.pop();
|
|
1632
|
+
const { node, parentContext, isExit } = item;
|
|
1633
|
+
if (isExit) {
|
|
1634
|
+
if (parentContext?.symbolRef && parentContext.isAbstract) {
|
|
1635
|
+
parentContext.symbolRef.isAbstract = true;
|
|
1636
|
+
if (parentContext.symbolRef.metadata) {
|
|
1637
|
+
parentContext.symbolRef.metadata.isAbstract = true;
|
|
1638
|
+
}
|
|
1639
|
+
}
|
|
1640
|
+
if (parentContext && scopeStack.length > 0 && scopeStack[scopeStack.length - 1] === parentContext) {
|
|
1641
|
+
scopeStack.pop();
|
|
1642
|
+
}
|
|
1643
|
+
continue;
|
|
1644
|
+
}
|
|
1645
|
+
if (node.isMissing || node.hasError) {
|
|
1646
|
+
if (node.type === "ERROR" || node.type === "MISSING") {
|
|
1647
|
+
if (errors.length < 3) errors.push({
|
|
1648
|
+
message: `Syntax ${node.type} detected`,
|
|
1649
|
+
range: this.nodeToRange(node),
|
|
1650
|
+
severity: "warning"
|
|
1651
|
+
});
|
|
1652
|
+
}
|
|
1653
|
+
}
|
|
1654
|
+
let newContext = parentContext;
|
|
1655
|
+
const type = node.type;
|
|
1656
|
+
let pushedScope = false;
|
|
1657
|
+
if (type === "identifier" || type === "qualified_identifier" || type === "type_identifier") {
|
|
1658
|
+
const text = node.text;
|
|
1659
|
+
if (text === "std::make_unique" || text === "std::make_shared") {
|
|
1660
|
+
memorySafetyScore = Math.min(100, memorySafetyScore + 5);
|
|
1661
|
+
}
|
|
1662
|
+
}
|
|
1663
|
+
if (type === "new_expression" || type === "delete_expression") {
|
|
1664
|
+
memorySafetyScore = Math.max(0, memorySafetyScore - 5);
|
|
1665
|
+
}
|
|
1666
|
+
if (type === "if_statement" || type === "for_statement" || type === "while_statement" || type === "do_statement" || type === "case_statement" || type === "catch_clause" || type === "conditional_expression") {
|
|
1667
|
+
fileDecisions++;
|
|
1668
|
+
} else if (type === "binary_expression") {
|
|
1669
|
+
const op = node.childForFieldName("operator")?.text;
|
|
1670
|
+
if (op === "&&" || op === "||") fileDecisions++;
|
|
1671
|
+
}
|
|
1672
|
+
if (type === "using_declaration") {
|
|
1673
|
+
const inClass = parentContext && (parentContext.kind === ASTEngine.SymbolKind.Class || parentContext.kind === ASTEngine.SymbolKind.Struct || parentContext.kind === ASTEngine.SymbolKind.Method || parentContext.kind === ASTEngine.SymbolKind.Function || parentContext.kind === ASTEngine.SymbolKind.Constructor);
|
|
1674
|
+
if (!inClass) {
|
|
1675
|
+
const targetNode = this.findChild(node, "qualified_identifier") || this.findChild(node, "identifier");
|
|
1676
|
+
const raw = targetNode?.text ?? "";
|
|
1677
|
+
const isNamespaceForm = node.text.indexOf("using namespace") !== -1;
|
|
1678
|
+
if (raw && isCppSpelledName(raw)) {
|
|
1679
|
+
const segs = raw.split("::").filter(Boolean);
|
|
1680
|
+
imports.push({
|
|
1681
|
+
module: raw,
|
|
1682
|
+
items: isNamespaceForm ? ["*"] : segs.length > 1 ? [segs[segs.length - 1]] : [raw],
|
|
1683
|
+
range: this.nodeToRange(node),
|
|
1684
|
+
metadata: { isImport: true, isUsingDeclaration: true, isNamespaceImport: isNamespaceForm }
|
|
1685
|
+
});
|
|
1686
|
+
if (!referencesSeen.has(raw)) {
|
|
1687
|
+
referencesSeen.add(raw);
|
|
1688
|
+
relationships.push({
|
|
1689
|
+
// File-level dependency statement → source "global" (same
|
|
1690
|
+
// convention as the #include references edge above).
|
|
1691
|
+
source: "global",
|
|
1692
|
+
target: raw,
|
|
1693
|
+
type: ASTEngine.RelationshipType.References,
|
|
1694
|
+
confidence: 0.9,
|
|
1695
|
+
// the written path names the edge, not the `using`
|
|
1696
|
+
// keyword — `using namespace\n std;` would otherwise name the line
|
|
1697
|
+
// above the module.
|
|
1698
|
+
line_number: this.nodeLine(targetNode ?? node),
|
|
1699
|
+
// M14-HONESTY-CONTRACT : an edge below 1.0 carries a
|
|
1700
|
+
// machine-readable reason code, and `classifyEvidenceTier` defaults
|
|
1701
|
+
// a MISSING confidence to 1 — so a 0.9 with no code is a number a
|
|
1702
|
+
// consumer can bucket but not explain. Measured over 929 corpus
|
|
1703
|
+
// files before this field existed: 625 `references` rows at 0.9 and
|
|
1704
|
+
// 0 of them with a code.
|
|
1705
|
+
//
|
|
1706
|
+
// What the 0.9 means here, stated so it is checkable: the edge
|
|
1707
|
+
// binds the NAME the source spells, and nothing inside the named
|
|
1708
|
+
// module. `using std::vector;` proves a dependency on `std`; it
|
|
1709
|
+
// does not prove which declaration of `vector` is meant, and this
|
|
1710
|
+
// analyzer does no header-level resolution to find out.
|
|
1711
|
+
metadata: {
|
|
1712
|
+
isUsingDeclaration: true,
|
|
1713
|
+
isImport: true,
|
|
1714
|
+
relationshipType: "using_dependency",
|
|
1715
|
+
reasonCode: "using-dependency-name-only"
|
|
1716
|
+
}
|
|
1717
|
+
});
|
|
1718
|
+
}
|
|
1719
|
+
}
|
|
1720
|
+
}
|
|
1721
|
+
} else if (type === "alias_declaration") {
|
|
1722
|
+
if (ownerIsFunctionScope(parentContext)) {
|
|
1723
|
+
newContext = parentContext;
|
|
1724
|
+
for (let i = node.namedChildCount - 1; i >= 0; i--) {
|
|
1725
|
+
const child = node.namedChild(i);
|
|
1726
|
+
if (child) stack.push({ node: child, parentContext: newContext });
|
|
1727
|
+
}
|
|
1728
|
+
continue;
|
|
1729
|
+
}
|
|
1730
|
+
const nameNode = node.childForFieldName("name") || this.findChild(node, "type_identifier");
|
|
1731
|
+
const name = nameNode?.text;
|
|
1732
|
+
if (name && isCppSpelledName(name)) {
|
|
1733
|
+
symbols.push({
|
|
1734
|
+
name,
|
|
1735
|
+
kind: ASTEngine.SymbolKind.Type,
|
|
1736
|
+
range: this.nodeToRange(node),
|
|
1737
|
+
visibility: parentContext?.visibility || "public",
|
|
1738
|
+
ownerName: parentContext?.name,
|
|
1739
|
+
ownerKind: parentContext?.kind,
|
|
1740
|
+
// #921: the type NAME's own line (a wrapped alias writes it below).
|
|
1741
|
+
metadata: { isTypeAlias: true, isUsingDeclaration: true, nameLine: this.nodeLine(nameNode) }
|
|
1742
|
+
});
|
|
1743
|
+
}
|
|
1744
|
+
} else if (type === "type_definition" || node.text.startsWith("typedef")) {
|
|
1745
|
+
if (ownerIsFunctionScope(parentContext)) {
|
|
1746
|
+
newContext = parentContext;
|
|
1747
|
+
for (let i = node.namedChildCount - 1; i >= 0; i--) {
|
|
1748
|
+
const child = node.namedChild(i);
|
|
1749
|
+
if (child) stack.push({ node: child, parentContext: newContext });
|
|
1750
|
+
}
|
|
1751
|
+
continue;
|
|
1752
|
+
}
|
|
1753
|
+
const typeId = this.findChild(node, "type_identifier");
|
|
1754
|
+
if (typeId && isCppSpelledName(typeId.text)) {
|
|
1755
|
+
symbols.push({
|
|
1756
|
+
name: typeId.text,
|
|
1757
|
+
kind: ASTEngine.SymbolKind.Type,
|
|
1758
|
+
range: this.nodeToRange(node),
|
|
1759
|
+
visibility: parentContext?.visibility || "public",
|
|
1760
|
+
ownerName: parentContext?.name,
|
|
1761
|
+
ownerKind: parentContext?.kind,
|
|
1762
|
+
metadata: { isTypeAlias: true, nameLine: this.nodeLine(typeId) }
|
|
1763
|
+
});
|
|
1764
|
+
}
|
|
1765
|
+
} else if (type === "preproc_def" || type === "preproc_function_def") {
|
|
1766
|
+
const nameNode = node.childForFieldName("name");
|
|
1767
|
+
const macroName = nameNode?.text?.trim();
|
|
1768
|
+
if (macroName && isCppSpelledName(macroName) && !symbols.some((s) => s.name === macroName)) {
|
|
1769
|
+
const isFunctionLikeMacro = type === "preproc_function_def";
|
|
1770
|
+
const parameters = [];
|
|
1771
|
+
if (isFunctionLikeMacro) {
|
|
1772
|
+
const paramsNode = node.childForFieldName("parameters") ?? this.findChild(node, "preproc_params");
|
|
1773
|
+
if (paramsNode) {
|
|
1774
|
+
for (let i = 0; i < paramsNode.childCount; i++) {
|
|
1775
|
+
const param = paramsNode.child(i);
|
|
1776
|
+
if (param && param.isNamed) parameters.push({ name: param.text, type: "any" });
|
|
1777
|
+
else if (param && (param.type === "..." || param.text === "...")) {
|
|
1778
|
+
parameters.push({ name: "...", type: "variadic", modifiers: [] });
|
|
1779
|
+
}
|
|
1780
|
+
}
|
|
1781
|
+
}
|
|
1782
|
+
}
|
|
1783
|
+
symbols.push({
|
|
1784
|
+
name: macroName,
|
|
1785
|
+
kind: isFunctionLikeMacro ? ASTEngine.SymbolKind.Macro : ASTEngine.SymbolKind.Constant,
|
|
1786
|
+
range: this.nodeToRange(node),
|
|
1787
|
+
scope: "global",
|
|
1788
|
+
visibility: "public",
|
|
1789
|
+
parameters: isFunctionLikeMacro ? parameters : void 0,
|
|
1790
|
+
metadata: { isMacro: true, isFunctionLikeMacro, nameLine: this.nodeLine(nameNode) }
|
|
1791
|
+
});
|
|
1792
|
+
}
|
|
1793
|
+
} else if (type === "preproc_include") {
|
|
1794
|
+
const text = node.text;
|
|
1795
|
+
let pathNode = node.childForFieldName("path");
|
|
1796
|
+
if (pathNode && pathNode.type !== "string_literal" && pathNode.type !== "system_lib_string") pathNode = null;
|
|
1797
|
+
if (!pathNode) {
|
|
1798
|
+
pathNode = this.findChild(node, "string_literal") ?? this.findChild(node, "system_lib_string");
|
|
1799
|
+
}
|
|
1800
|
+
const rawPath = (pathNode?.text ?? "").trim();
|
|
1801
|
+
const includePath = pathNode ? stripIncludeChars(rawPath).trim() : "";
|
|
1802
|
+
const isSystem = rawPath.startsWith("<");
|
|
1803
|
+
if (includePath) {
|
|
1804
|
+
imports.push({
|
|
1805
|
+
module: includePath,
|
|
1806
|
+
items: ["*"],
|
|
1807
|
+
range: this.nodeToRange(node),
|
|
1808
|
+
metadata: { isSystemInclude: isSystem, isImport: true, isIncludeDependency: true }
|
|
1809
|
+
});
|
|
1810
|
+
}
|
|
1811
|
+
if (includePath) {
|
|
1812
|
+
if (!referencesSeen.has(includePath)) {
|
|
1813
|
+
referencesSeen.add(includePath);
|
|
1814
|
+
const isQuotedPart = !isSystem;
|
|
1815
|
+
relationships.push({
|
|
1816
|
+
source: "global",
|
|
1817
|
+
// ★ THE TARGET IS A SYMBOL, NOT A PATH (per the measured ruling). MEASURED before this
|
|
1818
|
+
// change: emitting the PATH gave 0 resolved of 4,612 `part_of` edges — 100 % unresolved —
|
|
1819
|
+
// because the arbiter resolves TARGET STRINGS AGAINST SYMBOL NAMES and has no space for a
|
|
1820
|
+
// path. The contract permits either ("the part's file or its module symbol"); the symbol is
|
|
1821
|
+
// the only one that can bind.
|
|
1822
|
+
target: isQuotedPart ? cppPartSymbol(includePath) : includePath,
|
|
1823
|
+
type: isQuotedPart ? ASTEngine.RelationshipType.PartOf : ASTEngine.RelationshipType.Imports,
|
|
1824
|
+
confidence: 1,
|
|
1825
|
+
// , the C lane's rule: the include directive is the
|
|
1826
|
+
// source construct and its WRITTEN NAME is the path token — take
|
|
1827
|
+
// that node's line, not the directive's (a multi-line include puts
|
|
1828
|
+
// the path last).
|
|
1829
|
+
line_number: this.nodeLine(pathNode ?? node),
|
|
1830
|
+
metadata: {
|
|
1831
|
+
relationshipType: isQuotedPart ? "part_of_directive" : "include_dependency",
|
|
1832
|
+
isSystem,
|
|
1833
|
+
isSystemInclude: isSystem,
|
|
1834
|
+
// A part is NOT an import: the contract keeps `imports` and `part_of` DISTINCT, and
|
|
1835
|
+
// a quoted include that also claimed `isImport` would be the second fact that can
|
|
1836
|
+
// differ from the first.
|
|
1837
|
+
isImport: !isQuotedPart,
|
|
1838
|
+
isIncludeDependency: true,
|
|
1839
|
+
// THE MANDATORY PART ROLE (src/ast/types.ts:148-160). "part" is the directive that
|
|
1840
|
+
// NAMES a part (`part 'x.dart'` / `#include "x.h"`); "library" is the directive that
|
|
1841
|
+
// names the library a part belongs to (`part of 'y.dart'`). C++ has only the first
|
|
1842
|
+
// written form: the including unit names its fragments, never the reverse.
|
|
1843
|
+
...isQuotedPart ? {
|
|
1844
|
+
partRole: "part",
|
|
1845
|
+
partFile: true,
|
|
1846
|
+
isQuotedInclude: true,
|
|
1847
|
+
// THE PATH IS EVIDENCE, THE STEM IS THE TARGET. Keeping both is what makes the
|
|
1848
|
+
// difference between them checkable rather than a reader's inference.
|
|
1849
|
+
partPath: cppPartTargetPath(filePath, includePath),
|
|
1850
|
+
partSymbol: cppPartSymbol(includePath)
|
|
1851
|
+
} : {},
|
|
1852
|
+
// THE SPECIFIER IS KEPT BESIDE THE PATH: the path is what might resolve, the
|
|
1853
|
+
// specifier is what was WRITTEN, and only one of them is evidence.
|
|
1854
|
+
specifier: includePath,
|
|
1855
|
+
module: includePath,
|
|
1856
|
+
fileLevel: true
|
|
1857
|
+
}
|
|
1858
|
+
});
|
|
1859
|
+
}
|
|
1860
|
+
}
|
|
1861
|
+
const lowerText = text.toLowerCase();
|
|
1862
|
+
if (stringIncludes(lowerText, "memory") || stringIncludes(lowerText, "unique_ptr") || stringIncludes(lowerText, "shared_ptr")) {
|
|
1863
|
+
patterns["RAII"] = true;
|
|
1864
|
+
patterns["Smart Pointers"] = true;
|
|
1865
|
+
}
|
|
1866
|
+
if (stringIncludes(lowerText, "qt") || stringIncludes(lowerText, "qobject") || stringIncludes(lowerText, "qstring") || stringIncludes(lowerText, "qwidget"))
|
|
1867
|
+
frameworks["Qt"] = true;
|
|
1868
|
+
if (stringIncludes(lowerText, "iostream") || stringIncludes(lowerText, "vector") || stringIncludes(lowerText, "algorithm"))
|
|
1869
|
+
frameworks["stl"] = true;
|
|
1870
|
+
if (stringIncludes(lowerText, "windows.h")) frameworks["win32"] = true;
|
|
1871
|
+
}
|
|
1872
|
+
if (type === "auto") patterns["auto"] = true;
|
|
1873
|
+
if (type === "lambda_expression") patterns["lambda"] = true;
|
|
1874
|
+
if (this.hasDirectModifier(node, "constexpr")) patterns["constexpr"] = true;
|
|
1875
|
+
if (type === "access_specifier") {
|
|
1876
|
+
if (parentContext) {
|
|
1877
|
+
const t = node.text.replace(":", "").trim();
|
|
1878
|
+
if (t === "public" || t === "private" || t === "protected") {
|
|
1879
|
+
parentContext.visibility = t;
|
|
1880
|
+
}
|
|
1881
|
+
}
|
|
1882
|
+
}
|
|
1883
|
+
if (type === "namespace_definition") {
|
|
1884
|
+
const name = this.childText(node, "namespace_identifier") || CPP_ANONYMOUS;
|
|
1885
|
+
if (name === CPP_ANONYMOUS || !isCppSpelledName(name)) {
|
|
1886
|
+
newContext = parentContext;
|
|
1887
|
+
for (let i = node.namedChildCount - 1; i >= 0; i--) {
|
|
1888
|
+
const child = node.namedChild(i);
|
|
1889
|
+
if (child) stack.push({ node: child, parentContext: newContext });
|
|
1890
|
+
}
|
|
1891
|
+
continue;
|
|
1892
|
+
}
|
|
1893
|
+
symbols.push({
|
|
1894
|
+
name,
|
|
1895
|
+
kind: ASTEngine.SymbolKind.Namespace,
|
|
1896
|
+
range: this.nodeToRange(node),
|
|
1897
|
+
visibility: "public",
|
|
1898
|
+
metadata: { isCppNamespace: true, fullName: this.buildFullName(scopeStack, name) }
|
|
1899
|
+
});
|
|
1900
|
+
newContext = {
|
|
1901
|
+
node,
|
|
1902
|
+
name,
|
|
1903
|
+
kind: ASTEngine.SymbolKind.Namespace,
|
|
1904
|
+
visibility: "public",
|
|
1905
|
+
isTemplate: false,
|
|
1906
|
+
isAbstract: false,
|
|
1907
|
+
friends: []
|
|
1908
|
+
};
|
|
1909
|
+
scopeStack.push(newContext);
|
|
1910
|
+
pushedScope = true;
|
|
1911
|
+
} else if (type === "class_specifier" || type === "struct_specifier" || type === "union_specifier") {
|
|
1912
|
+
let bodyNode = this.findChild(node, "field_declaration_list");
|
|
1913
|
+
if (!bodyNode) {
|
|
1914
|
+
const decorated = macroDecoratedClassDecl(node);
|
|
1915
|
+
if (decorated) {
|
|
1916
|
+
const ownerName = parentContext?.name;
|
|
1917
|
+
const decorBases = [];
|
|
1918
|
+
for (const b of decorated.bases) {
|
|
1919
|
+
const target = extendsTargetName(b.text, decorated.name);
|
|
1920
|
+
if (!target) continue;
|
|
1921
|
+
decorBases.push(target);
|
|
1922
|
+
relationships.push({
|
|
1923
|
+
source: decorated.name,
|
|
1924
|
+
target,
|
|
1925
|
+
type: ASTEngine.RelationshipType.Extends,
|
|
1926
|
+
confidence: 1,
|
|
1927
|
+
line_number: this.nameLine(b),
|
|
1928
|
+
metadata: { nodeType: b.type, macroDecoratedHead: true, ...indexBaseFacts(target) }
|
|
1929
|
+
});
|
|
1930
|
+
}
|
|
1931
|
+
classBases.set(decorated.name, decorBases);
|
|
1932
|
+
const decorMacro = node.childForFieldName?.("name")?.text?.trim() ?? "";
|
|
1933
|
+
if (decorMacro && cppExportMarker(decorMacro)) {
|
|
1934
|
+
const ek = `exports::${decorated.name}::${decorMacro}`;
|
|
1935
|
+
if (!exportsSeen.has(ek)) {
|
|
1936
|
+
exportsSeen.add(ek);
|
|
1937
|
+
relationships.push({
|
|
1938
|
+
source: decorated.name,
|
|
1939
|
+
target: decorMacro,
|
|
1940
|
+
type: ASTEngine.RelationshipType.Exports,
|
|
1941
|
+
confidence: 1,
|
|
1942
|
+
line_number: this.nodeLine(node),
|
|
1943
|
+
metadata: { relationshipType: "module_export", exportMacro: decorMacro, reasonCode: "export-marker-in-specifier" }
|
|
1944
|
+
});
|
|
1945
|
+
}
|
|
1946
|
+
}
|
|
1947
|
+
const decoratedSym = {
|
|
1948
|
+
name: decorated.name,
|
|
1949
|
+
kind: ASTEngine.SymbolKind.Class,
|
|
1950
|
+
range: {
|
|
1951
|
+
startLine: node.startPosition.row + 1,
|
|
1952
|
+
startColumn: node.startPosition.column + 1,
|
|
1953
|
+
endLine: decorated.body.endPosition.row + 1,
|
|
1954
|
+
endColumn: decorated.body.endPosition.column + 1,
|
|
1955
|
+
startIndex: node.startIndex,
|
|
1956
|
+
endIndex: decorated.body.endIndex
|
|
1957
|
+
},
|
|
1958
|
+
...ownerName ? { ownerName, ownerKind: parentContext?.kind } : { scope: "global" },
|
|
1959
|
+
visibility: parentContext?.visibility || "public",
|
|
1960
|
+
metadata: {
|
|
1961
|
+
isCppClass: true,
|
|
1962
|
+
isTemplate: false,
|
|
1963
|
+
nameLine: this.nodeLine(decorated.nameNode),
|
|
1964
|
+
macroDecoratedHead: node.childForFieldName?.("name")?.text?.trim() ?? "",
|
|
1965
|
+
baseClasses: decorated.bases.map((b) => extendsTargetName(b.text, decorated.name)).filter((x) => !!x)
|
|
1966
|
+
}
|
|
1967
|
+
};
|
|
1968
|
+
symbols.push(decoratedSym);
|
|
1969
|
+
newContext = {
|
|
1970
|
+
node: decorated.owner,
|
|
1971
|
+
name: decorated.name,
|
|
1972
|
+
kind: ASTEngine.SymbolKind.Class,
|
|
1973
|
+
visibility: "public",
|
|
1974
|
+
isTemplate: false,
|
|
1975
|
+
isAbstract: false,
|
|
1976
|
+
friends: [],
|
|
1977
|
+
symbolRef: decoratedSym,
|
|
1978
|
+
locals: /* @__PURE__ */ new Set()
|
|
1979
|
+
};
|
|
1980
|
+
pushedScope = true;
|
|
1981
|
+
stack.push({ node: decorated.owner, parentContext: newContext, isExit: true });
|
|
1982
|
+
stack.push({ node: decorated.body, parentContext: newContext });
|
|
1983
|
+
continue;
|
|
1984
|
+
}
|
|
1985
|
+
newContext = parentContext;
|
|
1986
|
+
for (let i = node.namedChildCount - 1; i >= 0; i--) {
|
|
1987
|
+
const child = node.child(i);
|
|
1988
|
+
if (child) stack.push({ node: child, parentContext: newContext });
|
|
1989
|
+
}
|
|
1990
|
+
continue;
|
|
1991
|
+
}
|
|
1992
|
+
const name = this.childText(node, "type_identifier") || // D7 arm A: a `class`/`struct` may have a QUALIFIED name (an explicit
|
|
1993
|
+
// specialization, `template <> struct fmt::formatter<T> : formatter<U>
|
|
1994
|
+
// { … }`), which the direct-child `type_identifier` scan cannot see, so
|
|
1995
|
+
// the class published as `anonymous` — the no-name sentinel. The rule
|
|
1996
|
+
// is CPPD1's leaf, read off the grammar's `name` field.
|
|
1997
|
+
specifierDeclaredName(node) || (() => {
|
|
1998
|
+
const tt = this.findChild(node, "template_type");
|
|
1999
|
+
if (tt) {
|
|
2000
|
+
const tn = tt.childForFieldName?.("name") || this.findChild(tt, "type_identifier") || this.findChild(tt, "qualified_identifier");
|
|
2001
|
+
if (tn?.text) return tn.text;
|
|
2002
|
+
const lt = tt.text.indexOf("<");
|
|
2003
|
+
return lt > 0 ? tt.text.slice(0, lt).trim() : null;
|
|
2004
|
+
}
|
|
2005
|
+
return null;
|
|
2006
|
+
})() || CPP_ANONYMOUS;
|
|
2007
|
+
const kind = ASTEngine.SymbolKind.Class;
|
|
2008
|
+
const defaultVis = type === "class_specifier" ? "private" : "public";
|
|
2009
|
+
if (name === CPP_ANONYMOUS) {
|
|
2010
|
+
newContext = parentContext;
|
|
2011
|
+
for (let i = node.namedChildCount - 1; i >= 0; i--) {
|
|
2012
|
+
const child = node.namedChild(i);
|
|
2013
|
+
if (child) stack.push({ node: child, parentContext: newContext });
|
|
2014
|
+
}
|
|
2015
|
+
continue;
|
|
2016
|
+
}
|
|
2017
|
+
if (!isCppSpelledName(name)) {
|
|
2018
|
+
newContext = parentContext;
|
|
2019
|
+
for (let i = node.namedChildCount - 1; i >= 0; i--) {
|
|
2020
|
+
const child = node.namedChild(i);
|
|
2021
|
+
if (child) stack.push({ node: child, parentContext: newContext });
|
|
2022
|
+
}
|
|
2023
|
+
continue;
|
|
2024
|
+
}
|
|
2025
|
+
const isTemplate = node.parent?.type === "template_declaration";
|
|
2026
|
+
newContext = { node, name, kind, visibility: defaultVis, isTemplate, isAbstract: false, friends: [] };
|
|
2027
|
+
scopeStack.push(newContext);
|
|
2028
|
+
pushedScope = true;
|
|
2029
|
+
const baseClasses = [];
|
|
2030
|
+
const baseClause = this.findChild(node, "base_class_clause");
|
|
2031
|
+
if (baseClause) {
|
|
2032
|
+
for (let i = 0; i < baseClause.namedChildCount; i++) {
|
|
2033
|
+
const bc = baseClause.namedChild(i);
|
|
2034
|
+
if (!bc || bc.type === "access_specifier") continue;
|
|
2035
|
+
const typeNode = this.findChild(bc, "type_identifier") || this.findChild(bc, "qualified_identifier") || this.findChild(bc, "template_type") || bc;
|
|
2036
|
+
const target = extendsTargetName(typeNode.text, name);
|
|
2037
|
+
if (target) {
|
|
2038
|
+
baseClasses.push(target);
|
|
2039
|
+
relationships.push({
|
|
2040
|
+
source: name,
|
|
2041
|
+
target,
|
|
2042
|
+
type: ASTEngine.RelationshipType.Extends,
|
|
2043
|
+
confidence: 1,
|
|
2044
|
+
// a type expression's start is not always the
|
|
2045
|
+
// line of the names inside it (a specifier starts at its
|
|
2046
|
+
// KEYWORD) — `nameLine` prefers the grammar's `name` field.
|
|
2047
|
+
line_number: this.nameLine(typeNode),
|
|
2048
|
+
metadata: { nodeType: typeNode.type, ...indexBaseFacts(target) }
|
|
2049
|
+
});
|
|
2050
|
+
pendingImplements.push({ derived: name, base: target, line: this.nameLine(typeNode) ?? 1 });
|
|
2051
|
+
}
|
|
2052
|
+
}
|
|
2053
|
+
classBases.set(name, [...baseClasses]);
|
|
2054
|
+
}
|
|
2055
|
+
const sym = {
|
|
2056
|
+
name,
|
|
2057
|
+
kind: ASTEngine.SymbolKind.Class,
|
|
2058
|
+
range: this.nodeToRange(node),
|
|
2059
|
+
visibility: parentContext?.visibility || "public",
|
|
2060
|
+
ownerName: parentContext?.name,
|
|
2061
|
+
ownerKind: parentContext?.kind,
|
|
2062
|
+
metadata: {
|
|
2063
|
+
isCppClass: true,
|
|
2064
|
+
isTemplate,
|
|
2065
|
+
baseClasses,
|
|
2066
|
+
// #921: the class NAME's own line (`template <…>` puts it below).
|
|
2067
|
+
nameLine: this.nameLine(node)
|
|
2068
|
+
}
|
|
2069
|
+
};
|
|
2070
|
+
symbols.push(sym);
|
|
2071
|
+
newContext.symbolRef = sym;
|
|
2072
|
+
{
|
|
2073
|
+
const spelling = this.exportAttributeSpelling(node);
|
|
2074
|
+
if (spelling) {
|
|
2075
|
+
pendingExports.push({
|
|
2076
|
+
name,
|
|
2077
|
+
owner: parentContext?.name ?? null,
|
|
2078
|
+
line: this.nameLine(node) ?? 1,
|
|
2079
|
+
spelling
|
|
2080
|
+
});
|
|
2081
|
+
}
|
|
2082
|
+
}
|
|
2083
|
+
} else if (type === "compound_statement" && enclosingFunctionScope() === null && macroDeclaredBody(node) && // D8: a trailing ANNOTATION's body reaches this branch looking exactly like
|
|
2084
|
+
// a macro-declared heading's body, and publishing it here is what produced
|
|
2085
|
+
// `function GTEST_LOCK_EXCLUDED_(mutex_)` with `expandedFrom:
|
|
2086
|
+
// "macro-heading"` — a symbol for a macro, carrying the REAL function's
|
|
2087
|
+
// statements. The `function_definition` branch refuses the annotation
|
|
2088
|
+
// itself; this is the second half of the same suppression, because the body
|
|
2089
|
+
// is a SEPARATE node and arrives here on its own.
|
|
2090
|
+
//
|
|
2091
|
+
// The body's statements are still walked (the branch is only skipped, not
|
|
2092
|
+
// the subtree), so its reads keep a real owner instead of a macro's.
|
|
2093
|
+
!this.isTrailingAnnotationMacro(node.parent, content)) {
|
|
2094
|
+
const rec = macroDeclaredBody(node);
|
|
2095
|
+
const headingText = macroInvocationText(content, rec.nameNode.startIndex);
|
|
2096
|
+
if (headingText) {
|
|
2097
|
+
const headingRange = this.nodeToRange(rec.heading);
|
|
2098
|
+
const sym = {
|
|
2099
|
+
name: headingText,
|
|
2100
|
+
kind: ASTEngine.SymbolKind.Function,
|
|
2101
|
+
range: {
|
|
2102
|
+
...headingRange,
|
|
2103
|
+
endLine: node.endPosition.row + 1,
|
|
2104
|
+
endColumn: node.endPosition.column + 1,
|
|
2105
|
+
endIndex: node.endIndex
|
|
2106
|
+
},
|
|
2107
|
+
visibility: "public",
|
|
2108
|
+
metadata: {
|
|
2109
|
+
isCppFunction: false,
|
|
2110
|
+
expandedFrom: "macro-heading",
|
|
2111
|
+
macroName: rec.macroName,
|
|
2112
|
+
headingText
|
|
2113
|
+
}
|
|
2114
|
+
};
|
|
2115
|
+
symbols.push(sym);
|
|
2116
|
+
newContext = {
|
|
2117
|
+
node,
|
|
2118
|
+
name: headingText,
|
|
2119
|
+
kind: ASTEngine.SymbolKind.Function,
|
|
2120
|
+
visibility: "public",
|
|
2121
|
+
isTemplate: false,
|
|
2122
|
+
isAbstract: false,
|
|
2123
|
+
friends: [],
|
|
2124
|
+
symbolRef: sym,
|
|
2125
|
+
locals: /* @__PURE__ */ new Set()
|
|
2126
|
+
};
|
|
2127
|
+
scopeStack.push(newContext);
|
|
2128
|
+
pushedScope = true;
|
|
2129
|
+
}
|
|
2130
|
+
} else if (type === "enum_specifier") {
|
|
2131
|
+
const enumNameNode = node.childForFieldName("name") || this.findChild(node, "type_identifier");
|
|
2132
|
+
const displaced = attributeDisplacedEnumName(node);
|
|
2133
|
+
const name = displaced || this.childText(node, "type_identifier") || specifierDeclaredName(node) || CPP_ANONYMOUS;
|
|
2134
|
+
if (name === CPP_ANONYMOUS || !isCppSpelledName(name)) {
|
|
2135
|
+
newContext = parentContext;
|
|
2136
|
+
for (let i = node.namedChildCount - 1; i >= 0; i--) {
|
|
2137
|
+
const child = node.namedChild(i);
|
|
2138
|
+
if (child) stack.push({ node: child, parentContext: newContext });
|
|
2139
|
+
}
|
|
2140
|
+
continue;
|
|
2141
|
+
}
|
|
2142
|
+
let isEnumClass = false;
|
|
2143
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
2144
|
+
const child = node.child(i);
|
|
2145
|
+
if (child && (child.text === "class" || child.text === "struct")) {
|
|
2146
|
+
isEnumClass = true;
|
|
2147
|
+
break;
|
|
2148
|
+
}
|
|
2149
|
+
}
|
|
2150
|
+
symbols.push({
|
|
2151
|
+
name,
|
|
2152
|
+
kind: ASTEngine.SymbolKind.Enum,
|
|
2153
|
+
range: this.nodeToRange(node),
|
|
2154
|
+
visibility: parentContext?.visibility || "public",
|
|
2155
|
+
ownerName: parentContext?.name,
|
|
2156
|
+
ownerKind: parentContext?.kind,
|
|
2157
|
+
metadata: { isCppEnum: true, isEnumClass, isScopedEnum: isEnumClass, nameLine: this.nodeLine(enumNameNode) }
|
|
2158
|
+
});
|
|
2159
|
+
newContext = {
|
|
2160
|
+
node,
|
|
2161
|
+
name,
|
|
2162
|
+
kind: ASTEngine.SymbolKind.Enum,
|
|
2163
|
+
visibility: "public",
|
|
2164
|
+
isTemplate: false,
|
|
2165
|
+
isAbstract: false,
|
|
2166
|
+
friends: []
|
|
2167
|
+
};
|
|
2168
|
+
scopeStack.push(newContext);
|
|
2169
|
+
pushedScope = true;
|
|
2170
|
+
} else if (type === "function_definition" || type === "field_declaration" || type === "declaration") {
|
|
2171
|
+
const friendDecl = enclosingFriendDeclaration(node);
|
|
2172
|
+
if (friendDecl) {
|
|
2173
|
+
const friendDefines = type === "function_definition";
|
|
2174
|
+
const friendDetails = friendDefines ? this.getFunctionDetails(node) : null;
|
|
2175
|
+
if (friendDetails && !friendDetails.isDestructor && !this.isMacroFunctionShape(node, friendDetails.name)) {
|
|
2176
|
+
const owner = friendBodyOwner(scopeStack, friendDecl);
|
|
2177
|
+
const fnDeclarator2 = friendDetails.declarator;
|
|
2178
|
+
const sym = buildFunctionSymbol(node, fnDeclarator2, {
|
|
2179
|
+
name: friendDetails.name,
|
|
2180
|
+
kind: ASTEngine.SymbolKind.Function,
|
|
2181
|
+
visibility: parentContext?.visibility,
|
|
2182
|
+
// `global` (nothing published encloses the class) is the sentinel the
|
|
2183
|
+
// builder turns into "no owner": the friend belongs to the FILE then.
|
|
2184
|
+
ownerName: owner.name,
|
|
2185
|
+
ownerKind: owner.kind,
|
|
2186
|
+
metadata: {
|
|
2187
|
+
isCppFriendDefinition: true,
|
|
2188
|
+
isDestructor: false,
|
|
2189
|
+
isOperatorOverload: friendDetails.isOperator,
|
|
2190
|
+
// The friend's OWN template parameter list hangs one level higher
|
|
2191
|
+
// than a member's: `template_declaration > friend_declaration >
|
|
2192
|
+
// function_definition`, not `template_declaration >
|
|
2193
|
+
// function_definition`. The outer class's template is the CLASS
|
|
2194
|
+
// symbol's `isTemplate`, not this function's.
|
|
2195
|
+
isTemplate: node.parent?.type === "template_declaration" || friendDecl.parent?.type === "template_declaration",
|
|
2196
|
+
nameLine: this.nodeLine(friendDetails.nameNode)
|
|
2197
|
+
}
|
|
2198
|
+
});
|
|
2199
|
+
symbols.push(sym);
|
|
2200
|
+
newContext = {
|
|
2201
|
+
node,
|
|
2202
|
+
name: friendDetails.name,
|
|
2203
|
+
kind: ASTEngine.SymbolKind.Function,
|
|
2204
|
+
visibility: "public",
|
|
2205
|
+
isTemplate: false,
|
|
2206
|
+
isAbstract: false,
|
|
2207
|
+
friends: [],
|
|
2208
|
+
symbolRef: sym,
|
|
2209
|
+
locals: new Set(declaratorParamNames(fnDeclarator2))
|
|
2210
|
+
};
|
|
2211
|
+
scopeStack.push(newContext);
|
|
2212
|
+
pushedScope = true;
|
|
2213
|
+
stack.push({ node, parentContext: newContext, isExit: true });
|
|
2214
|
+
} else {
|
|
2215
|
+
newContext = parentContext;
|
|
2216
|
+
if (friendDefines) {
|
|
2217
|
+
const outer = friendBodyOwner(scopeStack, friendDecl);
|
|
2218
|
+
newContext = { ...parentContext, node: outer.node, name: outer.name, kind: outer.kind };
|
|
2219
|
+
}
|
|
2220
|
+
}
|
|
2221
|
+
for (let i = node.namedChildCount - 1; i >= 0; i--) {
|
|
2222
|
+
const child = node.namedChild(i);
|
|
2223
|
+
if (child) stack.push({ node: child, parentContext: newContext });
|
|
2224
|
+
}
|
|
2225
|
+
continue;
|
|
2226
|
+
}
|
|
2227
|
+
const funcDetails = this.getFunctionDetails(node);
|
|
2228
|
+
const fnDeclarator = funcDetails?.declarator ?? null;
|
|
2229
|
+
const cppDecorators = this.declarationDecorators(node, fnDeclarator);
|
|
2230
|
+
let isStatic = cppDecorators.includes("static");
|
|
2231
|
+
let isConstexpr = cppDecorators.includes("constexpr");
|
|
2232
|
+
let isConst = cppDecorators.includes("const");
|
|
2233
|
+
let isVirtual = cppDecorators.includes("virtual") || cppDecorators.includes("override");
|
|
2234
|
+
if (type === "function_definition" && this.isTrailingAnnotationMacro(node, content)) {
|
|
2235
|
+
for (let i = node.namedChildCount - 1; i >= 0; i--) {
|
|
2236
|
+
const child = node.namedChild(i);
|
|
2237
|
+
if (child) stack.push({ node: child, parentContext });
|
|
2238
|
+
}
|
|
2239
|
+
continue;
|
|
2240
|
+
}
|
|
2241
|
+
if (type === "function_definition" && isMacroDecoratedClassFunction(node)) {
|
|
2242
|
+
for (let i = node.namedChildCount - 1; i >= 0; i--) {
|
|
2243
|
+
const child = node.namedChild(i);
|
|
2244
|
+
if (!child || child.type === "compound_statement") continue;
|
|
2245
|
+
stack.push({ node: child, parentContext });
|
|
2246
|
+
}
|
|
2247
|
+
continue;
|
|
2248
|
+
}
|
|
2249
|
+
if (type === "function_definition" && this.returnTypeIsClassDefinition(node)) {
|
|
2250
|
+
for (let i = node.namedChildCount - 1; i >= 0; i--) {
|
|
2251
|
+
const child = node.namedChild(i);
|
|
2252
|
+
if (child) stack.push({ node: child, parentContext });
|
|
2253
|
+
}
|
|
2254
|
+
continue;
|
|
2255
|
+
}
|
|
2256
|
+
if (funcDetails) {
|
|
2257
|
+
const { name: funcName, scope: explicitScope, isDestructor, isOperator, nameNode: funcNameNode } = funcDetails;
|
|
2258
|
+
const ownerIsFunction = ownerIsFunctionScope(parentContext);
|
|
2259
|
+
if (funcDetails) {
|
|
2260
|
+
const marker = this.specifierExportMacro(node.childForFieldName?.("type") ?? null);
|
|
2261
|
+
if (marker && cppExportMarker(marker) && isCppSpelledName(funcName)) {
|
|
2262
|
+
const ek = `exports::${funcName}::${marker}`;
|
|
2263
|
+
if (!exportsSeen.has(ek)) {
|
|
2264
|
+
exportsSeen.add(ek);
|
|
2265
|
+
relationships.push({
|
|
2266
|
+
source: funcName,
|
|
2267
|
+
target: marker,
|
|
2268
|
+
type: ASTEngine.RelationshipType.Exports,
|
|
2269
|
+
confidence: 1,
|
|
2270
|
+
line_number: this.nodeLine(funcNameNode ?? node),
|
|
2271
|
+
metadata: {
|
|
2272
|
+
relationshipType: "module_export",
|
|
2273
|
+
exportMacro: marker,
|
|
2274
|
+
reasonCode: "export-marker-in-specifier"
|
|
2275
|
+
}
|
|
2276
|
+
});
|
|
2277
|
+
}
|
|
2278
|
+
}
|
|
2279
|
+
}
|
|
2280
|
+
if (ownerIsFunction && !explicitScope) {
|
|
2281
|
+
const fnScope = enclosingFunctionScope();
|
|
2282
|
+
if (fnScope && isCppSpelledName(funcName)) {
|
|
2283
|
+
if (!fnScope.locals) fnScope.locals = /* @__PURE__ */ new Set();
|
|
2284
|
+
fnScope.locals.add(funcName);
|
|
2285
|
+
}
|
|
2286
|
+
newContext = parentContext;
|
|
2287
|
+
for (let i = node.namedChildCount - 1; i >= 0; i--) {
|
|
2288
|
+
const child = node.namedChild(i);
|
|
2289
|
+
if (child) stack.push({ node: child, parentContext: newContext });
|
|
2290
|
+
}
|
|
2291
|
+
continue;
|
|
2292
|
+
}
|
|
2293
|
+
if (!isDestructor && this.isMacroFunctionShape(node, funcName)) {
|
|
2294
|
+
const annotation = this.isTrailingAnnotationMacro(node, content);
|
|
2295
|
+
const macroHeading = annotation ? null : this.macroHeadingText(node, content, funcName);
|
|
2296
|
+
let sym2 = null;
|
|
2297
|
+
if (macroHeading) {
|
|
2298
|
+
sym2 = {
|
|
2299
|
+
name: macroHeading,
|
|
2300
|
+
kind: ASTEngine.SymbolKind.Function,
|
|
2301
|
+
range: this.nodeToRange(node),
|
|
2302
|
+
visibility: "public",
|
|
2303
|
+
metadata: {
|
|
2304
|
+
isCppFunction: false,
|
|
2305
|
+
expandedFrom: "macro-heading",
|
|
2306
|
+
macroName: funcName,
|
|
2307
|
+
headingText: macroHeading
|
|
2308
|
+
}
|
|
2309
|
+
};
|
|
2310
|
+
symbols.push(sym2);
|
|
2311
|
+
}
|
|
2312
|
+
newContext = {
|
|
2313
|
+
node,
|
|
2314
|
+
name: macroHeading ?? "",
|
|
2315
|
+
kind: ASTEngine.SymbolKind.Function,
|
|
2316
|
+
visibility: "public",
|
|
2317
|
+
isTemplate: false,
|
|
2318
|
+
isAbstract: false,
|
|
2319
|
+
friends: [],
|
|
2320
|
+
...sym2 ? { symbolRef: sym2 } : {},
|
|
2321
|
+
locals: /* @__PURE__ */ new Set()
|
|
2322
|
+
};
|
|
2323
|
+
scopeStack.push(newContext);
|
|
2324
|
+
pushedScope = true;
|
|
2325
|
+
stack.push({ node, parentContext: newContext, isExit: true });
|
|
2326
|
+
for (let i = node.namedChildCount - 1; i >= 0; i--) {
|
|
2327
|
+
const child = node.namedChild(i);
|
|
2328
|
+
if (child) stack.push({ node: child, parentContext: newContext });
|
|
2329
|
+
}
|
|
2330
|
+
continue;
|
|
2331
|
+
}
|
|
2332
|
+
const actualOwnerName = explicitScope || parentContext?.name;
|
|
2333
|
+
const actualOwnerKind = explicitScope ? ASTEngine.SymbolKind.Class : parentContext?.kind;
|
|
2334
|
+
const isConstructor = actualOwnerName === funcName && actualOwnerKind === ASTEngine.SymbolKind.Class;
|
|
2335
|
+
const isPureVirtual = cppPureVirtualDeclaration(node);
|
|
2336
|
+
if (isPureVirtual && parentContext && parentContext.name === actualOwnerName) {
|
|
2337
|
+
parentContext.isAbstract = true;
|
|
2338
|
+
}
|
|
2339
|
+
const isTemplate = node.parent?.type === "template_declaration";
|
|
2340
|
+
const kind = isConstructor ? ASTEngine.SymbolKind.Constructor : actualOwnerKind === ASTEngine.SymbolKind.Class ? ASTEngine.SymbolKind.Method : ASTEngine.SymbolKind.Function;
|
|
2341
|
+
const sym = buildFunctionSymbol(node, fnDeclarator, {
|
|
2342
|
+
name: isConstructor ? "constructor" : funcName,
|
|
2343
|
+
kind,
|
|
2344
|
+
// CP3 (cpp review, HC1 parity): file-scope `static` (internal linkage) is
|
|
2345
|
+
// private. Class-member `static` is NOT internal linkage — members keep
|
|
2346
|
+
// their access-specifier visibility.
|
|
2347
|
+
visibility: isStatic && actualOwnerKind !== ASTEngine.SymbolKind.Class ? "private" : parentContext?.visibility,
|
|
2348
|
+
isStatic,
|
|
2349
|
+
ownerName: actualOwnerName,
|
|
2350
|
+
ownerKind: actualOwnerKind,
|
|
2351
|
+
decorators: cppDecorators,
|
|
2352
|
+
metadata: {
|
|
2353
|
+
isTemplate,
|
|
2354
|
+
isVirtual,
|
|
2355
|
+
isPureVirtual,
|
|
2356
|
+
isConst,
|
|
2357
|
+
isStatic,
|
|
2358
|
+
isConstexpr,
|
|
2359
|
+
isConstructor,
|
|
2360
|
+
isDestructor,
|
|
2361
|
+
isOperatorOverload: isOperator,
|
|
2362
|
+
// #612 store-phase: DECLARATION vs DEFINITION. This branch is reached for
|
|
2363
|
+
// all three node types that can name a function, and the type itself is
|
|
2364
|
+
// the whole discriminator — the grammar parses a body as
|
|
2365
|
+
// `function_definition` and a prototype (file-scope `int f(void);`, an
|
|
2366
|
+
// in-class `int f();`, a field declaration) as
|
|
2367
|
+
// `declaration`/`field_declaration`. Without this key the only signal the
|
|
2368
|
+
// store had was the symbol NAME, so a cpp header prototype competed on
|
|
2369
|
+
// equal footing with the .cpp definition in another file and name
|
|
2370
|
+
// resolution could pick the prototype.
|
|
2371
|
+
...type === "function_definition" ? {} : { isDeclaration: true },
|
|
2372
|
+
// #921 (M60): the line the declared NAME is written on. A wrapped return
|
|
2373
|
+
// type puts it below the declaration's start (`void NORETURN` then
|
|
2374
|
+
// `CheckFailed(const char *file, …)`), and the containment edge this
|
|
2375
|
+
// symbol may own has to name THAT line — the rule every other
|
|
2376
|
+
// relationship type already follows. Absent when the tree gives no name
|
|
2377
|
+
// node (the containment edge then falls back to the symbol range, which
|
|
2378
|
+
// is the pre-#921 behaviour).
|
|
2379
|
+
nameLine: this.nodeLine(funcNameNode)
|
|
2380
|
+
}
|
|
2381
|
+
});
|
|
2382
|
+
if (!isConstructor && kind === ASTEngine.SymbolKind.Method && parentContext?.kind === ASTEngine.SymbolKind.Class && funcName !== parentContext.name) {
|
|
2383
|
+
const decl2 = this.functionDeclaratorOf(node);
|
|
2384
|
+
const paramsNode = decl2?.childForFieldName("parameters") ?? null;
|
|
2385
|
+
const normParams = buildParamSig(paramsNode);
|
|
2386
|
+
const sigs = classVirtuals.get(parentContext.name) ?? /* @__PURE__ */ new Map();
|
|
2387
|
+
const prev = sigs.get(funcName) ?? [];
|
|
2388
|
+
if (!prev.includes(normParams)) sigs.set(funcName, [...prev, normParams]);
|
|
2389
|
+
else sigs.set(funcName, prev);
|
|
2390
|
+
classVirtuals.set(parentContext.name, sigs);
|
|
2391
|
+
if (this.isPureVirtualDeclaration(node)) {
|
|
2392
|
+
const pure = classPureVirtuals.get(parentContext.name) ?? /* @__PURE__ */ new Set();
|
|
2393
|
+
pure.add(funcName);
|
|
2394
|
+
classPureVirtuals.set(parentContext.name, pure);
|
|
2395
|
+
}
|
|
2396
|
+
const overrideKeyword = this.functionOverrideSpecifier(node);
|
|
2397
|
+
if ((classBases.get(parentContext.name)?.length ?? 0) > 0) {
|
|
2398
|
+
pendingOverrides.push({
|
|
2399
|
+
cls: parentContext.name,
|
|
2400
|
+
method: funcName,
|
|
2401
|
+
params: normParams,
|
|
2402
|
+
line: this.nodeLine(node),
|
|
2403
|
+
...overrideKeyword ? { keyword: overrideKeyword } : {}
|
|
2404
|
+
});
|
|
2405
|
+
}
|
|
2406
|
+
}
|
|
2407
|
+
{
|
|
2408
|
+
const spelling = this.exportAttributeSpelling(node);
|
|
2409
|
+
if (spelling) {
|
|
2410
|
+
const owner = parentContext && parentContext.kind !== ASTEngine.SymbolKind.Function ? parentContext.name : null;
|
|
2411
|
+
pendingExports.push({
|
|
2412
|
+
name: funcName,
|
|
2413
|
+
owner,
|
|
2414
|
+
line: this.nodeLine(funcNameNode ?? node) ?? 1,
|
|
2415
|
+
spelling
|
|
2416
|
+
});
|
|
2417
|
+
}
|
|
2418
|
+
}
|
|
2419
|
+
symbols.push(sym);
|
|
2420
|
+
if (type === "function_definition") {
|
|
2421
|
+
newContext = {
|
|
2422
|
+
node,
|
|
2423
|
+
// CPPD9 (deep review 2026-09-05): the scope NAME must match the
|
|
2424
|
+
// emitted symbol name. Constructors were pushed as the raw
|
|
2425
|
+
// class name (`Engine`) while their symbol was normalized to
|
|
2426
|
+
// "constructor" — so ctor-body calls emitted source=Engine (the
|
|
2427
|
+
// Class symbol) instead of the Constructor symbol, and the
|
|
2428
|
+
// qualified_calls payload never reached the constructor. The
|
|
2429
|
+
// family contract (groovy/java/csharp) scopes ctors under the
|
|
2430
|
+
// normalized "constructor" name, which is why the scope takes the
|
|
2431
|
+
// name off the SYMBOL rather than keeping a second copy of it.
|
|
2432
|
+
name: sym.name,
|
|
2433
|
+
kind,
|
|
2434
|
+
visibility: "public",
|
|
2435
|
+
isTemplate: false,
|
|
2436
|
+
isAbstract: false,
|
|
2437
|
+
friends: [],
|
|
2438
|
+
symbolRef: sym,
|
|
2439
|
+
// CP5: out-of-line class scope for member resolution.
|
|
2440
|
+
classScope: explicitScope,
|
|
2441
|
+
// CP1: parameters bind at function entry (flow-sensitive
|
|
2442
|
+
// shadowing like the C family — reads before any local
|
|
2443
|
+
// binding still resolve outward).
|
|
2444
|
+
locals: new Set(declaratorParamNames(this.findChild(node, "function_declarator")))
|
|
2445
|
+
};
|
|
2446
|
+
scopeStack.push(newContext);
|
|
2447
|
+
pushedScope = true;
|
|
2448
|
+
}
|
|
2449
|
+
} else if (type === "field_declaration" || type === "declaration") {
|
|
2450
|
+
const decls = this.findAllChildren(node, "init_declarator");
|
|
2451
|
+
if (decls.length === 0) {
|
|
2452
|
+
if (declaredValueName(node)) decls.push(node);
|
|
2453
|
+
}
|
|
2454
|
+
for (let idx = 0; idx < decls.length; idx++) {
|
|
2455
|
+
const d = decls[idx];
|
|
2456
|
+
const idNode = recoveredDeclaredName(d) ?? (d.type === "init_declarator" ? declaredNameNode(d) : declaredValueName(d));
|
|
2457
|
+
const name = idNode ? idNode.text : "";
|
|
2458
|
+
const isDisplacedEnumName = (() => {
|
|
2459
|
+
const holder = d.type === "init_declarator" ? d.parent ?? null : d;
|
|
2460
|
+
const tf = holder?.childForFieldName?.("type") ?? null;
|
|
2461
|
+
if (!tf || tf.type !== "enum_specifier") return false;
|
|
2462
|
+
return attributeDisplacedEnumName(tf) === name;
|
|
2463
|
+
})();
|
|
2464
|
+
const insideFunction = parentContext && (parentContext.kind === ASTEngine.SymbolKind.Function || parentContext.kind === ASTEngine.SymbolKind.Method || parentContext.kind === ASTEngine.SymbolKind.Constructor);
|
|
2465
|
+
if (name && insideFunction) {
|
|
2466
|
+
const fnScope = enclosingFunctionScope();
|
|
2467
|
+
if (fnScope) {
|
|
2468
|
+
if (!fnScope.locals) fnScope.locals = /* @__PURE__ */ new Set();
|
|
2469
|
+
fnScope.locals.add(name);
|
|
2470
|
+
}
|
|
2471
|
+
}
|
|
2472
|
+
if (name && !isDisplacedEnumName && !stringIncludes(name, "(") && !insideFunction && !this.isBareMacroDeclaration(node, name)) {
|
|
2473
|
+
const visStatic = isStatic && parentContext?.kind !== ASTEngine.SymbolKind.Class ? "private" : void 0;
|
|
2474
|
+
const declDecorators = this.declarationDecorators(node, this.findChild(node, "function_declarator"));
|
|
2475
|
+
symbols.push({
|
|
2476
|
+
name,
|
|
2477
|
+
kind: isConstexpr ? ASTEngine.SymbolKind.Constant : parentContext?.kind === ASTEngine.SymbolKind.Class ? ASTEngine.SymbolKind.Property : ASTEngine.SymbolKind.Variable,
|
|
2478
|
+
range: this.nodeToRange(d),
|
|
2479
|
+
visibility: visStatic || parentContext?.visibility || "public",
|
|
2480
|
+
isStatic,
|
|
2481
|
+
ownerName: parentContext?.name,
|
|
2482
|
+
ownerKind: parentContext?.kind,
|
|
2483
|
+
...declDecorators.length > 0 ? { decorators: declDecorators } : {},
|
|
2484
|
+
metadata: {
|
|
2485
|
+
isConstexpr,
|
|
2486
|
+
isStatic,
|
|
2487
|
+
isConst,
|
|
2488
|
+
// #921: the declared NAME's own line — `struct { … } options;`
|
|
2489
|
+
// and a wrapped type both write it below the declaration start.
|
|
2490
|
+
nameLine: this.nodeLine(idNode)
|
|
2491
|
+
}
|
|
2492
|
+
});
|
|
2493
|
+
}
|
|
2494
|
+
}
|
|
2495
|
+
}
|
|
2496
|
+
} else if (type === "enumerator") {
|
|
2497
|
+
const enumeratorNameNode = node.childForFieldName("name") || this.findChild(node, "identifier");
|
|
2498
|
+
const name = this.childText(node, "identifier");
|
|
2499
|
+
if (name && isCppSpelledName(name)) {
|
|
2500
|
+
const fnScope = enclosingFunctionScope();
|
|
2501
|
+
if (fnScope && scopeStack.length > 0) {
|
|
2502
|
+
if (this.insideFunctionBody(node.parent)) {
|
|
2503
|
+
if (!fnScope.locals) fnScope.locals = /* @__PURE__ */ new Set();
|
|
2504
|
+
fnScope.locals.add(name);
|
|
2505
|
+
}
|
|
2506
|
+
}
|
|
2507
|
+
symbols.push({
|
|
2508
|
+
name,
|
|
2509
|
+
kind: ASTEngine.SymbolKind.EnumMember,
|
|
2510
|
+
range: this.nodeToRange(node),
|
|
2511
|
+
visibility: "public",
|
|
2512
|
+
ownerName: parentContext?.name,
|
|
2513
|
+
ownerKind: parentContext?.kind,
|
|
2514
|
+
metadata: { isCppEnumMember: true, nameLine: this.nodeLine(enumeratorNameNode) }
|
|
2515
|
+
});
|
|
2516
|
+
}
|
|
2517
|
+
} else if (type === "field_expression") {
|
|
2518
|
+
const field = node.childForFieldName("field");
|
|
2519
|
+
const obj = node.childForFieldName("argument") || node.namedChild(0);
|
|
2520
|
+
const pType = node.parent?.type;
|
|
2521
|
+
const lhsAssign = pType === "assignment_expression" && node.parent?.namedChild(0) && node.parent.namedChild(0).startIndex === node.startIndex;
|
|
2522
|
+
if (field && field.text.trim() && isCppSpelledName(field.text) && pType !== "call_expression" && !lhsAssign) {
|
|
2523
|
+
const source = parentContext?.name || "global";
|
|
2524
|
+
const relKey = `${source}::uses::${field.text}`;
|
|
2525
|
+
if (!usesSeen.has(relKey)) {
|
|
2526
|
+
usesSeen.add(relKey);
|
|
2527
|
+
relationships.push({
|
|
2528
|
+
source,
|
|
2529
|
+
target: field.text,
|
|
2530
|
+
type: ASTEngine.RelationshipType.Uses,
|
|
2531
|
+
confidence: 0.9,
|
|
2532
|
+
// / #870: the FIELD LEAF names this edge. This row
|
|
2533
|
+
// carried no line at all, which is the #870 measurement (a member
|
|
2534
|
+
// read reported at line 0 in the fmt lane).
|
|
2535
|
+
line_number: this.nodeLine(field),
|
|
2536
|
+
// M14-HONESTY-CONTRACT , and the code says what the 0.9 means:
|
|
2537
|
+
// the edge states that THIS SCOPE READS a member with this name, and
|
|
2538
|
+
// the RECEIVER'S TYPE is not resolved — nothing here carries C++ type
|
|
2539
|
+
// inference, so which class declares the member is unknown and a
|
|
2540
|
+
// same-named member on an unrelated class is indistinguishable from
|
|
2541
|
+
// the intended one. Measured before this field existed: 3,012 `uses`
|
|
2542
|
+
// rows at 0.9, none carrying a code.
|
|
2543
|
+
metadata: {
|
|
2544
|
+
relationshipType: "field_access",
|
|
2545
|
+
accessedFrom: obj?.text,
|
|
2546
|
+
reasonCode: "member-read-receiver-type-unresolved"
|
|
2547
|
+
}
|
|
2548
|
+
});
|
|
2549
|
+
}
|
|
2550
|
+
}
|
|
2551
|
+
} else if (type === "call_expression") {
|
|
2552
|
+
const funcNode = node.childForFieldName("function") || node.namedChild(0);
|
|
2553
|
+
if (funcNode) {
|
|
2554
|
+
let targetName = funcNode.text;
|
|
2555
|
+
let qualifiedName = targetName;
|
|
2556
|
+
const NAME_BEARING = (t) => t === "identifier" || t === "field_identifier" || t === "type_identifier" || t === "operator_name" || t === "destructor_name" || t === "namespace_identifier";
|
|
2557
|
+
const calleeLeafNode = (n) => {
|
|
2558
|
+
let cur = n;
|
|
2559
|
+
let guard = 0;
|
|
2560
|
+
while (cur && guard++ < 8) {
|
|
2561
|
+
const t = cur.type;
|
|
2562
|
+
if (t === "field_expression") {
|
|
2563
|
+
const f = cur.childForFieldName("field") ?? cur.namedChild(cur.namedChildCount - 1);
|
|
2564
|
+
if (!f) return null;
|
|
2565
|
+
cur = f;
|
|
2566
|
+
continue;
|
|
2567
|
+
}
|
|
2568
|
+
if (t === "template_function" || t === "template_method") {
|
|
2569
|
+
const base = cur.namedChild(0);
|
|
2570
|
+
if (!base) return null;
|
|
2571
|
+
cur = base;
|
|
2572
|
+
continue;
|
|
2573
|
+
}
|
|
2574
|
+
if (t === "dependent_name" && cur.namedChildCount >= 1) {
|
|
2575
|
+
const inner = cur.namedChild(0);
|
|
2576
|
+
if (inner && (inner.type === "template_method" || inner.type === "template_function" || inner.type === "identifier" || inner.type === "field_identifier")) {
|
|
2577
|
+
cur = inner;
|
|
2578
|
+
continue;
|
|
2579
|
+
}
|
|
2580
|
+
return null;
|
|
2581
|
+
}
|
|
2582
|
+
if (t === "qualified_identifier" && cur.namedChildCount >= 1) {
|
|
2583
|
+
cur = cur.namedChild(cur.namedChildCount - 1);
|
|
2584
|
+
continue;
|
|
2585
|
+
}
|
|
2586
|
+
if (t === "parenthesized_expression" && cur.namedChildCount >= 1) {
|
|
2587
|
+
cur = cur.namedChild(0);
|
|
2588
|
+
continue;
|
|
2589
|
+
}
|
|
2590
|
+
if (!NAME_BEARING(t)) return null;
|
|
2591
|
+
if (t === "identifier" && !isCppSpelledName(cur.text)) return null;
|
|
2592
|
+
return cur;
|
|
2593
|
+
}
|
|
2594
|
+
return null;
|
|
2595
|
+
};
|
|
2596
|
+
const calleeLeaf = (n) => calleeLeafNode(n)?.text ?? "";
|
|
2597
|
+
let calleeSite = calleeLeafNode(funcNode);
|
|
2598
|
+
if (funcNode.type === "field_expression") {
|
|
2599
|
+
const field = funcNode.childForFieldName("field");
|
|
2600
|
+
const obj = funcNode.childForFieldName("argument") || funcNode.namedChild(0);
|
|
2601
|
+
if (field) {
|
|
2602
|
+
targetName = calleeLeaf(field);
|
|
2603
|
+
qualifiedName = obj ? `${obj.text}.${field.text}` : field.text;
|
|
2604
|
+
calleeSite = calleeLeafNode(field);
|
|
2605
|
+
}
|
|
2606
|
+
} else {
|
|
2607
|
+
targetName = calleeLeaf(funcNode);
|
|
2608
|
+
qualifiedName = funcNode.text;
|
|
2609
|
+
}
|
|
2610
|
+
const source = parentContext?.name || "global";
|
|
2611
|
+
const calleeIsTempCall = funcNode.type === "call_expression";
|
|
2612
|
+
const calleeIsUnnamed = calleeSite === null;
|
|
2613
|
+
const calleeIsError = !!funcNode.hasError;
|
|
2614
|
+
const ownerIsTypeScope = parentContext?.kind === ASTEngine.SymbolKind.Class || parentContext?.kind === ASTEngine.SymbolKind.Struct;
|
|
2615
|
+
const classCallingItself = ownerIsTypeScope && parentContext?.name === targetName;
|
|
2616
|
+
if (!targetName.startsWith("operator") && !calleeIsTempCall && !calleeIsUnnamed && !calleeIsError && !classCallingItself) {
|
|
2617
|
+
relationships.push({
|
|
2618
|
+
source,
|
|
2619
|
+
target: targetName,
|
|
2620
|
+
fromSymbol: source,
|
|
2621
|
+
toSymbol: qualifiedName,
|
|
2622
|
+
type: ASTEngine.RelationshipType.Calls,
|
|
2623
|
+
// M14 ONTOLOGY (this round), question 6 ("is `confidence` right?").
|
|
2624
|
+
// This row carried NO `confidence` at all, and it is the largest edge
|
|
2625
|
+
// family in the lane: measured 101,340 `calls` rows over 929 corpus
|
|
2626
|
+
// files, every one of them `undefined`, against a documentation
|
|
2627
|
+
// table that claimed 1.0. A consumer reading `rel.confidence` got
|
|
2628
|
+
// `undefined` on three quarters of the lane's output, and
|
|
2629
|
+
// `classifyEvidenceTier` papered over it by defaulting a missing
|
|
2630
|
+
// value to 1 — so the missing field was invisible from inside the
|
|
2631
|
+
// analyzer and from the evidence tier at the same time.
|
|
2632
|
+
//
|
|
2633
|
+
// 1.0 is the honest value, and it is not a claim that the CALLEE is
|
|
2634
|
+
// the function the author meant: it is a claim about what the edge
|
|
2635
|
+
// IS. A `call_expression` whose callee leaf is a name is a
|
|
2636
|
+
// deterministic AST fact, which is why `uses`/`extends`/
|
|
2637
|
+
// `instantiates`/`references` all publish a number for the same kind
|
|
2638
|
+
// of edge. Which overload a bare name selects is recorded separately
|
|
2639
|
+
// and honestly, in `metadata.qualified` (the full callee path) — it
|
|
2640
|
+
// is not a confidence question.
|
|
2641
|
+
confidence: 1,
|
|
2642
|
+
// / M60.P1 (#873): ONE line, ONE base, ONE node. The
|
|
2643
|
+
// row's `line_number` is the callee leaf's line (the node that
|
|
2644
|
+
// names the edge) and the metadata mirror repeats THAT value
|
|
2645
|
+
// instead of the call node's — a mirror that named a different
|
|
2646
|
+
// node would make "the row's line" ambiguous again, which is what
|
|
2647
|
+
// #873 was about. Base stays 1-based (R6).
|
|
2648
|
+
line_number: this.nodeLine(calleeSite),
|
|
2649
|
+
// task-22 (flag-gated): the project index's declaration facts for this callee. Empty
|
|
2650
|
+
// object = flag off / incomplete index, so the row is byte-identical to the verdict.
|
|
2651
|
+
metadata: {
|
|
2652
|
+
line: this.nodeLine(calleeSite),
|
|
2653
|
+
qualified: qualifiedName,
|
|
2654
|
+
...indexCallFacts(targetName),
|
|
2655
|
+
// task-22: a QUALIFIED callee names its owner syntactically; the owner channel can
|
|
2656
|
+
// resolve the row through it. Emitted only under the flag + a complete index + the
|
|
2657
|
+
// index agreeing the class-like owner DECLARES the member.
|
|
2658
|
+
...indexOwnerFacts(qualifiedName, targetName),
|
|
2659
|
+
// task-22 milestone 5: the three declaration-bearing RECEIVER classes (constructor-temp,
|
|
2660
|
+
// typed-local, implicit-this). Empty for opaque receivers — the owner is never guessed.
|
|
2661
|
+
...indexReceiverOwnerFacts(funcNode, targetName),
|
|
2662
|
+
// ISO C++ citation claim (Lead vocabulary `iso-cpp-stdlib`, flag-gated): ONLY a listed
|
|
2663
|
+
// symbol whose own spelling root is std; verified by the arbiter as extOK, never resolved.
|
|
2664
|
+
...cppCitationFacts(qualifiedName, targetName),
|
|
2665
|
+
// The package-scope claim (flag-gated): the callee is a member of a dependency the
|
|
2666
|
+
// GOVERNING MANIFEST declares, on the evidence of the translation unit's external
|
|
2667
|
+
// include roots. The package name carried is the MANIFEST'S OWN SPELLING, so the
|
|
2668
|
+
// arbiter's exact-match verification holds and the claim cannot refute itself.
|
|
2669
|
+
...cppPackageScopeFacts(targetName, qualifiedName, packageScopeRoots, packageScopePackages)
|
|
2670
|
+
}
|
|
2671
|
+
});
|
|
2672
|
+
const caller = parentContext?.symbolRef;
|
|
2673
|
+
if (caller && (caller.kind === ASTEngine.SymbolKind.Method || caller.kind === ASTEngine.SymbolKind.Function || caller.kind === ASTEngine.SymbolKind.Constructor)) {
|
|
2674
|
+
if (!caller.qualified_calls) caller.qualified_calls = [];
|
|
2675
|
+
caller.qualified_calls.push(qualifiedName);
|
|
2676
|
+
}
|
|
2677
|
+
}
|
|
2678
|
+
}
|
|
2679
|
+
} else if (type === "new_expression") {
|
|
2680
|
+
const typeNode = node.namedChild(0);
|
|
2681
|
+
if (typeNode) {
|
|
2682
|
+
let typeName = typeNode.text;
|
|
2683
|
+
let cur = typeNode;
|
|
2684
|
+
while (cur && cur.type === "qualified_identifier" && cur.namedChildCount >= 1) {
|
|
2685
|
+
const inner = cur.namedChild(cur.namedChildCount - 1);
|
|
2686
|
+
if (!inner) break;
|
|
2687
|
+
cur = inner;
|
|
2688
|
+
}
|
|
2689
|
+
if (cur && cur !== typeNode) typeName = cur.text;
|
|
2690
|
+
const lt3 = typeName.indexOf("<");
|
|
2691
|
+
if (lt3 !== -1) typeName = typeName.slice(0, lt3);
|
|
2692
|
+
if (this.isPlainIdentifier(typeName) && isCppSpelledName(typeName) && !CPP_NON_CLASS_STANDARD_TYPES.has(typeName) && !this.isTemplateParameterName(node, typeName)) {
|
|
2693
|
+
const source = parentContext?.name || "global";
|
|
2694
|
+
relationships.push({
|
|
2695
|
+
source,
|
|
2696
|
+
target: typeName,
|
|
2697
|
+
fromSymbol: source,
|
|
2698
|
+
toSymbol: typeNode.text,
|
|
2699
|
+
type: ASTEngine.RelationshipType.Instantiates,
|
|
2700
|
+
confidence: typeNode.hasError ? 0.6 : 1,
|
|
2701
|
+
// the constructed type's own node names this edge —
|
|
2702
|
+
// `new` and its type can sit on different lines. Same value in the
|
|
2703
|
+
// #873 mirror, 1-based.
|
|
2704
|
+
line_number: this.nodeLine(typeNode),
|
|
2705
|
+
metadata: { line: this.nodeLine(typeNode), isNew: true }
|
|
2706
|
+
});
|
|
2707
|
+
}
|
|
2708
|
+
}
|
|
2709
|
+
} else if (type === "identifier") {
|
|
2710
|
+
const p = node.parent;
|
|
2711
|
+
if (p && p.type === "lambda_capture_initializer" && p.namedChild(0)?.startIndex === node.startIndex) {
|
|
2712
|
+
const fnScope = enclosingFunctionScope();
|
|
2713
|
+
if (fnScope) {
|
|
2714
|
+
if (!fnScope.locals) fnScope.locals = /* @__PURE__ */ new Set();
|
|
2715
|
+
fnScope.locals.add(node.text);
|
|
2716
|
+
}
|
|
2717
|
+
} else if (isDeclarationPosition(node) && !isFunctionOwnName(node)) {
|
|
2718
|
+
const fnScope = enclosingFunctionScope();
|
|
2719
|
+
if (fnScope) {
|
|
2720
|
+
if (this.insideFunctionBody(p)) {
|
|
2721
|
+
if (!fnScope.locals) fnScope.locals = /* @__PURE__ */ new Set();
|
|
2722
|
+
fnScope.locals.add(node.text);
|
|
2723
|
+
}
|
|
2724
|
+
}
|
|
2725
|
+
} else if (node.text && // D1, EDGE GATE. The roster gate above is the structural fix; this is
|
|
2726
|
+
// the edge-level guarantee, so the impossibility holds on the row
|
|
2727
|
+
// itself even if a future path adds a name to a roster directly. A
|
|
2728
|
+
// keyword names no value, so it is never a read.
|
|
2729
|
+
isCppSpelledName(node.text) && !isDeclarationPosition(node) && !isPreprocText(node) && // Assignment LHS is a WRITE, never a read (C CGD1 / family
|
|
2730
|
+
// write-is-not-a-use contract — plain and compound assignments
|
|
2731
|
+
// both carry the LHS as first named child).
|
|
2732
|
+
!(p && p.type === "assignment_expression" && p.namedChild(0)?.startIndex === node.startIndex && (p.namedChild(0)?.endIndex ?? 0) >= node.endIndex) && // Call callee (span-matched like C CGD17, climbing through
|
|
2733
|
+
// template/dependent/parenthesized wrappers like the calls
|
|
2734
|
+
// branch's calleeLeaf) — the calls branch owns it.
|
|
2735
|
+
!isCallCallee(node) && // Member leaves — the field path owns them (both read and call
|
|
2736
|
+
// positions). The OBJECT of a field access is a genuine read and
|
|
2737
|
+
// stays (CGD31 contract): only the field-role leaf is skipped.
|
|
2738
|
+
// CPPD14: seen through template wrappers (`key.get_ref<string_t&>()`).
|
|
2739
|
+
!isMemberLeaf(node) && // Qualified reads (`ns::x`) — limitation, stays silent.
|
|
2740
|
+
!hasQualifiedAncestor(node) && // Labels and attributes never name values.
|
|
2741
|
+
!(p && (p.type === "goto_statement" || p.type === "labeled_statement")) && !hasAttributeAncestor(node)) {
|
|
2742
|
+
const fnScope = enclosingFunctionScope();
|
|
2743
|
+
const shadowed = !!fnScope?.locals?.has(node.text);
|
|
2744
|
+
if (!shadowed && isResolvableRead(node.text)) {
|
|
2745
|
+
const source = parentContext?.name || "global";
|
|
2746
|
+
const relKey = `${source}::uses::${node.text}`;
|
|
2747
|
+
if (!usesSeen.has(relKey)) {
|
|
2748
|
+
usesSeen.add(relKey);
|
|
2749
|
+
relationships.push({
|
|
2750
|
+
source,
|
|
2751
|
+
target: node.text,
|
|
2752
|
+
type: ASTEngine.RelationshipType.Uses,
|
|
2753
|
+
confidence: 1,
|
|
2754
|
+
line_number: this.nodeLine(node),
|
|
2755
|
+
metadata: { relationshipType: "identifier_usage", nodeType: node.type }
|
|
2756
|
+
});
|
|
2757
|
+
}
|
|
2758
|
+
}
|
|
2759
|
+
}
|
|
2760
|
+
}
|
|
2761
|
+
if (pushedScope) {
|
|
2762
|
+
stack.push({ node, parentContext: newContext, isExit: true });
|
|
2763
|
+
}
|
|
2764
|
+
for (let i = node.namedChildCount - 1; i >= 0; i--) {
|
|
2765
|
+
const child = node.namedChild(i);
|
|
2766
|
+
if (child) {
|
|
2767
|
+
stack.push({ node: child, parentContext: newContext });
|
|
2768
|
+
}
|
|
2769
|
+
}
|
|
2770
|
+
}
|
|
2771
|
+
const seenExportNames = /* @__PURE__ */ new Set();
|
|
2772
|
+
for (const sym of symbols) {
|
|
2773
|
+
if (!sym.ownerName && !sym.ownerKind) {
|
|
2774
|
+
const isPrivate = sym.visibility === "private" || sym.isStatic;
|
|
2775
|
+
const isMacroExpansion = sym.metadata?.expandedFrom === "macro-heading";
|
|
2776
|
+
if (!isPrivate && !isMacroExpansion && sym.name !== CPP_ANONYMOUS && !seenExportNames.has(sym.name)) {
|
|
2777
|
+
seenExportNames.add(sym.name);
|
|
2778
|
+
exports.push({
|
|
2779
|
+
name: sym.name,
|
|
2780
|
+
kind: sym.kind,
|
|
2781
|
+
range: sym.range,
|
|
2782
|
+
visibility: sym.visibility || "public"
|
|
2783
|
+
});
|
|
2784
|
+
}
|
|
2785
|
+
}
|
|
2786
|
+
}
|
|
2787
|
+
const companion = context?.overrideCompanion instanceof Map ? context.overrideCompanion : void 0;
|
|
2788
|
+
{
|
|
2789
|
+
const resolves = (startCls, method, params, keyword) => {
|
|
2790
|
+
const found = [];
|
|
2791
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2792
|
+
const queue = [startCls];
|
|
2793
|
+
let depth = 0;
|
|
2794
|
+
while (queue.length && depth < 10) {
|
|
2795
|
+
depth++;
|
|
2796
|
+
const cls = queue.shift();
|
|
2797
|
+
if (seen.has(cls)) continue;
|
|
2798
|
+
seen.add(cls);
|
|
2799
|
+
for (const base of classBases.get(cls) ?? []) {
|
|
2800
|
+
if (base === startCls) continue;
|
|
2801
|
+
const baseSigs = classVirtuals.get(base);
|
|
2802
|
+
const sigMatch = !!baseSigs && (baseSigs.get(method) ?? []).includes(params);
|
|
2803
|
+
if (sigMatch) {
|
|
2804
|
+
found.push({
|
|
2805
|
+
base,
|
|
2806
|
+
confidence: keyword ? 1 : 0.9,
|
|
2807
|
+
reasonCode: keyword ? "override-keyword+signature-match" : "signature-match"
|
|
2808
|
+
});
|
|
2809
|
+
} else if (keyword && companion) {
|
|
2810
|
+
const declared = companion.get(base);
|
|
2811
|
+
if (declared && declared.has(method)) {
|
|
2812
|
+
found.push({ base, confidence: 0.95, reasonCode: "override-keyword+companion-declares-method" });
|
|
2813
|
+
}
|
|
2814
|
+
}
|
|
2815
|
+
queue.push(base);
|
|
2816
|
+
}
|
|
2817
|
+
}
|
|
2818
|
+
return found;
|
|
2819
|
+
};
|
|
2820
|
+
const seenOv = /* @__PURE__ */ new Set();
|
|
2821
|
+
for (const pend of pendingOverrides) {
|
|
2822
|
+
for (const hit of resolves(pend.cls, pend.method, pend.params, !!pend.keyword)) {
|
|
2823
|
+
const k = `${pend.cls}.${pend.method}|${hit.base}.${pend.method}`;
|
|
2824
|
+
if (seenOv.has(k)) continue;
|
|
2825
|
+
seenOv.add(k);
|
|
2826
|
+
relationships.push({
|
|
2827
|
+
source: `${pend.cls}.${pend.method}`,
|
|
2828
|
+
// THE TARGET IS THE DECLARED MEMBER, NOT `Base.method`. The resolver matches an
|
|
2829
|
+
// edge target against a SYMBOL NAME (or a qualified-name alias), and a cpp member
|
|
2830
|
+
// symbol publishes only its leaf — `pool-worker.ts` normalises `qualifiedName` to
|
|
2831
|
+
// `name` for a symbol with no `scope`/`fqn`, so `Base.method` is a spelling no
|
|
2832
|
+
// declaration answers to. Measured before this spelling was restored: 1,875 of
|
|
2833
|
+
// 1,875 `overrides` edges unresolved on the whole cpp population (CPP-RESIDUAL ).
|
|
2834
|
+
// This is commit a29357562's landed fix ("the leaf spelling … WITH the owner carried
|
|
2835
|
+
// in metadata", measured there as 86 edges: 86 unresolved -> 0), which the
|
|
2836
|
+
// `0b597d2e4` take-main merge dropped and this iteration restores.
|
|
2837
|
+
target: pend.method,
|
|
2838
|
+
type: ASTEngine.RelationshipType.Overrides,
|
|
2839
|
+
confidence: hit.confidence,
|
|
2840
|
+
// the overriding method's own declaration site (the
|
|
2841
|
+
// pending record carries the line captured where the method was
|
|
2842
|
+
// walked — a post-loop resolution has no node to ask).
|
|
2843
|
+
line_number: pend.line,
|
|
2844
|
+
metadata: {
|
|
2845
|
+
isVirtualOverride: true,
|
|
2846
|
+
reasonCode: hit.reasonCode,
|
|
2847
|
+
// THE OWNER CARRIES THE DISTINCTION THE TARGET NO LONGER CAN. Two methods named
|
|
2848
|
+
// `run` in two classes are two declarations; the leaf names both, so the base
|
|
2849
|
+
// that was actually matched by the signature/keyword evidence rides HERE rather
|
|
2850
|
+
// than disappearing. This is the field the lane's own fixtures pin
|
|
2851
|
+
// (`meta.overriddenOwner`), and it is the machine-readable answer to "which
|
|
2852
|
+
// declaration did the resolver intend" when the leaf alone is ambiguous.
|
|
2853
|
+
overriddenOwner: hit.base,
|
|
2854
|
+
// task-22 (flag-gated): does the INDEX's inheritance-closed member view confirm this
|
|
2855
|
+
// base declares the method? Absent when there is no complete index.
|
|
2856
|
+
...indexOverridesFacts(hit.base, pend.method),
|
|
2857
|
+
// M14-HONESTY-CONTRACT : any edge under 1.0 carries a
|
|
2858
|
+
// machine-readable reason code, and `override`/`final` is
|
|
2859
|
+
// recorded so a reader can tell an asserted override from an
|
|
2860
|
+
// inferred one without re-parsing the source.
|
|
2861
|
+
...pend.keyword ? { overrideSpecifier: pend.keyword } : {}
|
|
2862
|
+
}
|
|
2863
|
+
});
|
|
2864
|
+
}
|
|
2865
|
+
}
|
|
2866
|
+
}
|
|
2867
|
+
{
|
|
2868
|
+
const seenImpl = /* @__PURE__ */ new Set();
|
|
2869
|
+
for (const pend of pendingImplements) {
|
|
2870
|
+
const k = `${pend.derived} ${pend.base}`;
|
|
2871
|
+
if (seenImpl.has(k)) continue;
|
|
2872
|
+
const pure = classPureVirtuals.get(pend.base);
|
|
2873
|
+
if (!pure || pure.size === 0) continue;
|
|
2874
|
+
seenImpl.add(k);
|
|
2875
|
+
relationships.push({
|
|
2876
|
+
source: pend.derived,
|
|
2877
|
+
target: pend.base,
|
|
2878
|
+
type: ASTEngine.RelationshipType.Implements,
|
|
2879
|
+
confidence: 1,
|
|
2880
|
+
line_number: pend.line,
|
|
2881
|
+
metadata: {
|
|
2882
|
+
// WHY this base is a contract, stated in machine-readable form so a
|
|
2883
|
+
// consumer can re-derive it: the pure virtual members it declares.
|
|
2884
|
+
reasonCode: "pure-virtual-base",
|
|
2885
|
+
pureVirtuals: [...pure],
|
|
2886
|
+
pureVirtualCount: pure.size,
|
|
2887
|
+
...indexBaseFacts(pend.base)
|
|
2888
|
+
}
|
|
2889
|
+
});
|
|
2890
|
+
}
|
|
2891
|
+
}
|
|
2892
|
+
{
|
|
2893
|
+
for (const pend of pendingExports) {
|
|
2894
|
+
const target = pend.owner ? `${pend.owner}.${pend.name}` : pend.name;
|
|
2895
|
+
if (exportsSeen.has(target)) continue;
|
|
2896
|
+
exportsSeen.add(target);
|
|
2897
|
+
relationships.push({
|
|
2898
|
+
source: "global",
|
|
2899
|
+
target,
|
|
2900
|
+
type: ASTEngine.RelationshipType.Exports,
|
|
2901
|
+
confidence: 1,
|
|
2902
|
+
// the exported NAME's line, which is the declaration's
|
|
2903
|
+
// `nameLine`, not the attribute's — `__declspec` precedes the type.
|
|
2904
|
+
line_number: pend.line,
|
|
2905
|
+
metadata: {
|
|
2906
|
+
// WHICH spelling declared the export. Recorded because the two
|
|
2907
|
+
// mechanisms have different reach — `dllexport` is MSVC and
|
|
2908
|
+
// `visibility("default")` is GCC/Clang ELF — and a reader triaging
|
|
2909
|
+
// a symbol's visibility needs to know which one the author used.
|
|
2910
|
+
exportSpelling: pend.spelling,
|
|
2911
|
+
relationshipType: "export_attribute"
|
|
2912
|
+
}
|
|
2913
|
+
});
|
|
2914
|
+
}
|
|
2915
|
+
}
|
|
2916
|
+
const containsSeen = /* @__PURE__ */ new Set();
|
|
2917
|
+
for (const s of symbols) {
|
|
2918
|
+
const _owner = s.ownerName || s.scope;
|
|
2919
|
+
if (!_owner) continue;
|
|
2920
|
+
const relKey = `${_owner}\0${s.name}`;
|
|
2921
|
+
if (containsSeen.has(relKey)) continue;
|
|
2922
|
+
containsSeen.add(relKey);
|
|
2923
|
+
relationships.push({
|
|
2924
|
+
source: _owner,
|
|
2925
|
+
target: s.name,
|
|
2926
|
+
type: ASTEngine.RelationshipType.Contains,
|
|
2927
|
+
confidence: 1,
|
|
2928
|
+
// + #921 (M60): a contains edge takes the CHILD's line,
|
|
2929
|
+
// and "the child's line" means the line that NAMES it — the rule every
|
|
2930
|
+
// other relationship type already follows (`nameLine`) and the rule the
|
|
2931
|
+
// Tier A oracle checks. The symbol's RANGE still starts at its whole
|
|
2932
|
+
// declaration node (search, edit anchors and the M73 line base read
|
|
2933
|
+
// that), so the two are no longer forced to agree: a wrapped return
|
|
2934
|
+
// type used to put the edge on the declaration's first line, where the
|
|
2935
|
+
// name is not written (`void NORETURN` then `CheckFailed(…)`).
|
|
2936
|
+
line_number: s.metadata?.nameLine ?? s.range?.startLine,
|
|
2937
|
+
metadata: {}
|
|
2938
|
+
});
|
|
2939
|
+
}
|
|
2940
|
+
for (const sym of symbols) {
|
|
2941
|
+
if (Array.isArray(sym.qualified_calls) && sym.qualified_calls.length > 1) {
|
|
2942
|
+
sym.qualified_calls = [...new Set(sym.qualified_calls)];
|
|
2943
|
+
}
|
|
2944
|
+
}
|
|
2945
|
+
this.classifyEvidenceTier(relationships);
|
|
2946
|
+
for (const sym of symbols) {
|
|
2947
|
+
const owner = String(sym?.ownerName ?? "").trim();
|
|
2948
|
+
if (owner === "" || owner === "global") continue;
|
|
2949
|
+
const name = String(sym?.name ?? "").trim();
|
|
2950
|
+
if (name === "") continue;
|
|
2951
|
+
const existing = sym?.qualifiedName;
|
|
2952
|
+
if (typeof existing === "string" && existing !== "") continue;
|
|
2953
|
+
sym.qualifiedName = `${owner}::${name}`;
|
|
2954
|
+
}
|
|
2955
|
+
return {
|
|
2956
|
+
symbols,
|
|
2957
|
+
relationships,
|
|
2958
|
+
imports,
|
|
2959
|
+
exports,
|
|
2960
|
+
errors,
|
|
2961
|
+
metadata: {
|
|
2962
|
+
linesOfCode: content.split("\n").length,
|
|
2963
|
+
cyclomaticComplexity: 1 + fileDecisions,
|
|
2964
|
+
complexity: 1 + fileDecisions,
|
|
2965
|
+
patterns: (() => {
|
|
2966
|
+
const arr = [];
|
|
2967
|
+
for (const p in patterns) arr.push(p);
|
|
2968
|
+
return arr;
|
|
2969
|
+
})(),
|
|
2970
|
+
detectedFrameworks: (() => {
|
|
2971
|
+
const arr = [];
|
|
2972
|
+
for (const f in frameworks) arr.push(f);
|
|
2973
|
+
return arr;
|
|
2974
|
+
})(),
|
|
2975
|
+
memorySafetyScore
|
|
2976
|
+
}
|
|
2977
|
+
};
|
|
2978
|
+
}
|
|
2979
|
+
// CP2 (cpp review): test/macro function shapes — `TEST(suite, name)
|
|
2980
|
+
// {...}`, `BOOST_AUTO_TEST_CASE(x) {...}` parse as function_definitions
|
|
2981
|
+
// whose declarator params are BARE identifiers (macro paste-tokens, no
|
|
2982
|
+
// types). Minting a `TEST` Function symbol per site (11 in one file) and
|
|
2983
|
+
// resolving reads against it fabricates API + edges for a preprocessor
|
|
2984
|
+
// fiction (C-D2: no macro expansion). Detection: ALL-CAPS declarator
|
|
2985
|
+
// name + a non-empty parameter list in which NO parameter carries a type
|
|
2986
|
+
// node. Real functions always type their params (`void f()`/`f(void)`
|
|
2987
|
+
// have empty/void lists and are kept); ALL-CAPS real functions with
|
|
2988
|
+
// typed params (`WINAPI Foo(int)`) are kept.
|
|
2989
|
+
// #919: the heading TEXT of a `function_definition`-spelled macro body — from
|
|
2990
|
+
// the node's start to the end of its parameter list, i.e. exactly the text the
|
|
2991
|
+
// source writes before the block (`TEST(my_case, keeps_its_locals)`). Returns
|
|
2992
|
+
// null when the shape has no body (a decorator fragment such as gmock's
|
|
2993
|
+
// `GTEST_LOCK_EXCLUDED_(g_gmock_mutex) = 0;`), because then there is no
|
|
2994
|
+
// heading/body pair at all.
|
|
2995
|
+
macroHeadingText(node, content, funcName) {
|
|
2996
|
+
if (!node.childForFieldName("body")) return null;
|
|
2997
|
+
const decl = this.findChild(node, "function_declarator");
|
|
2998
|
+
if (!decl) return null;
|
|
2999
|
+
let nameNode = null;
|
|
3000
|
+
{
|
|
3001
|
+
let cur = decl;
|
|
3002
|
+
while (cur) {
|
|
3003
|
+
if ((cur.type === "identifier" || cur.type === "field_identifier") && cur.text === funcName) {
|
|
3004
|
+
nameNode = cur;
|
|
3005
|
+
break;
|
|
3006
|
+
}
|
|
3007
|
+
let next = null;
|
|
3008
|
+
for (let k = 0; k < cur.namedChildCount; k++) {
|
|
3009
|
+
const c = cur.namedChild(k);
|
|
3010
|
+
if (c && (c.type.endsWith("declarator") || c.type === "identifier" || c.type === "field_identifier")) {
|
|
3011
|
+
next = c;
|
|
3012
|
+
break;
|
|
3013
|
+
}
|
|
3014
|
+
}
|
|
3015
|
+
cur = next;
|
|
3016
|
+
}
|
|
3017
|
+
}
|
|
3018
|
+
if (!nameNode) return null;
|
|
3019
|
+
return macroInvocationText(content, nameNode.startIndex);
|
|
3020
|
+
}
|
|
3021
|
+
// #919: is this node inside a function BODY? Two shapes qualify — a
|
|
3022
|
+
// `function_definition`, and the body of a macro-declared heading (which has
|
|
3023
|
+
// no `function_definition` at all: see `macroDeclaredBody`). A class/struct/
|
|
3024
|
+
// union boundary ends the search, exactly as the inline walk did before this
|
|
3025
|
+
// was factored out: a declaration inside a nested class belongs to that class,
|
|
3026
|
+
// not to the enclosing function whose locals we are collecting.
|
|
3027
|
+
insideFunctionBody(node) {
|
|
3028
|
+
let cur = node;
|
|
3029
|
+
while (cur) {
|
|
3030
|
+
if (cur.type === "function_definition") return true;
|
|
3031
|
+
if (cur.type === "class_specifier" || cur.type === "struct_specifier" || cur.type === "union_specifier") return false;
|
|
3032
|
+
if (macroDeclaredBody(cur)) return true;
|
|
3033
|
+
cur = cur.parent;
|
|
3034
|
+
}
|
|
3035
|
+
return false;
|
|
3036
|
+
}
|
|
3037
|
+
/**
|
|
3038
|
+
* Is this `function_definition` actually a MACRO INVOCATION?
|
|
3039
|
+
*
|
|
3040
|
+
* Two independent signals, and the second is the one that catches the annotation
|
|
3041
|
+
* class. The first is the historical one: an ALL-CAPS name whose parameters are
|
|
3042
|
+
* typeless (`TEST(a, b) { … }`).
|
|
3043
|
+
*
|
|
3044
|
+
* D8: a THREAD-SAFETY ANNOTATION is recovered as a whole extra function
|
|
3045
|
+
* definition. Given
|
|
3046
|
+
*
|
|
3047
|
+
* virtual void SetReaction(int obj, long r) GTEST_LOCK_EXCLUDED_(mu) {}
|
|
3048
|
+
*
|
|
3049
|
+
* the grammar emits the real definition AND a second, phantom one whose entire
|
|
3050
|
+
* text is `GTEST_LOCK_EXCLUDED_(mu) {}` — the annotation, parsed as a function
|
|
3051
|
+
* named after the macro. The analyzer published that phantom as a real method,
|
|
3052
|
+
* so `GTEST_LOCK_EXCLUDED_` and `GTEST_EXCLUSIVE_LOCK_REQUIRED_(m)` became
|
|
3053
|
+
* members of the class. (In the second one the published name even carried the
|
|
3054
|
+
* argument list, because the declarator is an identifier followed by a
|
|
3055
|
+
* parenthesised argument the grammar reads as a function-pointer declarator.)
|
|
3056
|
+
*
|
|
3057
|
+
* Why the existing parameter test could not see it: the argument of an
|
|
3058
|
+
* annotation is an EXPRESSION — a mutex variable, a member name — and
|
|
3059
|
+
* `internal::g_mutex` is spelled exactly like a parameter that has a type, so
|
|
3060
|
+
* `paramHasType` said yes and the function-shape test bailed. Testing the
|
|
3061
|
+
* parameter was always a proxy for "is this a macro invocation", and the
|
|
3062
|
+
* RETURN TYPE is the real thing: **C++ has no function definition without one.**
|
|
3063
|
+
* A constructor or destructor has no return type either, but its declarator
|
|
3064
|
+
* names the class, and a class name spelled in all caps is not a shape this
|
|
3065
|
+
* codebase has to survive. Measured on this lane's corpora: the annotation
|
|
3066
|
+
* phantoms are uniformly `type=(NONE)` while every real definition carries
|
|
3067
|
+
* `primitive_type`, `sized_type_specifier`, `qualified_identifier` or a
|
|
3068
|
+
* `macro_prefixed_type_specifier`.
|
|
3069
|
+
*/
|
|
3070
|
+
isMacroFunctionShape(node, funcName) {
|
|
3071
|
+
const decl = this.findChild(node, "function_declarator");
|
|
3072
|
+
if (!decl) return false;
|
|
3073
|
+
const bare = this.bareDeclaratorName(decl) ?? funcName;
|
|
3074
|
+
if (!this.isAllCapsName(bare)) return false;
|
|
3075
|
+
if (!node.childForFieldName?.("type")) {
|
|
3076
|
+
const body = this.findChild(node, "compound_statement");
|
|
3077
|
+
if (this.findChild(decl, "parameters") && body && body.namedChildCount === 0) return true;
|
|
3078
|
+
}
|
|
3079
|
+
const params = decl.childForFieldName("parameters");
|
|
3080
|
+
if (!params || params.namedChildCount === 0) return false;
|
|
3081
|
+
for (let i = 0; i < params.namedChildCount; i++) {
|
|
3082
|
+
const p = params.namedChild(i);
|
|
3083
|
+
if (!p) continue;
|
|
3084
|
+
if (p.type === "comment") continue;
|
|
3085
|
+
if (this.paramHasType(p)) return false;
|
|
3086
|
+
}
|
|
3087
|
+
return true;
|
|
3088
|
+
}
|
|
3089
|
+
/**
|
|
3090
|
+
* The BARE name a declarator chain declares: the first `identifier` /
|
|
3091
|
+
* `field_identifier` down the chain, with no argument list and no qualifier.
|
|
3092
|
+
* For `GTEST_LOCK_EXCLUDED_(mu)` that is `GTEST_LOCK_EXCLUDED_`; for
|
|
3093
|
+
* `namespace::detail::Widget` it is `Widget`. Returns null when the chain has
|
|
3094
|
+
* no spelled name, so a caller can fall back to the name it already has.
|
|
3095
|
+
*/
|
|
3096
|
+
/**
|
|
3097
|
+
* D8: is this no-return-type `function_definition` a TRAILING ANNOTATION — one
|
|
3098
|
+
* that decorates the declaration immediately before it and declares nothing?
|
|
3099
|
+
*
|
|
3100
|
+
* The shape is fixed by the grammar. Given
|
|
3101
|
+
*
|
|
3102
|
+
* void SetReaction(int obj, long r) GTEST_LOCK_EXCLUDED_(mu) { …body… }
|
|
3103
|
+
*
|
|
3104
|
+
* the grammar ends the `declaration` at the `)` and starts a SECOND
|
|
3105
|
+
* `function_definition` whose declarator is the annotation and whose BODY is the
|
|
3106
|
+
* real function's body. So the node is not a phantom with an empty body — it
|
|
3107
|
+
* carries the statements — and publishing it as a method both invents a symbol
|
|
3108
|
+
* and misattributes every read in that body to the macro.
|
|
3109
|
+
*
|
|
3110
|
+
* The discriminator is structural and it is the only one that held up against
|
|
3111
|
+
* the corpus. Everything else tried was confounded by real data:
|
|
3112
|
+
*
|
|
3113
|
+
* * the annotation's ARGUMENT looks like a parameter type
|
|
3114
|
+
* (`internal::g_gmock_mutex`), so the historical "params are typeless" test
|
|
3115
|
+
* cannot see it
|
|
3116
|
+
* * the body's being empty does not hold — this node HAS the body
|
|
3117
|
+
* * the character before the macro in the SOURCE does not hold: it is `)` for
|
|
3118
|
+
* some sites and `t` (of a trailing `const`) for others, and a `TEST` macro
|
|
3119
|
+
* preceded by a comment ending in `)` reads as `)` too
|
|
3120
|
+
* * `prevNamedSibling` is `null` throughout this grammar
|
|
3121
|
+
*
|
|
3122
|
+
* What does hold: the annotation's immediately preceding NAMED SIBLING is the
|
|
3123
|
+
* `declaration`/`field_declaration` of the function it decorates, and that
|
|
3124
|
+
* declaration carries a `function_declarator` — the grammar cut it at the `)`.
|
|
3125
|
+
* A `TEST(Suite, Name) { …body… }` heading has no such predecessor: it is the
|
|
3126
|
+
* first thing in its statement, so its previous named sibling is a comment, a
|
|
3127
|
+
* brace, or another `function_definition`. The `function_declarator` requirement
|
|
3128
|
+
* is what keeps `int x;` immediately before a `TEST` from being read as a
|
|
3129
|
+
* continuation.
|
|
3130
|
+
*
|
|
3131
|
+
* Node-only on purpose: it must be decidable WITHOUT `getFunctionDetails`, which
|
|
3132
|
+
* is null for exactly the nodes this rule is about.
|
|
3133
|
+
*/
|
|
3134
|
+
/**
|
|
3135
|
+
* Is this `function_definition`'s return type a class DEFINITION?
|
|
3136
|
+
*
|
|
3137
|
+
* C++ has no such thing: a function cannot return a class *definition*, only a
|
|
3138
|
+
* class type (by name, or an elaborated `class X` with no body). So a
|
|
3139
|
+
* `function_definition` whose `type` field resolves to a `class_specifier` /
|
|
3140
|
+
* `struct_specifier` / `union_specifier` that CARRIES A BODY is the recovery's
|
|
3141
|
+
* invention.
|
|
3142
|
+
*
|
|
3143
|
+
* The unwrap matters: fork patch 2's `macro_prefixed_type_specifier` puts the
|
|
3144
|
+
* class one level down, so the `type` field is that node and the specifier is its
|
|
3145
|
+
* child. Requiring a body is what separates this from the legal
|
|
3146
|
+
* `class X f() { ... }` (an elaborated return type), which has no body and must
|
|
3147
|
+
* keep publishing its function.
|
|
3148
|
+
*/
|
|
3149
|
+
returnTypeIsClassDefinition(node) {
|
|
3150
|
+
const t = node.childForFieldName?.("type");
|
|
3151
|
+
if (!t) return false;
|
|
3152
|
+
const spec = t.type === "macro_prefixed_type_specifier" ? (t.namedChildren ?? []).find(
|
|
3153
|
+
(c) => c.type === "class_specifier" || c.type === "struct_specifier" || c.type === "union_specifier"
|
|
3154
|
+
) : t;
|
|
3155
|
+
if (!spec) return false;
|
|
3156
|
+
if (spec.type !== "class_specifier" && spec.type !== "struct_specifier" && spec.type !== "union_specifier") {
|
|
3157
|
+
return false;
|
|
3158
|
+
}
|
|
3159
|
+
return (spec.namedChildren ?? []).some((c) => c.type === "field_declaration_list");
|
|
3160
|
+
}
|
|
3161
|
+
isTrailingAnnotationMacro(node, content) {
|
|
3162
|
+
if (node.childForFieldName?.("type")) return false;
|
|
3163
|
+
const body = this.findChild(node, "compound_statement");
|
|
3164
|
+
if (!body) return false;
|
|
3165
|
+
const decl = this.findChild(node, "function_declarator");
|
|
3166
|
+
if (!decl || !this.findChild(decl, "parameter_list")) return false;
|
|
3167
|
+
const bare = this.bareDeclaratorName(decl);
|
|
3168
|
+
if (bare === null || !this.isAllCapsName(bare)) return false;
|
|
3169
|
+
return this.continuesADeclaration(node.startIndex, content);
|
|
3170
|
+
}
|
|
3171
|
+
/**
|
|
3172
|
+
* D8's discriminator, and the only formulation that held up against the corpus:
|
|
3173
|
+
* does this macro invocation CONTINUE the declaration in front of it?
|
|
3174
|
+
*
|
|
3175
|
+
* Everything structural was confounded by real data, and each was tried and
|
|
3176
|
+
* measured before being dropped:
|
|
3177
|
+
*
|
|
3178
|
+
* * the annotation's ARGUMENT looks like a parameter type
|
|
3179
|
+
* (`internal::g_gmock_mutex`), so the historical "params are typeless" test
|
|
3180
|
+
* cannot see it
|
|
3181
|
+
* * the body's being empty does not hold — the node CARRIES the real body
|
|
3182
|
+
* * the previous named sibling is unreliable: `prevNamedSibling` is `null`
|
|
3183
|
+
* throughout this grammar, and finding the predecessor by index in the
|
|
3184
|
+
* parent's child list yields `declaration` for a real annotation AND for a
|
|
3185
|
+
* `TEST` that happens to follow a function declaration — that version
|
|
3186
|
+
* deleted every gtest heading, which is the largest single loss available
|
|
3187
|
+
* * `TEST` is itself spelled in all caps, so the macro-name test cannot
|
|
3188
|
+
* separate the two families
|
|
3189
|
+
*
|
|
3190
|
+
* What separates them is where the invocation SITS. An annotation is trailing:
|
|
3191
|
+
* it follows the parameter list (and possibly a trailing `const`/`&`/`noexcept`)
|
|
3192
|
+
* of the declaration it decorates. A heading is a statement in its own right.
|
|
3193
|
+
* So scan back over whitespace and comments; if the previous significant
|
|
3194
|
+
* character could END a statement, this macro OPENS one and is not an
|
|
3195
|
+
* annotation.
|
|
3196
|
+
*
|
|
3197
|
+
* Comments must be skipped, and that is not a refinement — the first attempt
|
|
3198
|
+
* did not skip them and read a `TEST` preceded by a comment ending in `)` as an
|
|
3199
|
+
* annotation, which is how the first version of this rule deleted the whole
|
|
3200
|
+
* gtest suite's worth of headings.
|
|
3201
|
+
*/
|
|
3202
|
+
continuesADeclaration(startIndex, content) {
|
|
3203
|
+
let j = startIndex - 1;
|
|
3204
|
+
while (j >= 0) {
|
|
3205
|
+
const c2 = content[j];
|
|
3206
|
+
if (c2 === " " || c2 === " " || c2 === "\r" || c2 === "\n") {
|
|
3207
|
+
j--;
|
|
3208
|
+
continue;
|
|
3209
|
+
}
|
|
3210
|
+
break;
|
|
3211
|
+
}
|
|
3212
|
+
if (j < 0) return false;
|
|
3213
|
+
const tail = content.slice(Math.max(0, j - 40), startIndex);
|
|
3214
|
+
if (tail.indexOf("//") !== -1 || tail.indexOf("/*") !== -1) return false;
|
|
3215
|
+
const c = content[j];
|
|
3216
|
+
if (c === ")" || c === "&" || c === ">" || c === "*") return true;
|
|
3217
|
+
const upTo = content.slice(Math.max(0, j - 12), j + 1).toLowerCase();
|
|
3218
|
+
let letters = "";
|
|
3219
|
+
for (let k = 0; k < upTo.length; k++) {
|
|
3220
|
+
const ch = upTo[k];
|
|
3221
|
+
if (ch >= "a" && ch <= "z") letters += ch;
|
|
3222
|
+
}
|
|
3223
|
+
for (const kw of ["const", "noexcept", "override", "final", "mutable", "volatile"]) {
|
|
3224
|
+
if (letters.endsWith(kw)) return true;
|
|
3225
|
+
}
|
|
3226
|
+
return false;
|
|
3227
|
+
}
|
|
3228
|
+
bareDeclaratorName(decl) {
|
|
3229
|
+
let cur = decl;
|
|
3230
|
+
for (let i = 0; i < 8 && cur; i++) {
|
|
3231
|
+
if (cur.type === "identifier" || cur.type === "field_identifier") return cur.text;
|
|
3232
|
+
const next = cur.childForFieldName?.("declarator") ?? null;
|
|
3233
|
+
if (!next) {
|
|
3234
|
+
for (let k = 0; k < cur.namedChildCount; k++) {
|
|
3235
|
+
const c = cur.namedChild(k);
|
|
3236
|
+
if (c && (c.type === "identifier" || c.type === "field_identifier")) return c.text;
|
|
3237
|
+
}
|
|
3238
|
+
return null;
|
|
3239
|
+
}
|
|
3240
|
+
cur = next;
|
|
3241
|
+
}
|
|
3242
|
+
return null;
|
|
3243
|
+
}
|
|
3244
|
+
// #915: a bare top-level macro must not become a `variable`. Two consecutive
|
|
3245
|
+
// macros at file scope are recovered by the grammar as a `declaration` whose
|
|
3246
|
+
// "type" is itself a macro and whose terminator is MISSING
|
|
3247
|
+
// (`FMT_END_EXPORT` / `FMT_END_NAMESPACE` -> type_identifier + identifier +
|
|
3248
|
+
// MISSING `;`), and the declaration branch then names the symbol after the
|
|
3249
|
+
// second macro. Measured on fmt: 13 such phantoms in a 58-file draw, all bare
|
|
3250
|
+
// ALL-CAPS macro names (`variable FMT_END_NAMESPACE` x8, `GTEST_ATTRIBUTE_UNUSED_`
|
|
3251
|
+
// x2, `RUN_ALL_TESTS`, `GTEST_DEFAULT_DEATH_TEST_STYLE`, `FMT_BEGIN_EXPORT`),
|
|
3252
|
+
// and 0 on nlohmann-json. This is the BARE sibling of the call-shaped class
|
|
3253
|
+
// `isMacroFunctionShape` already suppresses.
|
|
3254
|
+
//
|
|
3255
|
+
// Three conditions, each doing real work — the first two alone would lose a
|
|
3256
|
+
// real symbol:
|
|
3257
|
+
// * the name is ALL-CAPS (`MAX_SIZE` stays a symbol, `MAX_SIZE;`)
|
|
3258
|
+
// * the node is ERROR-RECOVERED (a clean `MyType MAX_SIZE;` is kept,
|
|
3259
|
+
// because its terminator is real; only
|
|
3260
|
+
// already-broken code is affected)
|
|
3261
|
+
// * it carries NO hard type (`int MAX_SIZE` and
|
|
3262
|
+
// `MyType *MAX_SIZE` are kept — a lone
|
|
3263
|
+
// `type_identifier` is the grammar's
|
|
3264
|
+
// classification of macro soup, not a type,
|
|
3265
|
+
// the same reading `paramHasType` uses)
|
|
3266
|
+
isBareMacroDeclaration(node, name) {
|
|
3267
|
+
if (!this.isAllCapsName(name)) return false;
|
|
3268
|
+
let recovered = false;
|
|
3269
|
+
const walkRecovered = [node];
|
|
3270
|
+
while (walkRecovered.length > 0) {
|
|
3271
|
+
const m = walkRecovered.pop();
|
|
3272
|
+
if (m.isMissing || m.isError) {
|
|
3273
|
+
recovered = true;
|
|
3274
|
+
break;
|
|
3275
|
+
}
|
|
3276
|
+
for (let i = 0; i < m.childCount; i++) {
|
|
3277
|
+
const c = m.child(i);
|
|
3278
|
+
if (c) walkRecovered.push(c);
|
|
3279
|
+
}
|
|
3280
|
+
}
|
|
3281
|
+
if (!recovered) return false;
|
|
3282
|
+
const walkType = [node];
|
|
3283
|
+
while (walkType.length > 0) {
|
|
3284
|
+
const m = walkType.pop();
|
|
3285
|
+
if (m.type === "primitive_type" || m.type === "sized_type_specifier" || m.type === "template_type" || m.type === "qualified_identifier" || m.type === "dependent_type" || m.type === "decltype" || m.type === "struct_specifier" || m.type === "union_specifier" || m.type === "enum_specifier" || m.type === "class_specifier" || m.type === "placeholder_type_specifier" || m.type === "macro_prefixed_type_specifier") {
|
|
3286
|
+
return false;
|
|
3287
|
+
}
|
|
3288
|
+
for (let i = 0; i < m.childCount; i++) {
|
|
3289
|
+
const c = m.child(i);
|
|
3290
|
+
if (c) walkType.push(c);
|
|
3291
|
+
}
|
|
3292
|
+
}
|
|
3293
|
+
return true;
|
|
3294
|
+
}
|
|
3295
|
+
isAllCapsName(name) {
|
|
3296
|
+
let hasUpper = false;
|
|
3297
|
+
for (let i = 0; i < name.length; i++) {
|
|
3298
|
+
const c = name[i];
|
|
3299
|
+
if (c >= "a" && c <= "z") return false;
|
|
3300
|
+
if (c >= "A" && c <= "Z") hasUpper = true;
|
|
3301
|
+
}
|
|
3302
|
+
return hasUpper;
|
|
3303
|
+
}
|
|
3304
|
+
paramHasType(param) {
|
|
3305
|
+
let sawHardType = false;
|
|
3306
|
+
let sawTypeIdent = false;
|
|
3307
|
+
let sawNameIdent = false;
|
|
3308
|
+
const stack = [param];
|
|
3309
|
+
while (stack.length > 0) {
|
|
3310
|
+
const m = stack.pop();
|
|
3311
|
+
if (m.type === "primitive_type" || m.type === "sized_type_specifier" || m.type === "template_type" || m.type === "qualified_identifier" || m.type === "dependent_type" || m.type === "decltype" || m.type === "struct_specifier" || m.type === "union_specifier" || m.type === "enum_specifier") {
|
|
3312
|
+
sawHardType = true;
|
|
3313
|
+
break;
|
|
3314
|
+
}
|
|
3315
|
+
if (m.type === "type_identifier") sawTypeIdent = true;
|
|
3316
|
+
if (m.type === "identifier" || m.type === "field_identifier") sawNameIdent = true;
|
|
3317
|
+
for (let i = 0; i < m.namedChildCount; i++) {
|
|
3318
|
+
const c = m.namedChild(i);
|
|
3319
|
+
if (c) stack.push(c);
|
|
3320
|
+
}
|
|
3321
|
+
}
|
|
3322
|
+
return sawHardType || sawTypeIdent && sawNameIdent;
|
|
3323
|
+
}
|
|
3324
|
+
/**
|
|
3325
|
+
* M60.P1 `decorators`: a declaration's modifiers, as the SOURCE SPELLINGS the
|
|
3326
|
+
* node carries, in source order — so a consumer reads `["static", "constexpr"]`
|
|
3327
|
+
* instead of reversing four booleans back into words.
|
|
3328
|
+
*
|
|
3329
|
+
* This is the declared payload field, and the `metadata` flags
|
|
3330
|
+
* (`isVirtual`/`isConst`/`isConstexpr`/`isStatic`) were duplicating its content
|
|
3331
|
+
* without ever filling it: the M60 payload-seam matrix recorded
|
|
3332
|
+
* `UniversalSymbol.decorators` as a DEAD emit cell for cpp while this file's own
|
|
3333
|
+
* documentation claimed the row was supported.
|
|
3334
|
+
*
|
|
3335
|
+
* Shared by the function and the field/variable branch, because
|
|
3336
|
+
* `static constexpr int kMax = 10;` carries exactly as much modifier
|
|
3337
|
+
* information as `virtual int area() const = 0;` and a payload field that only
|
|
3338
|
+
* answered for one of them is a field a consumer cannot rely on.
|
|
3339
|
+
*
|
|
3340
|
+
* A destructor and an operator overload are deliberately ABSENT: `~File` and
|
|
3341
|
+
* `operator==` are part of the NAME, not modifiers applied to it, and listing
|
|
3342
|
+
* them here would put a name in a field whose meaning is "applied to this
|
|
3343
|
+
* declaration".
|
|
3344
|
+
*
|
|
3345
|
+
* The scan keys off the child's TEXT and EXCLUDES the declaration's own
|
|
3346
|
+
* payload fields (`type`, `declarator`, `default_value`, …), not off
|
|
3347
|
+
* `isNamed`. The grammar is inconsistent here and both forms occur in this
|
|
3348
|
+
* lane's own fixtures: `virtual int area() const = 0;` exposes `virtual` as an
|
|
3349
|
+
* UNNAMED token, while `static constexpr int kMax = 10;` exposes `static` and
|
|
3350
|
+
* `constexpr` as NAMED `storage_class_specifier` / `type_qualifier` wrappers.
|
|
3351
|
+
* A rule that only read unnamed tokens would find the modifier on the method
|
|
3352
|
+
* and miss it on the field, which is the worst of both — a payload field that
|
|
3353
|
+
* answers sometimes.
|
|
3354
|
+
*/
|
|
3355
|
+
declarationDecorators(node, declarator = null) {
|
|
3356
|
+
const out = [];
|
|
3357
|
+
const MODIFIERS = /* @__PURE__ */ new Set([
|
|
3358
|
+
"static",
|
|
3359
|
+
"constexpr",
|
|
3360
|
+
"const",
|
|
3361
|
+
"inline",
|
|
3362
|
+
"virtual",
|
|
3363
|
+
"explicit",
|
|
3364
|
+
"extern",
|
|
3365
|
+
"friend",
|
|
3366
|
+
"mutable",
|
|
3367
|
+
"thread_local",
|
|
3368
|
+
"volatile",
|
|
3369
|
+
"override",
|
|
3370
|
+
"final"
|
|
3371
|
+
]);
|
|
3372
|
+
const PAYLOAD_FIELDS = /* @__PURE__ */ new Set([
|
|
3373
|
+
"type",
|
|
3374
|
+
"declarator",
|
|
3375
|
+
"default_value",
|
|
3376
|
+
"value",
|
|
3377
|
+
"initializer",
|
|
3378
|
+
"parameters",
|
|
3379
|
+
"body",
|
|
3380
|
+
"name",
|
|
3381
|
+
"argument",
|
|
3382
|
+
"condition",
|
|
3383
|
+
"left",
|
|
3384
|
+
"right",
|
|
3385
|
+
"operator"
|
|
3386
|
+
]);
|
|
3387
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3388
|
+
const scan = (n) => {
|
|
3389
|
+
if (!n) return;
|
|
3390
|
+
for (let i = 0; i < n.childCount; i++) {
|
|
3391
|
+
const c = n.child(i);
|
|
3392
|
+
if (!c) continue;
|
|
3393
|
+
const field = n.fieldNameForChild ? n.fieldNameForChild(i) : null;
|
|
3394
|
+
if (field && PAYLOAD_FIELDS.has(field)) continue;
|
|
3395
|
+
if (MODIFIERS.has(c.text) && !seen.has(c.text)) {
|
|
3396
|
+
seen.add(c.text);
|
|
3397
|
+
out.push(c.text);
|
|
3398
|
+
}
|
|
3399
|
+
}
|
|
3400
|
+
};
|
|
3401
|
+
scan(node);
|
|
3402
|
+
scan(declarator);
|
|
3403
|
+
return out;
|
|
3404
|
+
}
|
|
3405
|
+
/**
|
|
3406
|
+
* The `function_declarator` of a declaration, descending one `init_declarator`.
|
|
3407
|
+
*
|
|
3408
|
+
* ONTOLOGY `overrides`, second-order defect. `virtual bool ReportContext(const
|
|
3409
|
+
* Context&) = 0;` — the PURE VIRTUAL, which is how C++ spells the interface a
|
|
3410
|
+
* derived class implements — does not put the declarator where the shallow scan
|
|
3411
|
+
* looks:
|
|
3412
|
+
*
|
|
3413
|
+
* declaration [declarator=init_declarator type=primitive_type]
|
|
3414
|
+
* virtual, primitive_type "bool"
|
|
3415
|
+
* init_declarator [declarator=function_declarator value=number_literal]
|
|
3416
|
+
* function_declarator [declarator=identifier parameters=parameter_list]
|
|
3417
|
+
*
|
|
3418
|
+
* So `findChild(node, "function_declarator")` returned null, the signature
|
|
3419
|
+
* normaliser registered the base method with an EMPTY parameter list, and no
|
|
3420
|
+
* derived signature could ever equal it. `Finalize()` resolved only because it
|
|
3421
|
+
* takes no parameters and `""` matched `""` — which is why the pre-fix count on
|
|
3422
|
+
* `reporter.h` was 2 of 4 and both happened to be the zero-argument ones.
|
|
3423
|
+
*
|
|
3424
|
+
* Scoped to ONE level and ONE wrapper on purpose. A general "descend until you
|
|
3425
|
+
* find a declarator" walk would also reach through `pointer_declarator` and
|
|
3426
|
+
* `reference_declarator`, where the pointer/reference is part of the C++ type
|
|
3427
|
+
* and dropping the wrapper would make `void f(Widget&)` and `void f(Widget)`
|
|
3428
|
+
* hash alike — the exact defect D8 removed from the published `parameters`
|
|
3429
|
+
* field. `init_declarator` carries no type, so descending through it loses
|
|
3430
|
+
* nothing.
|
|
3431
|
+
*/
|
|
3432
|
+
functionDeclaratorOf(node) {
|
|
3433
|
+
if (!node) return null;
|
|
3434
|
+
const direct = this.findChild(node, "function_declarator");
|
|
3435
|
+
if (direct) return direct;
|
|
3436
|
+
const init = this.findChild(node, "init_declarator");
|
|
3437
|
+
return init ? this.findChild(init, "function_declarator") : null;
|
|
3438
|
+
}
|
|
3439
|
+
methodComplexityCpp(node) {
|
|
3440
|
+
const decisionTypes = /* @__PURE__ */ new Set([
|
|
3441
|
+
"if_statement",
|
|
3442
|
+
"for_statement",
|
|
3443
|
+
"while_statement",
|
|
3444
|
+
"do_statement",
|
|
3445
|
+
"case_statement",
|
|
3446
|
+
"catch_clause",
|
|
3447
|
+
"conditional_expression"
|
|
3448
|
+
]);
|
|
3449
|
+
let c = 1;
|
|
3450
|
+
const st = [node];
|
|
3451
|
+
while (st.length) {
|
|
3452
|
+
const n = st.pop();
|
|
3453
|
+
if (decisionTypes.has(n.type)) c++;
|
|
3454
|
+
else if (n.type === "binary_expression") {
|
|
3455
|
+
const op = n.childForFieldName("operator")?.text;
|
|
3456
|
+
if (op === "&&" || op === "||") c++;
|
|
3457
|
+
}
|
|
3458
|
+
for (let i = 0; i < n.namedChildCount; i++) {
|
|
3459
|
+
const ch = n.namedChild(i);
|
|
3460
|
+
if (ch) st.push(ch);
|
|
3461
|
+
}
|
|
3462
|
+
}
|
|
3463
|
+
return c;
|
|
3464
|
+
}
|
|
3465
|
+
/**
|
|
3466
|
+
* #419-D - evidence-tier classification for EVERY emitted relationship
|
|
3467
|
+
* (same rules as the JVM/web family).
|
|
3468
|
+
*/
|
|
3469
|
+
classifyEvidenceTier(relationships) {
|
|
3470
|
+
for (const r of relationships) {
|
|
3471
|
+
const md = r.metadata ?? (r.metadata = {});
|
|
3472
|
+
if (md.evidenceClass) continue;
|
|
3473
|
+
const t = String(r.type ?? "");
|
|
3474
|
+
const conf = typeof r.confidence === "number" ? r.confidence : 1;
|
|
3475
|
+
if (t === "contains" || t === "exports") {
|
|
3476
|
+
md.evidenceClass = "structural";
|
|
3477
|
+
continue;
|
|
3478
|
+
}
|
|
3479
|
+
if (t === "imports") {
|
|
3480
|
+
md.evidenceClass = "import_based";
|
|
3481
|
+
continue;
|
|
3482
|
+
}
|
|
3483
|
+
if (t === "references") {
|
|
3484
|
+
md.evidenceClass = md.isImport ? "import_based" : "reference_qualified";
|
|
3485
|
+
continue;
|
|
3486
|
+
}
|
|
3487
|
+
if (t === "imports") {
|
|
3488
|
+
md.evidenceClass = md.isImport ? "import_based" : "usage_contextual";
|
|
3489
|
+
continue;
|
|
3490
|
+
}
|
|
3491
|
+
if (t === "extends" || t === "implements" || t === "overrides") {
|
|
3492
|
+
md.evidenceClass = conf >= 1 ? "structural" : conf >= 0.85 ? "usage_contextual" : "inferred";
|
|
3493
|
+
continue;
|
|
3494
|
+
}
|
|
3495
|
+
if (t === "instantiates") {
|
|
3496
|
+
md.evidenceClass = "structural";
|
|
3497
|
+
continue;
|
|
3498
|
+
}
|
|
3499
|
+
md.evidenceClass = "usage_contextual";
|
|
3500
|
+
}
|
|
3501
|
+
}
|
|
3502
|
+
/**
|
|
3503
|
+
* The EXPORT MARKER macro of a declaration-specifier, or `undefined`.
|
|
3504
|
+
*
|
|
3505
|
+
* The other half of `specifierTypeText`, and the reader for ONTOLOGY `exports`.
|
|
3506
|
+
* Both walk the same node and neither imports the other, because they answer
|
|
3507
|
+
* different questions — one asks what the type IS, the other asks what the
|
|
3508
|
+
* declaration is DECORATED with — and a node can answer both at once only if
|
|
3509
|
+
* both are asked. `cppExportMarker` decides whether the macro is an export
|
|
3510
|
+
* marker; see its comment for the measured population that forced the rule.
|
|
3511
|
+
*/
|
|
3512
|
+
specifierExportMacro(t) {
|
|
3513
|
+
if (!t || t.type !== "macro_prefixed_type_specifier") return void 0;
|
|
3514
|
+
for (let i = 0; i < t.namedChildCount; i++) {
|
|
3515
|
+
const c = t.namedChild(i);
|
|
3516
|
+
if (c && c.type === "macro_specifier") return c.text.trim();
|
|
3517
|
+
}
|
|
3518
|
+
return void 0;
|
|
3519
|
+
}
|
|
3520
|
+
/**
|
|
3521
|
+
* the line of the node that NAMES a relationship, 1-based, or
|
|
3522
|
+
* `undefined` when the site is not attributable — never a fabricated 0 and
|
|
3523
|
+
* never an enclosing declaration's line. The C lane carries the identical
|
|
3524
|
+
* helper; the contract is "call-site line or NULL" ().
|
|
3525
|
+
*/
|
|
3526
|
+
nodeLine(node) {
|
|
3527
|
+
if (!node || !node.startPosition || typeof node.startPosition.row !== "number") return void 0;
|
|
3528
|
+
return node.startPosition.row + 1;
|
|
3529
|
+
}
|
|
3530
|
+
/**
|
|
3531
|
+
* a type expression's start is not the line of the names inside
|
|
3532
|
+
* it. A specifier starts at its KEYWORD, and a wrapped declaration puts the
|
|
3533
|
+
* name on the following line:
|
|
3534
|
+
*
|
|
3535
|
+
* struct <- the specifier's start
|
|
3536
|
+
* Bigint { <- where the name is written
|
|
3537
|
+
*
|
|
3538
|
+
* The grammar's `name` field is the site when it exists; otherwise the node is.
|
|
3539
|
+
* (Mirror of the C lane's `nameLine`.)
|
|
3540
|
+
*/
|
|
3541
|
+
nameLine(node) {
|
|
3542
|
+
if (!node) return void 0;
|
|
3543
|
+
const nameField = typeof node.childForFieldName === "function" ? node.childForFieldName("name") : null;
|
|
3544
|
+
return this.nodeLine(nameField ?? node);
|
|
3545
|
+
}
|
|
3546
|
+
nodeToRange(node) {
|
|
3547
|
+
return {
|
|
3548
|
+
startLine: node.startPosition.row + 1,
|
|
3549
|
+
startColumn: node.startPosition.column + 1,
|
|
3550
|
+
endLine: node.endPosition.row + 1,
|
|
3551
|
+
endColumn: node.endPosition.column + 1,
|
|
3552
|
+
startIndex: node.startIndex,
|
|
3553
|
+
endIndex: node.endIndex
|
|
3554
|
+
};
|
|
3555
|
+
}
|
|
3556
|
+
findChild(node, type, text) {
|
|
3557
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
3558
|
+
const child = node.namedChild(i);
|
|
3559
|
+
if (child && child.type === type) {
|
|
3560
|
+
if (text === void 0 || child.text === text) return child;
|
|
3561
|
+
}
|
|
3562
|
+
}
|
|
3563
|
+
return null;
|
|
3564
|
+
}
|
|
3565
|
+
findAllChildren(node, type) {
|
|
3566
|
+
const res = [];
|
|
3567
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
3568
|
+
const child = node.namedChild(i);
|
|
3569
|
+
if (child && child.type === type) res.push(child);
|
|
3570
|
+
}
|
|
3571
|
+
return res;
|
|
3572
|
+
}
|
|
3573
|
+
childText(node, type) {
|
|
3574
|
+
const child = this.findChild(node, type);
|
|
3575
|
+
return child ? child.text : null;
|
|
3576
|
+
}
|
|
3577
|
+
/**
|
|
3578
|
+
* The `override` / `final` `virtual_specifier` on a function declaration, or
|
|
3579
|
+
* null. Read from the DECLARATOR, because that is where the pinned grammar puts
|
|
3580
|
+
* it: measured on the fork,
|
|
3581
|
+
*
|
|
3582
|
+
* field_declaration[declarator="go() override"]
|
|
3583
|
+
* primitive_type
|
|
3584
|
+
* function_declarator[declarator="go"]
|
|
3585
|
+
* field_identifier
|
|
3586
|
+
* parameter_list
|
|
3587
|
+
* virtual_specifier "override" <- a CHILD of the declarator
|
|
3588
|
+
*
|
|
3589
|
+
* so a declaration-level scan never fires — which is why the `decorators`
|
|
3590
|
+
* payload needed its own second lookup, and why override RESOLUTION ignored the
|
|
3591
|
+
* strongest evidence C++ offers until this round.
|
|
3592
|
+
*/
|
|
3593
|
+
functionOverrideSpecifier(declaration) {
|
|
3594
|
+
if (!declaration) return null;
|
|
3595
|
+
const fd = declaration.type === "function_declarator" ? declaration : this.findChild(declaration, "function_declarator");
|
|
3596
|
+
if (!fd) return null;
|
|
3597
|
+
for (let i = 0; i < fd.childCount; i++) {
|
|
3598
|
+
const c = fd.child(i);
|
|
3599
|
+
if (c?.type !== "virtual_specifier") continue;
|
|
3600
|
+
const txt = String(c.text ?? "").trim();
|
|
3601
|
+
if (txt === "override" || txt === "final") return txt;
|
|
3602
|
+
}
|
|
3603
|
+
return null;
|
|
3604
|
+
}
|
|
3605
|
+
/**
|
|
3606
|
+
* The EXPORT attribute a declaration carries, or null. Which spelling exists is
|
|
3607
|
+
* a measurement, not a preference — see `docs/analyzers/cpp.md`
|
|
3608
|
+
* "the exports construct, measured three ways".
|
|
3609
|
+
*
|
|
3610
|
+
* Two are readable on the pinned grammar and one of them is not:
|
|
3611
|
+
*
|
|
3612
|
+
* 1. `__declspec(dllexport)` — MSVC. The grammar keeps the paren group as a
|
|
3613
|
+
* `ms_declspec_modifier` node whose named children are the attribute names
|
|
3614
|
+
* (`ms_declspec_modifier [__declspec ( identifier)]`), so the spelling is a
|
|
3615
|
+
* node, not text.
|
|
3616
|
+
* 2. `__attribute__((visibility("default")))` — GCC/Clang ELF. An
|
|
3617
|
+
* `attribute_specifier` holding an `argument_list` whose
|
|
3618
|
+
* `call_expression` names `visibility` with a `"default"` string argument.
|
|
3619
|
+
* 3. `#define FOO __declspec(dllexport)` + `FOO void f();` — how both of the
|
|
3620
|
+
* above are ACTUALLY used. Measured over the corpus: 11 macro definitions
|
|
3621
|
+
* name `dllexport` and 0 declarations spell it inline except through one.
|
|
3622
|
+
* Reading that needs macro expansion, which is a DECLARED limitation of
|
|
3623
|
+
* this lane (M14-HONESTY-CONTRACT , "macro/template expansion
|
|
3624
|
+
* boundaries (c, cpp, rust, hcl)"), so the macro-mediated population is
|
|
3625
|
+
* reported with its count rather than resolved.
|
|
3626
|
+
*
|
|
3627
|
+
* `dllimport` is the opposite direction (this translation unit imports the
|
|
3628
|
+
* symbol from another module) and `visibility("hidden")` is the opposite of
|
|
3629
|
+
* default visibility, so neither is an export and neither is emitted.
|
|
3630
|
+
*/
|
|
3631
|
+
exportAttributeSpelling(node) {
|
|
3632
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
3633
|
+
const c = node.namedChild(i);
|
|
3634
|
+
if (!c) continue;
|
|
3635
|
+
if (c.type === "ms_declspec_modifier") {
|
|
3636
|
+
for (let j = 0; j < c.namedChildCount; j++) {
|
|
3637
|
+
const a = c.namedChild(j);
|
|
3638
|
+
if (a?.type === "identifier" && a.text === "dllexport") return "declspec-dllexport";
|
|
3639
|
+
}
|
|
3640
|
+
continue;
|
|
3641
|
+
}
|
|
3642
|
+
if (c.type === "attribute_specifier") {
|
|
3643
|
+
let hit = false;
|
|
3644
|
+
const visit = (n) => {
|
|
3645
|
+
if (hit) return;
|
|
3646
|
+
if (n.type === "call_expression") {
|
|
3647
|
+
const fn = n.childForFieldName?.("function");
|
|
3648
|
+
if (fn && String(fn.text ?? "").trim() === "visibility") {
|
|
3649
|
+
const args = n.childForFieldName?.("arguments");
|
|
3650
|
+
if (args) {
|
|
3651
|
+
for (let k = 0; k < args.namedChildCount; k++) {
|
|
3652
|
+
const arg = args.namedChild(k);
|
|
3653
|
+
const lit = arg?.type === "string_literal" ? arg : arg ? this.findChild(arg, "string_literal") : null;
|
|
3654
|
+
if (lit && stripIncludeChars(String(lit.text ?? "")).trim() === "default") {
|
|
3655
|
+
hit = true;
|
|
3656
|
+
return;
|
|
3657
|
+
}
|
|
3658
|
+
}
|
|
3659
|
+
}
|
|
3660
|
+
}
|
|
3661
|
+
}
|
|
3662
|
+
for (let k = 0; k < n.namedChildCount; k++) {
|
|
3663
|
+
const g = n.namedChild(k);
|
|
3664
|
+
if (g) visit(g);
|
|
3665
|
+
}
|
|
3666
|
+
};
|
|
3667
|
+
visit(c);
|
|
3668
|
+
if (hit) return "attribute-visibility-default";
|
|
3669
|
+
}
|
|
3670
|
+
if (c.type === "attribute_declaration") {
|
|
3671
|
+
const hit = this.attributeRoleExport(c);
|
|
3672
|
+
if (hit) return hit;
|
|
3673
|
+
}
|
|
3674
|
+
}
|
|
3675
|
+
return null;
|
|
3676
|
+
}
|
|
3677
|
+
/**
|
|
3678
|
+
* Does an `attribute_declaration` (`[[...]]`) export its declaration?
|
|
3679
|
+
*
|
|
3680
|
+
* Returns the spelling, or null. Node walk only: the attribute's last
|
|
3681
|
+
* `identifier` is its name, and the argument is read from the `argument_list`
|
|
3682
|
+
* child rather than sliced out of the attribute's text — so a COMMENT naming
|
|
3683
|
+
* `visibility` and a `visibility` inside a string literal cannot produce an
|
|
3684
|
+
* edge, which is the whole reason this lane holds no regexes.
|
|
3685
|
+
*/
|
|
3686
|
+
attributeRoleExport(attrDecl) {
|
|
3687
|
+
for (let i = 0; i < attrDecl.namedChildCount; i++) {
|
|
3688
|
+
const attr = attrDecl.namedChild(i);
|
|
3689
|
+
if (attr?.type !== "attribute") continue;
|
|
3690
|
+
let name = null;
|
|
3691
|
+
for (let j = 0; j < attr.namedChildCount; j++) {
|
|
3692
|
+
const c = attr.namedChild(j);
|
|
3693
|
+
if (c?.type === "identifier") name = String(c.text ?? "");
|
|
3694
|
+
}
|
|
3695
|
+
if (name === "dllexport") return "attribute-dllexport";
|
|
3696
|
+
if (name !== "visibility") continue;
|
|
3697
|
+
const args = this.findChild(attr, "argument_list");
|
|
3698
|
+
if (!args) continue;
|
|
3699
|
+
for (let k = 0; k < args.namedChildCount; k++) {
|
|
3700
|
+
const arg = args.namedChild(k);
|
|
3701
|
+
const lit = arg?.type === "string_literal" ? arg : arg ? this.findChild(arg, "string_literal") : null;
|
|
3702
|
+
if (lit && stripIncludeChars(String(lit.text ?? "")).trim() === "default") return "attribute-visibility-default";
|
|
3703
|
+
}
|
|
3704
|
+
}
|
|
3705
|
+
return null;
|
|
3706
|
+
}
|
|
3707
|
+
/**
|
|
3708
|
+
* Is this class-scope function declaration PURE VIRTUAL — `virtual void f() = 0;`?
|
|
3709
|
+
*
|
|
3710
|
+
* Structural, because the pinned grammar has no reachable node for it. The
|
|
3711
|
+
* fork DOES declare a `pure_virtual_clause` rule, but reaches it only from
|
|
3712
|
+
* `inline_method_definition` / `constructor_or_destructor_definition` and the
|
|
3713
|
+
* corpus produces none of them, so the marker has to be read from the tree.
|
|
3714
|
+
*
|
|
3715
|
+
* TWO SHAPES, because the grammar produces two and they occur inside the same
|
|
3716
|
+
* file (measured on cpp-example: 19 of 34 sites are the first, 15 the second):
|
|
3717
|
+
*
|
|
3718
|
+
* field_declaration [declarator=function_declarator type=primitive_type]
|
|
3719
|
+
* virtual, primitive_type "bool", function_declarator …
|
|
3720
|
+
* "=", number_literal "0" <- `0` a DIRECT child
|
|
3721
|
+
*
|
|
3722
|
+
* declaration [declarator=init_declarator type=primitive_type]
|
|
3723
|
+
* virtual, primitive_type "bool",
|
|
3724
|
+
* init_declarator [declarator=function_declarator value=number_literal]
|
|
3725
|
+
* <- `0` nested under value
|
|
3726
|
+
*
|
|
3727
|
+
* D23: the first cut read the `=`/`0` pair off the declaration's own direct
|
|
3728
|
+
* children only, which is why the second shape — the one `reporter.h` and
|
|
3729
|
+
* `json.hpp` actually use — was never registered as an interface operation.
|
|
3730
|
+
* A member variable initialised to zero (`virtual int x = 0;`) is NOT a pure
|
|
3731
|
+
* virtual, and in both shapes the declarator must be a `function_declarator`;
|
|
3732
|
+
* that pairing is what separates the two. A defaulted argument (`void f(int x =
|
|
3733
|
+
* 0)`) puts its `=` inside the `parameter_list`, which no arm here reads.
|
|
3734
|
+
*/
|
|
3735
|
+
isPureVirtualDeclaration(declaration) {
|
|
3736
|
+
if (declaration.type !== "field_declaration" && declaration.type !== "declaration") return false;
|
|
3737
|
+
const direct = declaration.childForFieldName?.("declarator");
|
|
3738
|
+
if (direct?.type === "function_declarator" && hasZeroLiteralChild(declaration)) return true;
|
|
3739
|
+
for (let i = 0; i < declaration.namedChildCount; i++) {
|
|
3740
|
+
const c = declaration.namedChild(i);
|
|
3741
|
+
if (!c || c.type !== "init_declarator") continue;
|
|
3742
|
+
const value = c.childForFieldName?.("value");
|
|
3743
|
+
const inner = c.childForFieldName?.("declarator");
|
|
3744
|
+
if (value?.type === "number_literal" && String(value.text ?? "").trim() === "0" && inner?.type === "function_declarator") return true;
|
|
3745
|
+
}
|
|
3746
|
+
return false;
|
|
3747
|
+
}
|
|
3748
|
+
/**
|
|
3749
|
+
* Truth guard for `instantiates`: is this constructed-type name bound by an
|
|
3750
|
+
* enclosing template's parameter list?
|
|
3751
|
+
*
|
|
3752
|
+
* `new Reporter(n)` inside `template <typename Reporter> Reporter*
|
|
3753
|
+
* reporterCreator(...)` constructs a TYPE PARAMETER, not a class, and the
|
|
3754
|
+
* published row named a target no translation unit can declare — measured on
|
|
3755
|
+
* cpp-example as `instantiates -> Reporter` from doctest's
|
|
3756
|
+
* `reporterCreator<Reporter>`, next to `instantiates -> char` from
|
|
3757
|
+
* `new char[4]` (refused by the keyword funnel beside this call). Both are
|
|
3758
|
+
* fabricated rows in the sense the honesty contract cares about: they are
|
|
3759
|
+
* stored, compared against nothing, and unresolvable in every file.
|
|
3760
|
+
*
|
|
3761
|
+
* TWO ARMS, because the binding survives in two spellings:
|
|
3762
|
+
*
|
|
3763
|
+
* (1) the ORDINARY shape — an ancestor `template_declaration` whose
|
|
3764
|
+
* `template_parameter_list` is a real node. The walk ascends through every
|
|
3765
|
+
* one of them (a nested template's inner list can bind a name an outer list
|
|
3766
|
+
* does not), and the declared names come from `templateParameterNames`.
|
|
3767
|
+
* (2) the RECOVERY shape — the list is displaced out of the ancestor chain
|
|
3768
|
+
* entirely, left as a `< … >` frame among the preceding children of the ERROR
|
|
3769
|
+
* region the grammar produced (`isTemplateParameterListFrame`), introduced by
|
|
3770
|
+
* the `template` token beside it. Measured at `doctest.h:2104`: that whole
|
|
3771
|
+
* file is one ERROR region, no `template_declaration` node exists for
|
|
3772
|
+
* `reporterCreator`, and arm (1) can never see the binding — which is why
|
|
3773
|
+
* `instantiates -> Reporter` survived this guard's first cut. Both arms ask
|
|
3774
|
+
* `templateParameterNames` the same question, so the two spellings cannot
|
|
3775
|
+
* drift apart.
|
|
3776
|
+
*
|
|
3777
|
+
* Arm (2) reads only nodes that START before the construction: a frame written
|
|
3778
|
+
* after it cannot have bound the name.
|
|
3779
|
+
*/
|
|
3780
|
+
isTemplateParameterName(node, name) {
|
|
3781
|
+
for (let cur = node.parent, i = 0; cur && i < 64; i++, cur = cur.parent) {
|
|
3782
|
+
if (cur.type === "template_declaration") {
|
|
3783
|
+
const params = this.findChild(cur, "template_parameter_list");
|
|
3784
|
+
if (params && templateParameterNames(params).has(name)) return true;
|
|
3785
|
+
continue;
|
|
3786
|
+
}
|
|
3787
|
+
if (cur.type !== "ERROR") continue;
|
|
3788
|
+
for (let k = 0; k < cur.childCount; k++) {
|
|
3789
|
+
const frame = cur.child(k);
|
|
3790
|
+
if (!frame || frame.startIndex >= node.startIndex) break;
|
|
3791
|
+
if (!isTemplateParameterListFrame(frame)) continue;
|
|
3792
|
+
if (cur.child(k - 1)?.type !== "template") continue;
|
|
3793
|
+
if (frame.startIndex <= node.startIndex && frame.endIndex >= node.endIndex) continue;
|
|
3794
|
+
if (templateParameterNames(frame).has(name)) return true;
|
|
3795
|
+
}
|
|
3796
|
+
}
|
|
3797
|
+
return false;
|
|
3798
|
+
}
|
|
3799
|
+
hasDirectModifier(node, modifier) {
|
|
3800
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
3801
|
+
const child = node.child(i);
|
|
3802
|
+
if (!child) continue;
|
|
3803
|
+
if (child.text === modifier) return true;
|
|
3804
|
+
if ((child.type === "type_qualifier" || child.type === "storage_class_specifier" || child.type === "virtual_specifier") && child.text === modifier) {
|
|
3805
|
+
return true;
|
|
3806
|
+
}
|
|
3807
|
+
}
|
|
3808
|
+
const funcDecl = this.findChild(node, "function_declarator");
|
|
3809
|
+
if (funcDecl) {
|
|
3810
|
+
for (let i = 0; i < funcDecl.childCount; i++) {
|
|
3811
|
+
const child = funcDecl.child(i);
|
|
3812
|
+
if (child?.type === "virtual_specifier" && child.text === modifier) return true;
|
|
3813
|
+
}
|
|
3814
|
+
}
|
|
3815
|
+
return false;
|
|
3816
|
+
}
|
|
3817
|
+
/**
|
|
3818
|
+
* #921 (M60): `nameNode` is the node whose text IS the declared name (the
|
|
3819
|
+
* qualified form returns its LAST named child, the constructor/destructor and
|
|
3820
|
+
* operator spellings included). The containment edge needs the line that
|
|
3821
|
+
* NAMES the child — a wrapped return type puts the name one or more lines
|
|
3822
|
+
* below the declaration's start (`void NORETURN` then `CheckFailed(…)`).
|
|
3823
|
+
*/
|
|
3824
|
+
getFunctionDetails(node) {
|
|
3825
|
+
let decl = this.findChild(node, "function_declarator");
|
|
3826
|
+
if (!decl) {
|
|
3827
|
+
const WRAPPERS = ["init_declarator", "pointer_declarator", "reference_declarator", "parenthesized_declarator"];
|
|
3828
|
+
let cur = null;
|
|
3829
|
+
for (const w of WRAPPERS) {
|
|
3830
|
+
cur = this.findChild(node, w);
|
|
3831
|
+
if (cur) break;
|
|
3832
|
+
}
|
|
3833
|
+
const enterRecovery = !!this.findChild(node, "compound_statement") || this.recoveryStatesType(cur);
|
|
3834
|
+
for (let i = 0; cur && i < 8; i++) {
|
|
3835
|
+
const found = this.firstDeclaratorIn(cur, enterRecovery);
|
|
3836
|
+
if (found) {
|
|
3837
|
+
decl = found;
|
|
3838
|
+
break;
|
|
3839
|
+
}
|
|
3840
|
+
let next = null;
|
|
3841
|
+
for (const w of WRAPPERS) {
|
|
3842
|
+
next = this.findChild(cur, w);
|
|
3843
|
+
if (next) break;
|
|
3844
|
+
}
|
|
3845
|
+
cur = next;
|
|
3846
|
+
}
|
|
3847
|
+
}
|
|
3848
|
+
if (!decl) return null;
|
|
3849
|
+
let nameNode = null;
|
|
3850
|
+
{
|
|
3851
|
+
let cur = decl;
|
|
3852
|
+
for (let i = 0; cur && i < 8; i++) {
|
|
3853
|
+
nameNode = this.findChild(cur, "identifier") || this.findChild(cur, "field_identifier") || this.findChild(cur, "destructor_name") || this.findChild(cur, "operator_name") || this.findChild(cur, "qualified_identifier");
|
|
3854
|
+
if (nameNode) break;
|
|
3855
|
+
cur = this.findChild(cur, "parenthesized_declarator");
|
|
3856
|
+
}
|
|
3857
|
+
}
|
|
3858
|
+
if (!nameNode) return null;
|
|
3859
|
+
let namedNode = nameNode;
|
|
3860
|
+
let name = nameNode.text;
|
|
3861
|
+
let scope = void 0;
|
|
3862
|
+
let isDestructor = nameNode.type === "destructor_name" || name.startsWith("~");
|
|
3863
|
+
let isOperator = nameNode.type === "operator_name" || name.startsWith("operator");
|
|
3864
|
+
if (!isCppSpelledName(name)) return null;
|
|
3865
|
+
if (nameNode.type === "qualified_identifier") {
|
|
3866
|
+
const parts = qualifiedDeclaratorLeaf(nameNode);
|
|
3867
|
+
const actualNameNode = parts?.nameNode ?? nameNode.namedChild(nameNode.namedChildCount - 1);
|
|
3868
|
+
const scopeQid = parts?.scopeQid ?? null;
|
|
3869
|
+
let scopeNode = null;
|
|
3870
|
+
for (let i = 0; scopeQid && i < scopeQid.namedChildCount - 1; i++) {
|
|
3871
|
+
const c = scopeQid.namedChild(i);
|
|
3872
|
+
if (c && (c.type === "namespace_identifier" || c.type === "type_identifier" || c.type === "identifier")) {
|
|
3873
|
+
scopeNode = c;
|
|
3874
|
+
break;
|
|
3875
|
+
}
|
|
3876
|
+
if (c && c.type === "template_type") {
|
|
3877
|
+
const tname = c.childForFieldName("name") || this.findChild(c, "type_identifier");
|
|
3878
|
+
if (tname) {
|
|
3879
|
+
scopeNode = tname;
|
|
3880
|
+
break;
|
|
3881
|
+
}
|
|
3882
|
+
}
|
|
3883
|
+
}
|
|
3884
|
+
const firstChild = scopeQid?.namedChild(0);
|
|
3885
|
+
if (!scopeNode && firstChild && firstChild.type === "qualified_identifier") {
|
|
3886
|
+
scopeNode = firstChild;
|
|
3887
|
+
}
|
|
3888
|
+
if (actualNameNode) {
|
|
3889
|
+
name = actualNameNode.text;
|
|
3890
|
+
namedNode = actualNameNode;
|
|
3891
|
+
if (scopeNode) scope = scopeNode.text;
|
|
3892
|
+
if (actualNameNode.type === "destructor_name" || name.startsWith("~")) isDestructor = true;
|
|
3893
|
+
if (actualNameNode.type === "operator_name" || name.startsWith("operator")) isOperator = true;
|
|
3894
|
+
}
|
|
3895
|
+
}
|
|
3896
|
+
if (!isPublishableFunctionName(name)) return null;
|
|
3897
|
+
return { name, scope, isDestructor, isOperator, nameNode: namedNode, declarator: decl };
|
|
3898
|
+
}
|
|
3899
|
+
/**
|
|
3900
|
+
* The FIRST `function_declarator` in DOCUMENT order under a declarator wrapper.
|
|
3901
|
+
* `findChild` scans one level, and inside a recovered head the wrapper's DIRECT
|
|
3902
|
+
* children can hold a DECOY the recovery read out of a mem-initializer, while the
|
|
3903
|
+
* declarator the source wrote sits one level deeper, in the grammar's own ERROR:
|
|
3904
|
+
*
|
|
3905
|
+
* JSON_HEDLEY_NON_NULL(3)
|
|
3906
|
+
* invalid_iterator(int id_, const char* what_arg) : exception(id_, what_arg) {}
|
|
3907
|
+
* function_definition[type: JSON_HEDLEY_NON_NULL]
|
|
3908
|
+
* parenthesized_declarator <- the macro's own parens
|
|
3909
|
+
* ERROR <- the recovery
|
|
3910
|
+
* number_literal(3)
|
|
3911
|
+
* function_declarator(invalid_iterator) <- the DECLARED name
|
|
3912
|
+
* function_declarator(exception) <- the base ctor CALL
|
|
3913
|
+
*
|
|
3914
|
+
* The recovery writes them in source order, so the first is the declared name;
|
|
3915
|
+
* taking the direct child instead published `method invalid_iterator.exception` —
|
|
3916
|
+
* a member named after the class's own BASE, with no parameters and a body it
|
|
3917
|
+
* does not have, while the constructor itself stayed unpublished. Depth is bounded
|
|
3918
|
+
* and the descent stays inside declarator wrappers, so a declarator inside the
|
|
3919
|
+
* function's BODY can never be reached.
|
|
3920
|
+
*
|
|
3921
|
+
* `enterRecovery` gates the descent into an ERROR, and the caller sets it from the
|
|
3922
|
+
* declaration's own evidence: a body, or a TYPE in the recovery. The same recovery
|
|
3923
|
+
* wraps a macro INVOCATION in the macro's parens, and there the ERROR holds an
|
|
3924
|
+
* EXPRESSION, not a declarator — `CHECK(p3.getHairColor() == "blue");` inside a
|
|
3925
|
+
* test macro's body recovers with the CALL's own `function_declarator`, and reading
|
|
3926
|
+
* it published a method nobody declared.
|
|
3927
|
+
*/
|
|
3928
|
+
firstDeclaratorIn(container, enterRecovery, depth = 0) {
|
|
3929
|
+
if (depth > 6) return null;
|
|
3930
|
+
for (let i = 0; i < container.namedChildCount; i++) {
|
|
3931
|
+
const c = container.namedChild(i);
|
|
3932
|
+
if (!c) continue;
|
|
3933
|
+
if (c.type === "function_declarator") return c;
|
|
3934
|
+
if (c.type.endsWith("declarator")) {
|
|
3935
|
+
const inner = this.firstDeclaratorIn(c, enterRecovery, depth + 1);
|
|
3936
|
+
if (inner) return inner;
|
|
3937
|
+
} else if (enterRecovery && c.type === "ERROR") {
|
|
3938
|
+
const inner = this.firstDeclaratorIn(c, enterRecovery, depth + 1);
|
|
3939
|
+
if (inner) return inner;
|
|
3940
|
+
}
|
|
3941
|
+
}
|
|
3942
|
+
return null;
|
|
3943
|
+
}
|
|
3944
|
+
/**
|
|
3945
|
+
* The other half of the declaration test for entering a recovery: does the wrapper
|
|
3946
|
+
* the macro left behind STATE A TYPE? That is what separates the declaration the
|
|
3947
|
+
* recovery swallowed from a macro INVOCATION, and the two are one node type apart:
|
|
3948
|
+
*
|
|
3949
|
+
* JSON_HEDLEY_RETURNS_NON_NULL CHECK(p3.getHairColor() == "blue");
|
|
3950
|
+
* char* to_chars(…) // field_declaration
|
|
3951
|
+
* parenthesized_declarator parenthesized_declarator
|
|
3952
|
+
* ERROR ERROR
|
|
3953
|
+
* identifier(JSON_HEDLEY_…) identifier(p3)
|
|
3954
|
+
* primitive_type(char) <- the TYPE function_declarator(getHairColor)
|
|
3955
|
+
* pointer_declarator <- (the call, no parameters)
|
|
3956
|
+
*
|
|
3957
|
+
* `char` is the discriminator: only a declaration of something states a type before
|
|
3958
|
+
* its declarator, and `recoveredTypeSpelling` reads it with the same narrowness the
|
|
3959
|
+
* return-type arm uses.
|
|
3960
|
+
*/
|
|
3961
|
+
recoveryStatesType(wrapper) {
|
|
3962
|
+
if (!wrapper) return false;
|
|
3963
|
+
for (let i = 0; i < wrapper.namedChildCount; i++) {
|
|
3964
|
+
const c = wrapper.namedChild(i);
|
|
3965
|
+
if (c && c.type === "ERROR" && this.recoveredTypeSpelling(c)) return true;
|
|
3966
|
+
}
|
|
3967
|
+
return false;
|
|
3968
|
+
}
|
|
3969
|
+
/**
|
|
3970
|
+
* D17's premise test: did the recovery leave material BETWEEN the decorator and
|
|
3971
|
+
* the declarator? A decorated head does — the macro's own parens, or an ERROR
|
|
3972
|
+
* holding the displaced type — while `HANDLE Open();` does not, and only the
|
|
3973
|
+
* first can be a decorator. Stops at the declarator, so an unrelated ERROR inside
|
|
3974
|
+
* the function's BODY cannot make a type look decorated.
|
|
3975
|
+
*/
|
|
3976
|
+
recoveredDecoratorHead(node, declarator) {
|
|
3977
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
3978
|
+
const c = node.namedChild(i);
|
|
3979
|
+
if (!c || declarator && c.startIndex > declarator.startIndex) break;
|
|
3980
|
+
if (c.type === "ERROR" || c.type === "parenthesized_declarator") return true;
|
|
3981
|
+
}
|
|
3982
|
+
return false;
|
|
3983
|
+
}
|
|
3984
|
+
/**
|
|
3985
|
+
* D18 arm B: the return type the recovery DISPLACED out of the `type` field,
|
|
3986
|
+
* visible as material between the decorator and the declarator:
|
|
3987
|
+
*
|
|
3988
|
+
* JSON_HEDLEY_NON_NULL(1) JSON_HEDLEY_WARN_UNUSED_RESULT
|
|
3989
|
+
* void grisu2(char* buf, …) constexpr bool is_primitive() const
|
|
3990
|
+
* ERROR[number_literal(1), type_qualifier(constexpr)
|
|
3991
|
+
* primitive_type(void)] ERROR[identifier(bool)]
|
|
3992
|
+
*
|
|
3993
|
+
* The type is the LAST type the recovery left before the declarator, read
|
|
3994
|
+
* OUTWARD from the declarator (its own ERROR first, then each enclosing node up
|
|
3995
|
+
* to the declaration) so the nearest evidence wins. Returns undefined when the
|
|
3996
|
+
* recovery left no type at all — a constructor's decorated head, where the ERROR
|
|
3997
|
+
* holds only the macro's argument and the declarator.
|
|
3998
|
+
*/
|
|
3999
|
+
displacedReturnType(node, declarator) {
|
|
4000
|
+
if (!declarator) return void 0;
|
|
4001
|
+
let cur = declarator;
|
|
4002
|
+
for (let depth = 0; cur && depth < 8; depth++) {
|
|
4003
|
+
const parent = cur.parent;
|
|
4004
|
+
if (!parent) break;
|
|
4005
|
+
if (parent.startIndex < node.startIndex || parent.endIndex > node.endIndex) break;
|
|
4006
|
+
let found = void 0;
|
|
4007
|
+
for (let i = 0; i < parent.namedChildCount; i++) {
|
|
4008
|
+
const c = parent.namedChild(i);
|
|
4009
|
+
if (!c || c.startIndex >= declarator.startIndex) break;
|
|
4010
|
+
const spelling = this.recoveredTypeSpelling(c);
|
|
4011
|
+
if (spelling) found = spelling;
|
|
4012
|
+
}
|
|
4013
|
+
if (found) return found;
|
|
4014
|
+
cur = parent;
|
|
4015
|
+
}
|
|
4016
|
+
return void 0;
|
|
4017
|
+
}
|
|
4018
|
+
/**
|
|
4019
|
+
* The type ONE recovered node states, or undefined when it states none.
|
|
4020
|
+
*
|
|
4021
|
+
* A bare `identifier` counts only when the ERROR holds exactly one named child,
|
|
4022
|
+
* the same narrowness `recoveredDeclaredName` uses for a displaced NAME: `bool`
|
|
4023
|
+
* and `T` arrive that way (the recovery does not classify them as types), and
|
|
4024
|
+
* anything richer is the recovery's own soup rather than a type. A macro
|
|
4025
|
+
* spelling never counts — the decorator is not the type it decorates, which is
|
|
4026
|
+
* the whole point of the arm.
|
|
4027
|
+
*/
|
|
4028
|
+
recoveredTypeSpelling(n, depth = 0) {
|
|
4029
|
+
if (depth > 6 || n.type === "function_declarator") return void 0;
|
|
4030
|
+
if (n.type === "ERROR") {
|
|
4031
|
+
if (n.namedChildCount === 1) {
|
|
4032
|
+
const only = n.namedChild(0);
|
|
4033
|
+
if (only && only.type === "identifier") {
|
|
4034
|
+
const single = collapseSpaces(only.text);
|
|
4035
|
+
return single && !isMacroSpelling(single) ? single : void 0;
|
|
4036
|
+
}
|
|
4037
|
+
}
|
|
4038
|
+
let found = void 0;
|
|
4039
|
+
for (let i = 0; i < n.namedChildCount; i++) {
|
|
4040
|
+
const c = n.namedChild(i);
|
|
4041
|
+
if (!c || c.type === "function_declarator") break;
|
|
4042
|
+
const spelling = this.recoveredTypeSpelling(c, depth + 1);
|
|
4043
|
+
if (spelling) found = spelling;
|
|
4044
|
+
}
|
|
4045
|
+
return found;
|
|
4046
|
+
}
|
|
4047
|
+
if (n.type === "primitive_type" || n.type === "sized_type_specifier" || n.type === "template_type" || n.type === "qualified_identifier" || n.type === "dependent_type" || n.type === "decltype" || n.type === "struct_specifier" || n.type === "union_specifier" || n.type === "enum_specifier" || n.type === "placeholder_type_specifier" || n.type === "macro_prefixed_type_specifier" || n.type === "type_identifier") {
|
|
4048
|
+
const text = n.type === "macro_prefixed_type_specifier" ? specifierTypeText(n) ?? "" : collapseSpaces(n.text);
|
|
4049
|
+
return text && !isMacroSpelling(text) ? text : void 0;
|
|
4050
|
+
}
|
|
4051
|
+
return void 0;
|
|
4052
|
+
}
|
|
4053
|
+
// ≡ /^[A-Za-z_]\w*$/ (zero-regex helper, M49 closure wave) — full-string
|
|
4054
|
+
// identifier shape: letter/underscore head, word-class tail. CGD19 class:
|
|
4055
|
+
// recovery-artifact type names (qualified, templated, mangled) fail this
|
|
4056
|
+
// and emit no edge.
|
|
4057
|
+
isPlainIdentifier(s) {
|
|
4058
|
+
if (s.length === 0) return false;
|
|
4059
|
+
const c0 = s.charCodeAt(0);
|
|
4060
|
+
const firstOk = c0 >= 65 && c0 <= 90 || c0 >= 97 && c0 <= 122 || c0 === 95;
|
|
4061
|
+
if (!firstOk) return false;
|
|
4062
|
+
for (let i = 1; i < s.length; i++) {
|
|
4063
|
+
const c = s.charCodeAt(i);
|
|
4064
|
+
const ok = c >= 65 && c <= 90 || c >= 97 && c <= 122 || c >= 48 && c <= 57 || c === 95;
|
|
4065
|
+
if (!ok) return false;
|
|
4066
|
+
}
|
|
4067
|
+
return true;
|
|
4068
|
+
}
|
|
4069
|
+
buildFullName(scopeStack, name) {
|
|
4070
|
+
const parts = [];
|
|
4071
|
+
for (let i = 0; i < scopeStack.length; i++) {
|
|
4072
|
+
if (scopeStack[i].kind === ASTEngine.SymbolKind.Namespace) {
|
|
4073
|
+
parts.push(scopeStack[i].name);
|
|
4074
|
+
}
|
|
4075
|
+
}
|
|
4076
|
+
parts.push(name);
|
|
4077
|
+
return parts.join("::");
|
|
4078
|
+
}
|
|
4079
|
+
};
|
|
4080
|
+
export {
|
|
4081
|
+
CPP_ANONYMOUS,
|
|
4082
|
+
CPP_BUILTIN_TYPE_KEYWORDS,
|
|
4083
|
+
CPP_EXPORT_MARKER_COMPONENTS,
|
|
4084
|
+
CPP_KEYWORDS,
|
|
4085
|
+
CPP_NON_CLASS_STANDARD_TYPES,
|
|
4086
|
+
CPP_ORACLE_CONTRACT,
|
|
4087
|
+
RealCppAnalyzer,
|
|
4088
|
+
attributeDisplacedEnumName,
|
|
4089
|
+
cppExportMarker,
|
|
4090
|
+
cppPureVirtualDeclaration,
|
|
4091
|
+
extendsTargetName,
|
|
4092
|
+
isCppSpelledName,
|
|
4093
|
+
isPublishableFunctionName,
|
|
4094
|
+
macroDeclaredBody,
|
|
4095
|
+
macroInvocationText,
|
|
4096
|
+
qualifiedDeclaratorLeaf,
|
|
4097
|
+
recoveredDeclaredName,
|
|
4098
|
+
sameTsNode,
|
|
4099
|
+
specifierDeclaredName,
|
|
4100
|
+
templateWrappedDeclaratorName
|
|
4101
|
+
};
|