xq-lsp 1.4.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +25 -0
- package/README.md +54 -8
- package/dist/completion-core.d.ts +1 -1
- package/dist/completion-core.js +72 -7
- package/dist/runtimes.d.ts +14 -1
- package/dist/runtimes.js +37 -0
- package/dist/server.js +61 -21
- package/dist/variable-diagnostics.d.ts +19 -0
- package/dist/variable-diagnostics.js +164 -0
- package/package.json +4 -4
- package/src/completion-core.ts +71 -6
- package/src/runtimes/existdb/predeclared-namespaces.json +34 -0
- package/src/runtimes/existdb/util.xq +32 -0
- package/src/runtimes.ts +43 -1
- package/src/server.ts +67 -19
- package/src/variable-diagnostics.ts +190 -0
- package/firebase.json +0 -12
- package/release-please-config.json +0 -16
- package/scripts/generate-existdb.ts +0 -890
- package/src/analyzer.test.ts +0 -163
- package/src/builtins.test.ts +0 -32
- package/src/completion.test.ts +0 -152
- package/src/context-item-diagnostics.test.ts +0 -182
- package/src/features.test.ts +0 -110
- package/src/functioncall-diagnostics.test.ts +0 -169
- package/src/lsp-config.test.ts +0 -183
- package/src/namespace-diagnostics.test.ts +0 -162
- package/src/qt4-diagnostic.test.ts +0 -271
- package/src/qt4-snaps/app-CatalogCheck.snap +0 -1
- package/src/qt4-snaps/app-Demos.snap +0 -18
- package/src/qt4-snaps/app-Duplicates.snap +0 -1
- package/src/qt4-snaps/app-FunctxFn.snap +0 -162
- package/src/qt4-snaps/app-FunctxFunctx.snap +0 -714
- package/src/qt4-snaps/app-UseCaseCompoundValues.snap +0 -1
- package/src/qt4-snaps/app-UseCaseJSON.snap +0 -1
- package/src/qt4-snaps/app-UseCaseNLP.snap +0 -1
- package/src/qt4-snaps/app-UseCaseNS.snap +0 -1
- package/src/qt4-snaps/app-UseCaseR.snap +0 -1
- package/src/qt4-snaps/app-UseCaseR31.snap +0 -1
- package/src/qt4-snaps/app-UseCaseSEQ.snap +0 -1
- package/src/qt4-snaps/app-UseCaseSGML.snap +0 -1
- package/src/qt4-snaps/app-UseCaseSTRING.snap +0 -1
- package/src/qt4-snaps/app-UseCaseTREE.snap +0 -1
- package/src/qt4-snaps/app-UseCaseXMP.snap +0 -1
- package/src/qt4-snaps/app-Walmsley.snap +0 -1
- package/src/qt4-snaps/app-XMark.snap +0 -10
- package/src/qt4-snaps/array-append.snap +0 -1
- package/src/qt4-snaps/array-filter.snap +0 -10
- package/src/qt4-snaps/array-flatten.snap +0 -1
- package/src/qt4-snaps/array-fold-left.snap +0 -1
- package/src/qt4-snaps/array-fold-right.snap +0 -1
- package/src/qt4-snaps/array-for-each-pair.snap +0 -1
- package/src/qt4-snaps/array-for-each.snap +0 -1
- package/src/qt4-snaps/array-get.snap +0 -1
- package/src/qt4-snaps/array-head.snap +0 -1
- package/src/qt4-snaps/array-insert-before.snap +0 -1
- package/src/qt4-snaps/array-join.snap +0 -1
- package/src/qt4-snaps/array-put.snap +0 -1
- package/src/qt4-snaps/array-remove.snap +0 -1
- package/src/qt4-snaps/array-reverse.snap +0 -1
- package/src/qt4-snaps/array-size.snap +0 -1
- package/src/qt4-snaps/array-sort.snap +0 -1
- package/src/qt4-snaps/array-subarray.snap +0 -1
- package/src/qt4-snaps/array-tail.snap +0 -1
- package/src/qt4-snaps/bin-and.snap +0 -1
- package/src/qt4-snaps/bin-bin.snap +0 -1
- package/src/qt4-snaps/bin-decode-string.snap +0 -1
- package/src/qt4-snaps/bin-encode-string.snap +0 -1
- package/src/qt4-snaps/bin-find.snap +0 -1
- package/src/qt4-snaps/bin-from-octets.snap +0 -1
- package/src/qt4-snaps/bin-hex.snap +0 -1
- package/src/qt4-snaps/bin-insert-before.snap +0 -1
- package/src/qt4-snaps/bin-join.snap +0 -1
- package/src/qt4-snaps/bin-length.snap +0 -1
- package/src/qt4-snaps/bin-not.snap +0 -1
- package/src/qt4-snaps/bin-octal.snap +0 -1
- package/src/qt4-snaps/bin-or.snap +0 -1
- package/src/qt4-snaps/bin-pack-double.snap +0 -1
- package/src/qt4-snaps/bin-pack-float.snap +0 -1
- package/src/qt4-snaps/bin-pack-integer.snap +0 -1
- package/src/qt4-snaps/bin-pad-left.snap +0 -1
- package/src/qt4-snaps/bin-pad-right.snap +0 -1
- package/src/qt4-snaps/bin-part.snap +0 -1
- package/src/qt4-snaps/bin-shift.snap +0 -1
- package/src/qt4-snaps/bin-to-octets.snap +0 -1
- package/src/qt4-snaps/bin-unpack-double.snap +0 -1
- package/src/qt4-snaps/bin-unpack-float.snap +0 -1
- package/src/qt4-snaps/bin-unpack-integer.snap +0 -1
- package/src/qt4-snaps/bin-unpack-unsigned-integer.snap +0 -1
- package/src/qt4-snaps/bin-xor.snap +0 -1
- package/src/qt4-snaps/file-file.snap +0 -1
- package/src/qt4-snaps/fn-QName.snap +0 -1
- package/src/qt4-snaps/fn-abs.snap +0 -1
- package/src/qt4-snaps/fn-adjust-date-to-timezone.snap +0 -1
- package/src/qt4-snaps/fn-adjust-dateTime-to-timezone.snap +0 -1
- package/src/qt4-snaps/fn-adjust-time-to-timezone.snap +0 -1
- package/src/qt4-snaps/fn-analyze-string.snap +0 -1
- package/src/qt4-snaps/fn-apply.snap +0 -1
- package/src/qt4-snaps/fn-available-environment-variables.snap +0 -1
- package/src/qt4-snaps/fn-avg.snap +0 -1
- package/src/qt4-snaps/fn-base-uri.snap +0 -1
- package/src/qt4-snaps/fn-boolean.snap +0 -1
- package/src/qt4-snaps/fn-ceiling.snap +0 -1
- package/src/qt4-snaps/fn-codepoint-equal.snap +0 -1
- package/src/qt4-snaps/fn-codepoints-to-string.snap +0 -1
- package/src/qt4-snaps/fn-collation-key.snap +0 -1
- package/src/qt4-snaps/fn-collection.snap +0 -1
- package/src/qt4-snaps/fn-compare.snap +0 -1
- package/src/qt4-snaps/fn-concat.snap +0 -1
- package/src/qt4-snaps/fn-contains-token.snap +0 -1
- package/src/qt4-snaps/fn-contains.snap +0 -1
- package/src/qt4-snaps/fn-count.snap +0 -1
- package/src/qt4-snaps/fn-current-date.snap +0 -1
- package/src/qt4-snaps/fn-current-dateTime.snap +0 -1
- package/src/qt4-snaps/fn-current-time.snap +0 -1
- package/src/qt4-snaps/fn-data.snap +0 -8
- package/src/qt4-snaps/fn-dateTime.snap +0 -1
- package/src/qt4-snaps/fn-day-from-date.snap +0 -1
- package/src/qt4-snaps/fn-day-from-dateTime.snap +0 -1
- package/src/qt4-snaps/fn-days-from-duration.snap +0 -1
- package/src/qt4-snaps/fn-deep-equal.snap +0 -1
- package/src/qt4-snaps/fn-default-collation.snap +0 -1
- package/src/qt4-snaps/fn-default-language.snap +0 -1
- package/src/qt4-snaps/fn-distinct-values.snap +0 -1
- package/src/qt4-snaps/fn-doc-available.snap +0 -1
- package/src/qt4-snaps/fn-doc.snap +0 -1
- package/src/qt4-snaps/fn-document-uri.snap +0 -8
- package/src/qt4-snaps/fn-element-with-id.snap +0 -1
- package/src/qt4-snaps/fn-empty.snap +0 -1
- package/src/qt4-snaps/fn-encode-for-uri.snap +0 -1
- package/src/qt4-snaps/fn-ends-with.snap +0 -1
- package/src/qt4-snaps/fn-environment-variable.snap +0 -1
- package/src/qt4-snaps/fn-error.snap +0 -128
- package/src/qt4-snaps/fn-escape-html-uri.snap +0 -1
- package/src/qt4-snaps/fn-exactly-one.snap +0 -1
- package/src/qt4-snaps/fn-exists.snap +0 -1
- package/src/qt4-snaps/fn-false.snap +0 -1
- package/src/qt4-snaps/fn-filter.snap +0 -10
- package/src/qt4-snaps/fn-floor.snap +0 -1
- package/src/qt4-snaps/fn-fold-left.snap +0 -1
- package/src/qt4-snaps/fn-fold-right.snap +0 -1
- package/src/qt4-snaps/fn-for-each-pair.snap +0 -10
- package/src/qt4-snaps/fn-for-each.snap +0 -10
- package/src/qt4-snaps/fn-format-date.snap +0 -26
- package/src/qt4-snaps/fn-format-dateTime.snap +0 -1
- package/src/qt4-snaps/fn-format-integer.snap +0 -1
- package/src/qt4-snaps/fn-format-number.snap +0 -38
- package/src/qt4-snaps/fn-format-time.snap +0 -1
- package/src/qt4-snaps/fn-function-arity.snap +0 -1
- package/src/qt4-snaps/fn-function-lookup.snap +0 -1
- package/src/qt4-snaps/fn-function-name.snap +0 -1
- package/src/qt4-snaps/fn-generate-id.snap +0 -18
- package/src/qt4-snaps/fn-has-children.snap +0 -26
- package/src/qt4-snaps/fn-head.snap +0 -1
- package/src/qt4-snaps/fn-hours-from-dateTime.snap +0 -1
- package/src/qt4-snaps/fn-hours-from-duration.snap +0 -1
- package/src/qt4-snaps/fn-hours-from-time.snap +0 -1
- package/src/qt4-snaps/fn-id.snap +0 -10
- package/src/qt4-snaps/fn-idref.snap +0 -10
- package/src/qt4-snaps/fn-implicit-timezone.snap +0 -1
- package/src/qt4-snaps/fn-in-scope-prefixes.snap +0 -26
- package/src/qt4-snaps/fn-index-of.snap +0 -1
- package/src/qt4-snaps/fn-innermost.snap +0 -58
- package/src/qt4-snaps/fn-insert-before.snap +0 -1
- package/src/qt4-snaps/fn-iri-to-uri.snap +0 -1
- package/src/qt4-snaps/fn-json-doc.snap +0 -1
- package/src/qt4-snaps/fn-json-to-xml.snap +0 -1
- package/src/qt4-snaps/fn-lang.snap +0 -10
- package/src/qt4-snaps/fn-last.snap +0 -1
- package/src/qt4-snaps/fn-load-xquery-module.snap +0 -1
- package/src/qt4-snaps/fn-local-name-from-QName.snap +0 -1
- package/src/qt4-snaps/fn-local-name.snap +0 -10
- package/src/qt4-snaps/fn-lower-case.snap +0 -1
- package/src/qt4-snaps/fn-matches.re.snap +0 -1
- package/src/qt4-snaps/fn-matches.snap +0 -1
- package/src/qt4-snaps/fn-max.snap +0 -1
- package/src/qt4-snaps/fn-min.snap +0 -1
- package/src/qt4-snaps/fn-minutes-from-dateTime.snap +0 -1
- package/src/qt4-snaps/fn-minutes-from-duration.snap +0 -1
- package/src/qt4-snaps/fn-minutes-from-time.snap +0 -1
- package/src/qt4-snaps/fn-month-from-date.snap +0 -1
- package/src/qt4-snaps/fn-month-from-dateTime.snap +0 -1
- package/src/qt4-snaps/fn-months-from-duration.snap +0 -1
- package/src/qt4-snaps/fn-name.snap +0 -10
- package/src/qt4-snaps/fn-namespace-uri-for-prefix.snap +0 -1
- package/src/qt4-snaps/fn-namespace-uri-from-QName.snap +0 -1
- package/src/qt4-snaps/fn-namespace-uri.snap +0 -1
- package/src/qt4-snaps/fn-nilled.snap +0 -16
- package/src/qt4-snaps/fn-node-name.snap +0 -32
- package/src/qt4-snaps/fn-normalize-space.snap +0 -1
- package/src/qt4-snaps/fn-normalize-unicode.snap +0 -26
- package/src/qt4-snaps/fn-not.snap +0 -1
- package/src/qt4-snaps/fn-number.snap +0 -1
- package/src/qt4-snaps/fn-one-or-more.snap +0 -1
- package/src/qt4-snaps/fn-outermost.snap +0 -58
- package/src/qt4-snaps/fn-parse-ietf-date.snap +0 -1
- package/src/qt4-snaps/fn-parse-json.snap +0 -1
- package/src/qt4-snaps/fn-parse-xml-fragment.snap +0 -1
- package/src/qt4-snaps/fn-parse-xml.snap +0 -1
- package/src/qt4-snaps/fn-path.snap +0 -26
- package/src/qt4-snaps/fn-position.snap +0 -1
- package/src/qt4-snaps/fn-prefix-from-QName.snap +0 -1
- package/src/qt4-snaps/fn-random-number-generator.snap +0 -1
- package/src/qt4-snaps/fn-remove.snap +0 -1
- package/src/qt4-snaps/fn-replace.snap +0 -330
- package/src/qt4-snaps/fn-resolve-QName.snap +0 -1
- package/src/qt4-snaps/fn-resolve-uri.snap +0 -1
- package/src/qt4-snaps/fn-reverse.snap +0 -1
- package/src/qt4-snaps/fn-root.snap +0 -10
- package/src/qt4-snaps/fn-round-half-to-even.snap +0 -1
- package/src/qt4-snaps/fn-round.snap +0 -8
- package/src/qt4-snaps/fn-seconds-from-dateTime.snap +0 -1
- package/src/qt4-snaps/fn-seconds-from-duration.snap +0 -1
- package/src/qt4-snaps/fn-seconds-from-time.snap +0 -1
- package/src/qt4-snaps/fn-serialize.snap +0 -50
- package/src/qt4-snaps/fn-sort.snap +0 -1
- package/src/qt4-snaps/fn-starts-with.snap +0 -1
- package/src/qt4-snaps/fn-static-base-uri.snap +0 -1
- package/src/qt4-snaps/fn-string-join.snap +0 -8
- package/src/qt4-snaps/fn-string-length.snap +0 -1
- package/src/qt4-snaps/fn-string-to-codepoints.snap +0 -1
- package/src/qt4-snaps/fn-string.snap +0 -10
- package/src/qt4-snaps/fn-subsequence.snap +0 -1
- package/src/qt4-snaps/fn-substring-after.snap +0 -1
- package/src/qt4-snaps/fn-substring-before.snap +0 -1
- package/src/qt4-snaps/fn-substring.snap +0 -1
- package/src/qt4-snaps/fn-sum.snap +0 -1
- package/src/qt4-snaps/fn-tail.snap +0 -1
- package/src/qt4-snaps/fn-timezone-from-date.snap +0 -1
- package/src/qt4-snaps/fn-timezone-from-dateTime.snap +0 -1
- package/src/qt4-snaps/fn-timezone-from-time.snap +0 -1
- package/src/qt4-snaps/fn-tokenize.snap +0 -16
- package/src/qt4-snaps/fn-trace.snap +0 -8
- package/src/qt4-snaps/fn-transform.snap +0 -1
- package/src/qt4-snaps/fn-translate.snap +0 -1
- package/src/qt4-snaps/fn-true.snap +0 -1
- package/src/qt4-snaps/fn-unordered.snap +0 -1
- package/src/qt4-snaps/fn-unparsed-text-available.snap +0 -1
- package/src/qt4-snaps/fn-unparsed-text-lines.snap +0 -1
- package/src/qt4-snaps/fn-unparsed-text.snap +0 -1
- package/src/qt4-snaps/fn-upper-case.snap +0 -1
- package/src/qt4-snaps/fn-uri-collection.snap +0 -1
- package/src/qt4-snaps/fn-xml-to-json.snap +0 -1
- package/src/qt4-snaps/fn-year-from-date.snap +0 -1
- package/src/qt4-snaps/fn-year-from-dateTime.snap +0 -1
- package/src/qt4-snaps/fn-years-from-duration.snap +0 -1
- package/src/qt4-snaps/fn-zero-or-one.snap +0 -1
- package/src/qt4-snaps/map-contains.snap +0 -1
- package/src/qt4-snaps/map-entry.snap +0 -1
- package/src/qt4-snaps/map-find.snap +0 -1
- package/src/qt4-snaps/map-for-each.snap +0 -1
- package/src/qt4-snaps/map-get.snap +0 -1
- package/src/qt4-snaps/map-keys.snap +0 -1
- package/src/qt4-snaps/map-merge.snap +0 -1
- package/src/qt4-snaps/map-put.snap +0 -1
- package/src/qt4-snaps/map-remove.snap +0 -1
- package/src/qt4-snaps/map-size.snap +0 -1
- package/src/qt4-snaps/math-math-acos.snap +0 -1
- package/src/qt4-snaps/math-math-asin.snap +0 -1
- package/src/qt4-snaps/math-math-atan.snap +0 -1
- package/src/qt4-snaps/math-math-atan2.snap +0 -1
- package/src/qt4-snaps/math-math-cos.snap +0 -1
- package/src/qt4-snaps/math-math-exp.snap +0 -1
- package/src/qt4-snaps/math-math-exp10.snap +0 -1
- package/src/qt4-snaps/math-math-log.snap +0 -1
- package/src/qt4-snaps/math-math-log10.snap +0 -1
- package/src/qt4-snaps/math-math-pi.snap +0 -1
- package/src/qt4-snaps/math-math-pow.snap +0 -1
- package/src/qt4-snaps/math-math-sin.snap +0 -1
- package/src/qt4-snaps/math-math-sqrt.snap +0 -1
- package/src/qt4-snaps/math-math-tan.snap +0 -1
- package/src/qt4-snaps/misc-AnnexE.snap +0 -1
- package/src/qt4-snaps/misc-AppendixA4.snap +0 -1
- package/src/qt4-snaps/misc-CombinedErrorCodes.snap +0 -350
- package/src/qt4-snaps/misc-ErrorsAndOptimization.snap +0 -1
- package/src/qt4-snaps/misc-HigherOrderFunctions.snap +0 -32
- package/src/qt4-snaps/misc-JsonTestSuite.snap +0 -1
- package/src/qt4-snaps/misc-StaticContext.snap +0 -8
- package/src/qt4-snaps/misc-Surrogates.snap +0 -34
- package/src/qt4-snaps/misc-UCACollation.snap +0 -1
- package/src/qt4-snaps/misc-XMLEdition.snap +0 -22
- package/src/qt4-snaps/op-QName-equal.snap +0 -1
- package/src/qt4-snaps/op-add-dayTimeDuration-to-date.snap +0 -1
- package/src/qt4-snaps/op-add-dayTimeDuration-to-dateTime.snap +0 -1
- package/src/qt4-snaps/op-add-dayTimeDuration-to-time.snap +0 -1
- package/src/qt4-snaps/op-add-dayTimeDurations.snap +0 -1
- package/src/qt4-snaps/op-add-yearMonthDuration-to-date.snap +0 -1
- package/src/qt4-snaps/op-add-yearMonthDuration-to-dateTime.snap +0 -1
- package/src/qt4-snaps/op-add-yearMonthDurations.snap +0 -1
- package/src/qt4-snaps/op-anyURI-equal.snap +0 -1
- package/src/qt4-snaps/op-anyURI-greater-than.snap +0 -1
- package/src/qt4-snaps/op-anyURI-less-than.snap +0 -1
- package/src/qt4-snaps/op-bang.snap +0 -1
- package/src/qt4-snaps/op-base64Binary-equal.snap +0 -1
- package/src/qt4-snaps/op-base64Binary-greater-than.snap +0 -1
- package/src/qt4-snaps/op-base64Binary-less-than.snap +0 -1
- package/src/qt4-snaps/op-boolean-equal.snap +0 -1
- package/src/qt4-snaps/op-boolean-greater-than.snap +0 -1
- package/src/qt4-snaps/op-boolean-less-than.snap +0 -1
- package/src/qt4-snaps/op-concat.snap +0 -1
- package/src/qt4-snaps/op-concatenate.snap +0 -1
- package/src/qt4-snaps/op-date-equal.snap +0 -1
- package/src/qt4-snaps/op-date-greater-than.snap +0 -1
- package/src/qt4-snaps/op-date-less-than.snap +0 -1
- package/src/qt4-snaps/op-dateTime-equal.snap +0 -1
- package/src/qt4-snaps/op-dateTime-greater-than.snap +0 -1
- package/src/qt4-snaps/op-dateTime-less-than.snap +0 -1
- package/src/qt4-snaps/op-dayTimeDuration-greater-than.snap +0 -1
- package/src/qt4-snaps/op-dayTimeDuration-less-than.snap +0 -1
- package/src/qt4-snaps/op-divide-dayTimeDuration-by-dayTimeDuration.snap +0 -1
- package/src/qt4-snaps/op-divide-dayTimeDuration.snap +0 -1
- package/src/qt4-snaps/op-divide-yearMonthDuration-by-yearMonthDuration.snap +0 -1
- package/src/qt4-snaps/op-divide-yearMonthDuration.snap +0 -1
- package/src/qt4-snaps/op-duration-equal.snap +0 -1
- package/src/qt4-snaps/op-except.snap +0 -1
- package/src/qt4-snaps/op-gDay-equal.snap +0 -1
- package/src/qt4-snaps/op-gMonth-equal.snap +0 -1
- package/src/qt4-snaps/op-gMonthDay-equal.snap +0 -1
- package/src/qt4-snaps/op-gYear-equal.snap +0 -1
- package/src/qt4-snaps/op-gYearMonth-equal.snap +0 -1
- package/src/qt4-snaps/op-hexBinary-equal.snap +0 -1
- package/src/qt4-snaps/op-hexBinary-greater-than.snap +0 -1
- package/src/qt4-snaps/op-hexBinary-less-than.snap +0 -1
- package/src/qt4-snaps/op-intersect.snap +0 -1
- package/src/qt4-snaps/op-is-same-node.snap +0 -1
- package/src/qt4-snaps/op-multiply-dayTimeDuration.snap +0 -1
- package/src/qt4-snaps/op-multiply-yearMonthDuration.snap +0 -1
- package/src/qt4-snaps/op-node-after.snap +0 -1
- package/src/qt4-snaps/op-node-before.snap +0 -1
- package/src/qt4-snaps/op-numeric-add.snap +0 -1
- package/src/qt4-snaps/op-numeric-divide.snap +0 -1
- package/src/qt4-snaps/op-numeric-equal.snap +0 -1
- package/src/qt4-snaps/op-numeric-greater-than.snap +0 -1
- package/src/qt4-snaps/op-numeric-integer-divide.snap +0 -1
- package/src/qt4-snaps/op-numeric-less-than.snap +0 -1
- package/src/qt4-snaps/op-numeric-mod.snap +0 -1
- package/src/qt4-snaps/op-numeric-multiply.snap +0 -1
- package/src/qt4-snaps/op-numeric-subtract.snap +0 -1
- package/src/qt4-snaps/op-numeric-unary-minus.snap +0 -1
- package/src/qt4-snaps/op-numeric-unary-plus.snap +0 -1
- package/src/qt4-snaps/op-same-key.snap +0 -1
- package/src/qt4-snaps/op-string-equal.snap +0 -1
- package/src/qt4-snaps/op-string-greater-than.snap +0 -1
- package/src/qt4-snaps/op-string-less-than.snap +0 -1
- package/src/qt4-snaps/op-subtract-dateTimes.snap +0 -1
- package/src/qt4-snaps/op-subtract-dates.snap +0 -1
- package/src/qt4-snaps/op-subtract-dayTimeDuration-from-date.snap +0 -1
- package/src/qt4-snaps/op-subtract-dayTimeDuration-from-dateTime.snap +0 -1
- package/src/qt4-snaps/op-subtract-dayTimeDuration-from-time.snap +0 -1
- package/src/qt4-snaps/op-subtract-dayTimeDurations.snap +0 -1
- package/src/qt4-snaps/op-subtract-times.snap +0 -1
- package/src/qt4-snaps/op-subtract-yearMonthDuration-from-date.snap +0 -1
- package/src/qt4-snaps/op-subtract-yearMonthDuration-from-dateTime.snap +0 -1
- package/src/qt4-snaps/op-subtract-yearMonthDurations.snap +0 -1
- package/src/qt4-snaps/op-time-equal.snap +0 -1
- package/src/qt4-snaps/op-time-greater-than.snap +0 -1
- package/src/qt4-snaps/op-time-less-than.snap +0 -1
- package/src/qt4-snaps/op-to.snap +0 -1
- package/src/qt4-snaps/op-union.snap +0 -1
- package/src/qt4-snaps/op-yearMonthDuration-greater-than.snap +0 -1
- package/src/qt4-snaps/op-yearMonthDuration-less-than.snap +0 -1
- package/src/qt4-snaps/prod-AllowingEmpty.snap +0 -1
- package/src/qt4-snaps/prod-Annotation.snap +0 -146
- package/src/qt4-snaps/prod-ArrayType.snap +0 -1
- package/src/qt4-snaps/prod-ArrowExpr.snap +0 -1
- package/src/qt4-snaps/prod-AxisStep.abbr.snap +0 -1
- package/src/qt4-snaps/prod-AxisStep.ancestor-or-self.snap +0 -1
- package/src/qt4-snaps/prod-AxisStep.ancestor.snap +0 -1
- package/src/qt4-snaps/prod-AxisStep.following-sibling.snap +0 -1
- package/src/qt4-snaps/prod-AxisStep.following.snap +0 -1
- package/src/qt4-snaps/prod-AxisStep.preceding-sibling.snap +0 -1
- package/src/qt4-snaps/prod-AxisStep.preceding.snap +0 -1
- package/src/qt4-snaps/prod-AxisStep.snap +0 -114
- package/src/qt4-snaps/prod-AxisStep.unabbr.snap +0 -1
- package/src/qt4-snaps/prod-BaseURIDecl.snap +0 -20
- package/src/qt4-snaps/prod-BoundarySpaceDecl.snap +0 -14
- package/src/qt4-snaps/prod-CastExpr.derived.snap +0 -1
- package/src/qt4-snaps/prod-CastExpr.snap +0 -218
- package/src/qt4-snaps/prod-CastableExpr.snap +0 -56
- package/src/qt4-snaps/prod-Comment.snap +0 -1
- package/src/qt4-snaps/prod-CompAttrConstructor.snap +0 -14
- package/src/qt4-snaps/prod-CompCommentConstructor.snap +0 -8
- package/src/qt4-snaps/prod-CompDocConstructor.snap +0 -1
- package/src/qt4-snaps/prod-CompElemConstructor.snap +0 -1
- package/src/qt4-snaps/prod-CompNamespaceConstructor.snap +0 -1
- package/src/qt4-snaps/prod-CompPIConstructor.snap +0 -1
- package/src/qt4-snaps/prod-CompTextConstructor.snap +0 -8
- package/src/qt4-snaps/prod-ConstructionDecl.snap +0 -14
- package/src/qt4-snaps/prod-ContextItemDecl.snap +0 -20
- package/src/qt4-snaps/prod-ContextItemExpr.snap +0 -1
- package/src/qt4-snaps/prod-CopyNamespacesDecl.snap +0 -14
- package/src/qt4-snaps/prod-CountClause.snap +0 -1
- package/src/qt4-snaps/prod-CurlyArrayConstructor.snap +0 -1
- package/src/qt4-snaps/prod-DecimalFormatDecl.snap +0 -92
- package/src/qt4-snaps/prod-DefaultCollationDecl.snap +0 -32
- package/src/qt4-snaps/prod-DefaultNamespaceDecl.snap +0 -92
- package/src/qt4-snaps/prod-DirAttributeList.snap +0 -58
- package/src/qt4-snaps/prod-DirElemConstructor.snap +0 -62
- package/src/qt4-snaps/prod-DirElemContent.namespace.snap +0 -176
- package/src/qt4-snaps/prod-DirElemContent.snap +0 -14
- package/src/qt4-snaps/prod-DirElemContent.whitespace.snap +0 -178
- package/src/qt4-snaps/prod-DirectConstructor.snap +0 -56
- package/src/qt4-snaps/prod-EQName.snap +0 -48
- package/src/qt4-snaps/prod-EmptyOrderDecl.snap +0 -14
- package/src/qt4-snaps/prod-ExtensionExpr.snap +0 -50
- package/src/qt4-snaps/prod-ForClause.snap +0 -158
- package/src/qt4-snaps/prod-FunctionCall.snap +0 -102
- package/src/qt4-snaps/prod-FunctionDecl.snap +0 -256
- package/src/qt4-snaps/prod-GeneralComp.eq.snap +0 -1
- package/src/qt4-snaps/prod-GeneralComp.ge.snap +0 -1
- package/src/qt4-snaps/prod-GeneralComp.gt.snap +0 -1
- package/src/qt4-snaps/prod-GeneralComp.le.snap +0 -1
- package/src/qt4-snaps/prod-GeneralComp.lt.snap +0 -1
- package/src/qt4-snaps/prod-GeneralComp.ne.snap +0 -1
- package/src/qt4-snaps/prod-GroupByClause.snap +0 -8
- package/src/qt4-snaps/prod-IfExpr.snap +0 -1
- package/src/qt4-snaps/prod-InlineFunctionExpr.snap +0 -14
- package/src/qt4-snaps/prod-InstanceofExpr.snap +0 -38
- package/src/qt4-snaps/prod-LetClause.snap +0 -40
- package/src/qt4-snaps/prod-Literal.snap +0 -68
- package/src/qt4-snaps/prod-Lookup.snap +0 -1
- package/src/qt4-snaps/prod-MapConstructor.snap +0 -1
- package/src/qt4-snaps/prod-MapType.snap +0 -8
- package/src/qt4-snaps/prod-ModuleImport.snap +0 -224
- package/src/qt4-snaps/prod-NameTest.snap +0 -164
- package/src/qt4-snaps/prod-NamedFunctionRef.snap +0 -1
- package/src/qt4-snaps/prod-NamespaceDecl.snap +0 -80
- package/src/qt4-snaps/prod-NodeTest.snap +0 -50
- package/src/qt4-snaps/prod-OptionDecl.serialization.snap +0 -14
- package/src/qt4-snaps/prod-OptionDecl.snap +0 -20
- package/src/qt4-snaps/prod-OrExpr.snap +0 -1
- package/src/qt4-snaps/prod-OrderByClause.snap +0 -20
- package/src/qt4-snaps/prod-OrderingModeDecl.snap +0 -14
- package/src/qt4-snaps/prod-ParenthesizedExpr.snap +0 -1
- package/src/qt4-snaps/prod-PathExpr.snap +0 -34
- package/src/qt4-snaps/prod-PositionalVar.snap +0 -26
- package/src/qt4-snaps/prod-Predicate.snap +0 -14
- package/src/qt4-snaps/prod-QuantifiedExpr.snap +0 -164
- package/src/qt4-snaps/prod-ReturnClause.snap +0 -8
- package/src/qt4-snaps/prod-SequenceType.snap +0 -1
- package/src/qt4-snaps/prod-SquareArrayConstructor.snap +0 -1
- package/src/qt4-snaps/prod-StepExpr.snap +0 -1
- package/src/qt4-snaps/prod-StringConstructor.snap +0 -26
- package/src/qt4-snaps/prod-SwitchExpr.snap +0 -1
- package/src/qt4-snaps/prod-TreatExpr.snap +0 -14
- package/src/qt4-snaps/prod-TryCatchExpr.snap +0 -38
- package/src/qt4-snaps/prod-TypeswitchExpr.snap +0 -50
- package/src/qt4-snaps/prod-UnaryLookup.snap +0 -1
- package/src/qt4-snaps/prod-UnorderedExpr.snap +0 -14
- package/src/qt4-snaps/prod-ValueComp.snap +0 -1
- package/src/qt4-snaps/prod-VarDecl.external.snap +0 -82
- package/src/qt4-snaps/prod-VarDecl.snap +0 -146
- package/src/qt4-snaps/prod-VarDefaultValue.snap +0 -8
- package/src/qt4-snaps/prod-VersionDecl.snap +0 -68
- package/src/qt4-snaps/prod-WhereClause.snap +0 -26
- package/src/qt4-snaps/prod-WindowClause.snap +0 -98
- package/src/qt4-snaps/ser-method-adaptive.snap +0 -1
- package/src/qt4-snaps/ser-method-html.snap +0 -1
- package/src/qt4-snaps/ser-method-xml.snap +0 -1
- package/src/qt4-snaps/upd-AdditionalTyping.snap +0 -1
- package/src/qt4-snaps/upd-AddressBookUpdates.snap +0 -1
- package/src/qt4-snaps/upd-AttrDataModelErrs.snap +0 -1
- package/src/qt4-snaps/upd-CommaExpression.snap +0 -1
- package/src/qt4-snaps/upd-Compatibility.snap +0 -1
- package/src/qt4-snaps/upd-ConditionalExpression.snap +0 -1
- package/src/qt4-snaps/upd-DeleteExpressions.snap +0 -1
- package/src/qt4-snaps/upd-FLWORExpression.snap +0 -1
- package/src/qt4-snaps/upd-FullAxis.snap +0 -1
- package/src/qt4-snaps/upd-FunctionCall.snap +0 -1
- package/src/qt4-snaps/upd-FunctionDeclaration.snap +0 -114
- package/src/qt4-snaps/upd-InsertExpressions.snap +0 -1
- package/src/qt4-snaps/upd-MinOther.snap +0 -1
- package/src/qt4-snaps/upd-NSBindingConflictErrs.snap +0 -1
- package/src/qt4-snaps/upd-NamespacesUpdates.snap +0 -1
- package/src/qt4-snaps/upd-NilUpdates.snap +0 -10
- package/src/qt4-snaps/upd-OtherExpressions.snap +0 -1
- package/src/qt4-snaps/upd-ParenthesizedExpressions.snap +0 -1
- package/src/qt4-snaps/upd-PartsUpdates.snap +0 -1
- package/src/qt4-snaps/upd-RelationalDataUpdates.snap +0 -1
- package/src/qt4-snaps/upd-RenameExpressions.snap +0 -1
- package/src/qt4-snaps/upd-ReplaceNode.snap +0 -1
- package/src/qt4-snaps/upd-ReplaceValue.snap +0 -1
- package/src/qt4-snaps/upd-RevalidationDeclarationLax.snap +0 -1
- package/src/qt4-snaps/upd-RevalidationDeclarationLaxUnsupported.snap +0 -1
- package/src/qt4-snaps/upd-RevalidationDeclarationSkip.snap +0 -10
- package/src/qt4-snaps/upd-RevalidationDeclarationSkipUnsupported.snap +0 -1
- package/src/qt4-snaps/upd-RevalidationDeclarationStrict.snap +0 -1
- package/src/qt4-snaps/upd-RevalidationDeclarationStrictUnsupported.snap +0 -1
- package/src/qt4-snaps/upd-RevalidationDeclarationStrictandSkip.snap +0 -1
- package/src/qt4-snaps/upd-SoapUpdates.snap +0 -1
- package/src/qt4-snaps/upd-TransformExpressions.snap +0 -1
- package/src/qt4-snaps/upd-TypeswitchExpression.snap +0 -1
- package/src/qt4-snaps/upd-VariableDeclaration.snap +0 -1
- package/src/qt4-snaps/upd-applyUpdates.snap +0 -1
- package/src/qt4-snaps/upd-fnput-document.snap +0 -1
- package/src/qt4-snaps/upd-mergeUpdates.snap +0 -1
- package/src/qt4-snaps/upd-propagateNamespace.snap +0 -1
- package/src/qt4-snaps/upd-setToUntyped.snap +0 -1
- package/src/qt4-snaps/xs-anyAtomicType.snap +0 -1
- package/src/qt4-snaps/xs-anySimpleType.snap +0 -8
- package/src/qt4-snaps/xs-anyURI.snap +0 -34
- package/src/qt4-snaps/xs-base64Binary.snap +0 -1
- package/src/qt4-snaps/xs-dateTimeStamp.snap +0 -1
- package/src/qt4-snaps/xs-double.snap +0 -1
- package/src/qt4-snaps/xs-error.snap +0 -10
- package/src/qt4-snaps/xs-float.snap +0 -1
- package/src/qt4-snaps/xs-hexBinary.snap +0 -1
- package/src/qt4-snaps/xs-normalizedString.snap +0 -1
- package/src/qt4-snaps/xs-notation.snap +0 -1
- package/src/qt4-snaps/xs-numeric.snap +0 -1
- package/src/qt4-snaps/xs-token.snap +0 -1
- package/src/runtimes.test.ts +0 -152
- package/src/typechecker.test.ts +0 -548
- package/src/unused-diagnostics.test.ts +0 -174
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,30 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.5.0](https://github.com/DrRataplan/xq-lsp/compare/lsp-v1.4.1...lsp-v1.5.0) (2026-06-18)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* **completion:** add function param completions with proper scope isolation ([dab41a3](https://github.com/DrRataplan/xq-lsp/commit/dab41a349ae1d387f1b32b530e476b1d30da8f07))
|
|
9
|
+
* **diagnostics:** report undeclared variable references as XPST0008 ([6ff1fa9](https://github.com/DrRataplan/xq-lsp/commit/6ff1fa9d5a9ecfb4d440119f3efa9e3f96204e05))
|
|
10
|
+
* eXist-db pre-declared namespaces suppress false XQST0081 diagnostics ([cd94650](https://github.com/DrRataplan/xq-lsp/commit/cd94650db968d727717de62e6a60411481a7fb16))
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Bug Fixes
|
|
14
|
+
|
|
15
|
+
* inject W3C predeclared namespaces in qt4-worker ([4c93ad6](https://github.com/DrRataplan/xq-lsp/commit/4c93ad677db0eb266e04f080ee1a1ad986dfc03a))
|
|
16
|
+
* **variable-diagnostics:** add explicit type annotation to fix TS build ([5867d7e](https://github.com/DrRataplan/xq-lsp/commit/5867d7ea549026cda98559b8d980f3bdf561f32f))
|
|
17
|
+
* **vscode:** make extension work by shimming import.meta.url ([fca848a](https://github.com/DrRataplan/xq-lsp/commit/fca848af51ee17a0dba8e7b95f4767c4d1ce974f))
|
|
18
|
+
|
|
19
|
+
## [1.4.1](https://github.com/DrRataplan/xq-lsp/compare/lsp-v1.4.0...lsp-v1.4.1) (2026-06-10)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
### Bug Fixes
|
|
23
|
+
|
|
24
|
+
* change the bin to the dist version ([e77944d](https://github.com/DrRataplan/xq-lsp/commit/e77944de7da857ac6ca9d3148b4a41abec175de4))
|
|
25
|
+
* do not package testing snapshots to npm ([9638734](https://github.com/DrRataplan/xq-lsp/commit/963873401f3e4d74a9338c387bcfc3c3564f4f0d))
|
|
26
|
+
* union multiple imports of modules ([1d77b65](https://github.com/DrRataplan/xq-lsp/commit/1d77b65b48933abfe20c6f0e9abd113c318f16b4))
|
|
27
|
+
|
|
3
28
|
## [1.4.0](https://github.com/DrRataplan/xq-lsp/compare/lsp-v1.3.0...lsp-v1.4.0) (2026-05-28)
|
|
4
29
|
|
|
5
30
|
|
package/README.md
CHANGED
|
@@ -59,24 +59,70 @@ For files with syntax errors (common while editing), the server falls back to re
|
|
|
59
59
|
|
|
60
60
|
Use the `lib` key to load built-in definitions for a specific runtime:
|
|
61
61
|
|
|
62
|
-
| Value
|
|
63
|
-
|
|
|
64
|
-
| `"fonto"`
|
|
62
|
+
| Value | Runtime |
|
|
63
|
+
| ----------- | ------------------------------------------------------------------------- |
|
|
64
|
+
| `"fonto"` | [Fonto XML editor](https://www.fontoxml.com/) — `fonto:*` functions |
|
|
65
|
+
| `"existdb"` | [eXist-db](https://exist-db.org/) — all extension module functions |
|
|
65
66
|
|
|
66
67
|
```xquery
|
|
67
|
-
map {"glob": "src/**/*.xq", "lib": "
|
|
68
|
+
map {"glob": "src/**/*.xq", "lib": "existdb"}
|
|
68
69
|
```
|
|
69
70
|
|
|
70
|
-
|
|
71
|
+
Multiple libs use an XPath sequence:
|
|
71
72
|
|
|
72
73
|
```xquery
|
|
73
|
-
|
|
74
|
+
map {"glob": "src/**/*.xq", "lib": ("fonto", "other")}
|
|
74
75
|
```
|
|
75
76
|
|
|
76
|
-
|
|
77
|
+
#### eXist-db runtime
|
|
78
|
+
|
|
79
|
+
The `"existdb"` runtime bundles stub definitions for every eXist-db extension
|
|
80
|
+
module (`util`, `xmldb`, `request`, `sm`, `ft`, `compression`, …).
|
|
81
|
+
|
|
82
|
+
eXist-db automatically pre-declares a large set of namespace prefixes — you can
|
|
83
|
+
call `util:log(…)`, `xmldb:store(…)`, `process:execute(…)`, etc. without any
|
|
84
|
+
`import module namespace` statement. When the `existdb` runtime is active the
|
|
85
|
+
server knows about these pre-declared prefixes and:
|
|
86
|
+
|
|
87
|
+
- **suppresses** false `XQST0081` ("namespace prefix not declared") diagnostics
|
|
88
|
+
for all pre-declared namespaces
|
|
89
|
+
- **provides completions, hover, and go-to-definition** for those modules
|
|
90
|
+
without requiring an explicit `import module namespace` in your source
|
|
91
|
+
|
|
92
|
+
If you do choose to write explicit imports (e.g. for editor portability), the
|
|
93
|
+
server handles those correctly too:
|
|
77
94
|
|
|
78
95
|
```xquery
|
|
79
|
-
|
|
96
|
+
(: explicit import — also fine :)
|
|
97
|
+
import module namespace util = "http://exist-db.org/xquery/util";
|
|
98
|
+
util:uuid()
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Modules that require explicit import even in eXist-db (e.g. `datetime`,
|
|
102
|
+
`httpclient`, `console`, `crypto`, `kwic`) still need a `import module
|
|
103
|
+
namespace` declaration; the server offers quick-fix code actions to insert it.
|
|
104
|
+
|
|
105
|
+
```xquery
|
|
106
|
+
(: lsp-config.xq in your project root :)
|
|
107
|
+
map {
|
|
108
|
+
"glob": "src/**/*.xq",
|
|
109
|
+
"lib": "existdb",
|
|
110
|
+
"import": map {
|
|
111
|
+
"generateLocationHints": false()
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
The `generateLocationHints: false()` setting prevents the server from appending
|
|
117
|
+
`at "…"` paths to generated imports, which eXist-db does not need (it resolves
|
|
118
|
+
modules by namespace URI).
|
|
119
|
+
|
|
120
|
+
#### Fonto runtime
|
|
121
|
+
|
|
122
|
+
Then import the namespace in your XQuery files as usual — the server resolves completions, hover, and go-to-definition against the bundled definitions:
|
|
123
|
+
|
|
124
|
+
```xquery
|
|
125
|
+
import module namespace fonto="http://www.fontoxml.com/functions";
|
|
80
126
|
```
|
|
81
127
|
|
|
82
128
|
### Location hints
|
|
@@ -14,4 +14,4 @@ export interface CompletionEntry {
|
|
|
14
14
|
isSnippet: boolean;
|
|
15
15
|
}
|
|
16
16
|
export declare function functionDoc(fn: FunctionSymbol): string;
|
|
17
|
-
export declare function getCompletions(ctx: CompletionContext, currentAnalysis: FileAnalysis, importedAnalyses: Map<string, FileAnalysis>, snippets?: boolean): CompletionEntry[];
|
|
17
|
+
export declare function getCompletions(ctx: CompletionContext, currentAnalysis: FileAnalysis, importedAnalyses: Map<string, FileAnalysis>, snippets?: boolean, lastValidAnalysis?: FileAnalysis): CompletionEntry[];
|
package/dist/completion-core.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { formatQName } from "./types.js";
|
|
2
|
-
import { resolvePrefix } from "./analyzer.js";
|
|
2
|
+
import { resolvePrefix, nodeStackAtOffset, findAll } from "./analyzer.js";
|
|
3
3
|
function parseToken(textBefore) {
|
|
4
4
|
const match = textBefore.match(/\$([\w:\-]*)$|(?:([\w\-]+):)([\w\-]*)$|([\w\-]+)$/);
|
|
5
5
|
if (!match)
|
|
@@ -41,6 +41,31 @@ function buildVariableEntry(v, filter) {
|
|
|
41
41
|
return null;
|
|
42
42
|
return { label, kind: "variable", insertText: label, isSnippet: false };
|
|
43
43
|
}
|
|
44
|
+
function buildParamEntry(p, filter) {
|
|
45
|
+
if (filter && !p.name.toLowerCase().startsWith(filter.toLowerCase()))
|
|
46
|
+
return null;
|
|
47
|
+
return {
|
|
48
|
+
label: `$${p.name}`,
|
|
49
|
+
kind: "variable",
|
|
50
|
+
detail: p.type ? `as ${p.type}` : undefined,
|
|
51
|
+
insertText: `$${p.name}`,
|
|
52
|
+
isSnippet: false,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
/** Find the function symbol enclosing the cursor using the AST. */
|
|
56
|
+
function findEnclosingFunction(ast, cursorOffset, functions) {
|
|
57
|
+
const stack = nodeStackAtOffset(ast, cursorOffset);
|
|
58
|
+
const bodyNode = stack.slice().reverse().find((n) => n.type === "FunctionBody");
|
|
59
|
+
if (!bodyNode || bodyNode.start === undefined || bodyNode.end == null)
|
|
60
|
+
return null;
|
|
61
|
+
const annotatedNode = stack.find((n) => n.type === "AnnotatedDecl");
|
|
62
|
+
if (!annotatedNode || annotatedNode.start === undefined)
|
|
63
|
+
return null;
|
|
64
|
+
const fn = functions.find((f) => f.sourceOffset === annotatedNode.start);
|
|
65
|
+
if (!fn)
|
|
66
|
+
return null;
|
|
67
|
+
return { fn, bodyStart: bodyNode.start, bodyEnd: bodyNode.end };
|
|
68
|
+
}
|
|
44
69
|
function buildFunctionEntry(fn, label, snippets) {
|
|
45
70
|
return {
|
|
46
71
|
label,
|
|
@@ -51,23 +76,63 @@ function buildFunctionEntry(fn, label, snippets) {
|
|
|
51
76
|
isSnippet: snippets && fn.params.length > 0,
|
|
52
77
|
};
|
|
53
78
|
}
|
|
54
|
-
export function getCompletions(ctx, currentAnalysis, importedAnalyses, snippets = false) {
|
|
79
|
+
export function getCompletions(ctx, currentAnalysis, importedAnalyses, snippets = false, lastValidAnalysis) {
|
|
55
80
|
const token = parseToken(ctx.textBeforeCursor);
|
|
56
81
|
const items = [];
|
|
57
82
|
if (token.kind === "variable") {
|
|
58
83
|
const filter = token.prefix;
|
|
84
|
+
// Module-level variables are always visible
|
|
59
85
|
for (const v of currentAnalysis.moduleVariables) {
|
|
60
86
|
const e = buildVariableEntry(v, filter);
|
|
61
87
|
if (e)
|
|
62
88
|
items.push(e);
|
|
63
89
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
90
|
+
// Prefer the current AST; fall back to the last successfully-parsed one for scope analysis
|
|
91
|
+
const scopeAst = currentAnalysis.ast ?? lastValidAnalysis?.ast;
|
|
92
|
+
if (scopeAst) {
|
|
93
|
+
const enclosing = findEnclosingFunction(scopeAst, ctx.cursorOffset, currentAnalysis.functions);
|
|
94
|
+
if (enclosing) {
|
|
95
|
+
// Inside a function body: params + local bindings within this body before cursor
|
|
96
|
+
for (const p of enclosing.fn.params) {
|
|
97
|
+
const e = buildParamEntry(p, filter);
|
|
98
|
+
if (e)
|
|
99
|
+
items.push(e);
|
|
100
|
+
}
|
|
101
|
+
for (const v of currentAnalysis.localBindings) {
|
|
102
|
+
if (v.offset > enclosing.bodyStart && v.offset < ctx.cursorOffset && v.offset < enclosing.bodyEnd) {
|
|
103
|
+
const e = buildVariableEntry(v, filter);
|
|
104
|
+
if (e)
|
|
105
|
+
items.push(e);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
// In query body: exclude local bindings that live inside function bodies
|
|
111
|
+
const functionBodyRanges = findAll(scopeAst, "FunctionBody")
|
|
112
|
+
.filter((b) => b.start !== undefined && b.end != null)
|
|
113
|
+
.map((b) => ({ start: b.start, end: b.end }));
|
|
114
|
+
for (const v of currentAnalysis.localBindings) {
|
|
115
|
+
if (v.offset >= ctx.cursorOffset)
|
|
116
|
+
continue;
|
|
117
|
+
if (functionBodyRanges.some((r) => v.offset >= r.start && v.offset <= r.end))
|
|
118
|
+
continue;
|
|
119
|
+
const e = buildVariableEntry(v, filter);
|
|
120
|
+
if (e)
|
|
121
|
+
items.push(e);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
// No AST ever available: simple offset-based filter, no function-param completions
|
|
127
|
+
for (const v of currentAnalysis.localBindings) {
|
|
128
|
+
if (v.offset < ctx.cursorOffset) {
|
|
129
|
+
const e = buildVariableEntry(v, filter);
|
|
130
|
+
if (e)
|
|
131
|
+
items.push(e);
|
|
132
|
+
}
|
|
69
133
|
}
|
|
70
134
|
}
|
|
135
|
+
// Imported module-level variables are always visible
|
|
71
136
|
for (const analysis of importedAnalyses.values()) {
|
|
72
137
|
for (const v of analysis.moduleVariables) {
|
|
73
138
|
const e = buildVariableEntry(v, filter);
|
package/dist/runtimes.d.ts
CHANGED
|
@@ -1,2 +1,15 @@
|
|
|
1
|
-
import type { FileAnalysis } from "./types.ts";
|
|
1
|
+
import type { FileAnalysis, NamespaceDecl } from "./types.ts";
|
|
2
|
+
/**
|
|
3
|
+
* Returns all namespace declarations that are in scope without any explicit
|
|
4
|
+
* `import module namespace` or `declare namespace` statement. Includes the
|
|
5
|
+
* standard XQuery 3.1 predeclared namespaces (math, map, array) and any
|
|
6
|
+
* runtime-specific additions (e.g. eXist-db's util, xmldb, …).
|
|
7
|
+
*/
|
|
8
|
+
export declare function getRuntimePredeclaredNamespaces(runtimes: string[]): NamespaceDecl[];
|
|
9
|
+
/**
|
|
10
|
+
* Return a copy of `analysis` with the given predeclared namespace declarations
|
|
11
|
+
* merged into `namespaceDecls`. Prefixes already visible via imports, the
|
|
12
|
+
* module's own prefix, or an existing `declare namespace` are skipped.
|
|
13
|
+
*/
|
|
14
|
+
export declare function withPredeclaredNs(analysis: FileAnalysis, predeclaredNs: NamespaceDecl[]): FileAnalysis;
|
|
2
15
|
export declare function getRuntimeAnalyses(runtimes: string[]): FileAnalysis[];
|
package/dist/runtimes.js
CHANGED
|
@@ -3,6 +3,14 @@ import * as fs from "fs";
|
|
|
3
3
|
import { fileURLToPath } from "url";
|
|
4
4
|
import { analyze } from "./analyzer.js";
|
|
5
5
|
const runtimesDir = path.join(path.dirname(fileURLToPath(import.meta.url)), "runtimes");
|
|
6
|
+
function toNamespaceDecls(raw) {
|
|
7
|
+
return Object.entries(raw).map(([prefix, namespaceUri]) => ({ prefix, namespaceUri }));
|
|
8
|
+
}
|
|
9
|
+
// Lazily loaded predeclared namespace list for the existdb runtime.
|
|
10
|
+
let existdbPredeclared = null;
|
|
11
|
+
function loadExistdbPredeclared() {
|
|
12
|
+
return (existdbPredeclared ??= toNamespaceDecls(JSON.parse(fs.readFileSync(path.join(runtimesDir, "existdb", "predeclared-namespaces.json"), "utf-8"))));
|
|
13
|
+
}
|
|
6
14
|
const RUNTIME_FILES = {
|
|
7
15
|
fonto: [path.join(runtimesDir, "fonto", "fonto.xq")],
|
|
8
16
|
existdb: [
|
|
@@ -39,6 +47,35 @@ const RUNTIME_FILES = {
|
|
|
39
47
|
],
|
|
40
48
|
};
|
|
41
49
|
const runtimeAnalysisCache = new Map();
|
|
50
|
+
/**
|
|
51
|
+
* Returns all namespace declarations that are in scope without any explicit
|
|
52
|
+
* `import module namespace` or `declare namespace` statement. Includes the
|
|
53
|
+
* standard XQuery 3.1 predeclared namespaces (math, map, array) and any
|
|
54
|
+
* runtime-specific additions (e.g. eXist-db's util, xmldb, …).
|
|
55
|
+
*/
|
|
56
|
+
export function getRuntimePredeclaredNamespaces(runtimes) {
|
|
57
|
+
if (!runtimes.includes("existdb"))
|
|
58
|
+
return [];
|
|
59
|
+
return loadExistdbPredeclared();
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Return a copy of `analysis` with the given predeclared namespace declarations
|
|
63
|
+
* merged into `namespaceDecls`. Prefixes already visible via imports, the
|
|
64
|
+
* module's own prefix, or an existing `declare namespace` are skipped.
|
|
65
|
+
*/
|
|
66
|
+
export function withPredeclaredNs(analysis, predeclaredNs) {
|
|
67
|
+
if (predeclaredNs.length === 0)
|
|
68
|
+
return analysis;
|
|
69
|
+
const known = new Set([
|
|
70
|
+
...analysis.imports.map((i) => i.prefix),
|
|
71
|
+
...analysis.namespaceDecls.map((nd) => nd.prefix),
|
|
72
|
+
...(analysis.modulePrefix ? [analysis.modulePrefix] : []),
|
|
73
|
+
]);
|
|
74
|
+
const toAdd = predeclaredNs.filter((nd) => !known.has(nd.prefix));
|
|
75
|
+
if (toAdd.length === 0)
|
|
76
|
+
return analysis;
|
|
77
|
+
return { ...analysis, namespaceDecls: [...analysis.namespaceDecls, ...toAdd] };
|
|
78
|
+
}
|
|
42
79
|
export function getRuntimeAnalyses(runtimes) {
|
|
43
80
|
const results = [];
|
|
44
81
|
for (const runtime of runtimes) {
|
package/dist/server.js
CHANGED
|
@@ -14,11 +14,13 @@ import { checkFunctionCalls } from "./functioncall-diagnostics.js";
|
|
|
14
14
|
import { findConfig, expandGlobs } from "./config.js";
|
|
15
15
|
import { findUndeclaredPrefixUsages, findImportInsertPosition, findDeclareNsInsertPosition, } from "./namespace-diagnostics.js";
|
|
16
16
|
import { checkUnused } from "./unused-diagnostics.js";
|
|
17
|
+
import { checkUndeclaredVariables } from "./variable-diagnostics.js";
|
|
17
18
|
import { checkContextItemUsage } from "./context-item-diagnostics.js";
|
|
18
|
-
import { getRuntimeAnalyses } from "./runtimes.js";
|
|
19
|
+
import { getRuntimeAnalyses, getRuntimePredeclaredNamespaces, withPredeclaredNs } from "./runtimes.js";
|
|
19
20
|
const connection = createConnection(ProposedFeatures.all);
|
|
20
21
|
const documents = new TextDocuments(TextDocument);
|
|
21
22
|
const analysisCache = new Map();
|
|
23
|
+
const lastValidAnalysisCache = new Map(); // last AST-path analysis per URI
|
|
22
24
|
const typeErrorCache = new Map();
|
|
23
25
|
// Glob analyses keyed by config directory, then by module namespace URI.
|
|
24
26
|
// Populated lazily on the first request for a given config root.
|
|
@@ -26,11 +28,15 @@ const globAnalysesByConfigDir = new Map();
|
|
|
26
28
|
function analyzeDocument(doc) {
|
|
27
29
|
const { analysis } = analyzeWithAst(doc.getText(), doc.uri);
|
|
28
30
|
analysisCache.set(doc.uri, analysis);
|
|
31
|
+
if (analysis.usedAstPath)
|
|
32
|
+
lastValidAnalysisCache.set(doc.uri, analysis);
|
|
29
33
|
return analysis;
|
|
30
34
|
}
|
|
31
35
|
function analyzeDocumentFull(doc) {
|
|
32
36
|
const { analysis, ast, parseError } = analyzeWithAst(doc.getText(), doc.uri);
|
|
33
37
|
analysisCache.set(doc.uri, analysis);
|
|
38
|
+
if (analysis.usedAstPath)
|
|
39
|
+
lastValidAnalysisCache.set(doc.uri, analysis);
|
|
34
40
|
return { analysis, parseDiagnostics: buildParseDiagnostics(parseError), hasAst: ast !== null, ast };
|
|
35
41
|
}
|
|
36
42
|
function buildParseDiagnostics(parseError) {
|
|
@@ -77,6 +83,13 @@ function getImportedAnalysis(importUri) {
|
|
|
77
83
|
return null;
|
|
78
84
|
}
|
|
79
85
|
}
|
|
86
|
+
function mergeAnalyses(a, b) {
|
|
87
|
+
return {
|
|
88
|
+
...a,
|
|
89
|
+
functions: [...a.functions, ...b.functions],
|
|
90
|
+
moduleVariables: [...a.moduleVariables, ...b.moduleVariables],
|
|
91
|
+
};
|
|
92
|
+
}
|
|
80
93
|
function getGlobAnalyses(currentUri) {
|
|
81
94
|
const found = findConfig(currentUri);
|
|
82
95
|
if (!found)
|
|
@@ -90,23 +103,28 @@ function getGlobAnalyses(currentUri) {
|
|
|
90
103
|
for (const filePath of expandGlobs(config.globs, configDir)) {
|
|
91
104
|
const fileUri = pathToFileURL(filePath).toString();
|
|
92
105
|
const imported = getImportedAnalysis(fileUri);
|
|
93
|
-
if (imported?.moduleNamespaceUri)
|
|
94
|
-
|
|
106
|
+
if (!imported?.moduleNamespaceUri)
|
|
107
|
+
continue;
|
|
108
|
+
const existing = byNamespace.get(imported.moduleNamespaceUri);
|
|
109
|
+
byNamespace.set(imported.moduleNamespaceUri, existing ? mergeAnalyses(existing, imported) : imported);
|
|
95
110
|
}
|
|
96
111
|
globAnalysesByConfigDir.set(configDir, byNamespace);
|
|
97
112
|
return { byNamespace, lib };
|
|
98
113
|
}
|
|
99
|
-
function
|
|
114
|
+
function resolveContext(currentUri, rawAnalysis) {
|
|
100
115
|
const result = new Map();
|
|
101
116
|
result.set("builtin:fn", getBuiltins());
|
|
102
117
|
const { byNamespace: globAnalyses, lib } = getGlobAnalyses(currentUri);
|
|
118
|
+
const predeclaredNs = getRuntimePredeclaredNamespaces(lib);
|
|
119
|
+
const analysis = withPredeclaredNs(rawAnalysis, predeclaredNs);
|
|
103
120
|
const runtimeByNamespace = new Map();
|
|
104
121
|
for (const runtimeAnalysis of getRuntimeAnalyses(lib)) {
|
|
105
122
|
if (runtimeAnalysis.moduleNamespaceUri) {
|
|
106
|
-
runtimeByNamespace.
|
|
123
|
+
const existing = runtimeByNamespace.get(runtimeAnalysis.moduleNamespaceUri);
|
|
124
|
+
runtimeByNamespace.set(runtimeAnalysis.moduleNamespaceUri, existing ? mergeAnalyses(existing, runtimeAnalysis) : runtimeAnalysis);
|
|
107
125
|
}
|
|
108
126
|
}
|
|
109
|
-
for (const imp of
|
|
127
|
+
for (const imp of rawAnalysis.imports) {
|
|
110
128
|
if (imp.atPath) {
|
|
111
129
|
const uri = resolveImportUri(currentUri, imp.atPath);
|
|
112
130
|
const imported = getImportedAnalysis(uri);
|
|
@@ -120,7 +138,15 @@ function resolveImports(currentUri, analysis) {
|
|
|
120
138
|
result.set(imp.namespaceUri, imported);
|
|
121
139
|
}
|
|
122
140
|
}
|
|
123
|
-
|
|
141
|
+
// Add pre-declared runtime analyses directly — available without explicit import
|
|
142
|
+
for (const nd of predeclaredNs) {
|
|
143
|
+
if (!result.has(nd.namespaceUri)) {
|
|
144
|
+
const runtimeAnalysis = runtimeByNamespace.get(nd.namespaceUri);
|
|
145
|
+
if (runtimeAnalysis)
|
|
146
|
+
result.set(nd.namespaceUri, runtimeAnalysis);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return { analysis, imported: result };
|
|
124
150
|
}
|
|
125
151
|
// ── LSP lifecycle ────────────────────────────────────────────────────────────
|
|
126
152
|
let snippetSupport = false;
|
|
@@ -145,12 +171,12 @@ connection.onInitialize((params) => {
|
|
|
145
171
|
});
|
|
146
172
|
documents.onDidChangeContent((change) => {
|
|
147
173
|
const doc = change.document;
|
|
148
|
-
const { analysis, parseDiagnostics: parseDiags, hasAst, ast } = analyzeDocumentFull(doc);
|
|
174
|
+
const { analysis: rawAnalysis, parseDiagnostics: parseDiags, hasAst, ast } = analyzeDocumentFull(doc);
|
|
175
|
+
const { analysis, imported: resolvedImported } = resolveContext(doc.uri, rawAnalysis);
|
|
149
176
|
if (hasAst && ast !== null) {
|
|
150
|
-
const imported = resolveImports(doc.uri, analysis);
|
|
151
177
|
typeErrorCache.set(doc.uri, [
|
|
152
|
-
...checkTypes(ast, doc.getText(), analysis,
|
|
153
|
-
...checkFunctionCalls(ast, analysis,
|
|
178
|
+
...checkTypes(ast, doc.getText(), analysis, resolvedImported),
|
|
179
|
+
...checkFunctionCalls(ast, analysis, resolvedImported),
|
|
154
180
|
]);
|
|
155
181
|
}
|
|
156
182
|
const typeDiags = (typeErrorCache.get(doc.uri) ?? []).map((td) => ({
|
|
@@ -199,34 +225,48 @@ documents.onDidChangeContent((change) => {
|
|
|
199
225
|
code: cd.code,
|
|
200
226
|
source: "xquery-lsp",
|
|
201
227
|
}));
|
|
228
|
+
const undeclaredVarDiagRaw = hasAst && ast !== null ? checkUndeclaredVariables(ast, analysis, resolvedImported) : [];
|
|
229
|
+
const undeclaredVarDiags = undeclaredVarDiagRaw.map((d) => ({
|
|
230
|
+
severity: DiagnosticSeverity.Error,
|
|
231
|
+
range: {
|
|
232
|
+
start: doc.positionAt(d.offset),
|
|
233
|
+
end: doc.positionAt(d.offset + d.length),
|
|
234
|
+
},
|
|
235
|
+
message: d.message,
|
|
236
|
+
code: d.code,
|
|
237
|
+
source: "xquery-lsp",
|
|
238
|
+
}));
|
|
202
239
|
connection.sendDiagnostics({
|
|
203
240
|
uri: doc.uri,
|
|
204
|
-
diagnostics: [
|
|
241
|
+
diagnostics: [
|
|
242
|
+
...parseDiags, ...typeDiags, ...nsDiags, ...unusedDiags, ...contextItemDiags,
|
|
243
|
+
...undeclaredVarDiags,
|
|
244
|
+
],
|
|
205
245
|
});
|
|
206
246
|
});
|
|
207
247
|
connection.onCompletion((params) => {
|
|
208
248
|
const doc = documents.get(params.textDocument.uri);
|
|
209
249
|
if (!doc)
|
|
210
250
|
return [];
|
|
211
|
-
const
|
|
212
|
-
const imported =
|
|
251
|
+
const rawAnalysis = analysisCache.get(doc.uri) ?? analyzeDocument(doc);
|
|
252
|
+
const { analysis, imported } = resolveContext(doc.uri, rawAnalysis);
|
|
213
253
|
const offset = doc.offsetAt(params.position);
|
|
214
|
-
return getCompletions({ textBeforeCursor: doc.getText().slice(0, offset), cursorOffset: offset }, analysis, imported, snippetSupport);
|
|
254
|
+
return getCompletions({ textBeforeCursor: doc.getText().slice(0, offset), cursorOffset: offset }, analysis, imported, snippetSupport, lastValidAnalysisCache.get(doc.uri));
|
|
215
255
|
});
|
|
216
256
|
connection.onHover((params) => {
|
|
217
257
|
const doc = documents.get(params.textDocument.uri);
|
|
218
258
|
if (!doc)
|
|
219
259
|
return null;
|
|
220
|
-
const
|
|
221
|
-
const imported =
|
|
260
|
+
const rawAnalysis = analysisCache.get(doc.uri) ?? analyzeDocument(doc);
|
|
261
|
+
const { analysis, imported } = resolveContext(doc.uri, rawAnalysis);
|
|
222
262
|
return getHover(doc, doc.offsetAt(params.position), analysis, imported);
|
|
223
263
|
});
|
|
224
264
|
connection.onSignatureHelp((params) => {
|
|
225
265
|
const doc = documents.get(params.textDocument.uri);
|
|
226
266
|
if (!doc)
|
|
227
267
|
return null;
|
|
228
|
-
const
|
|
229
|
-
const imported =
|
|
268
|
+
const rawAnalysis = analysisCache.get(doc.uri) ?? analyzeDocument(doc);
|
|
269
|
+
const { analysis, imported } = resolveContext(doc.uri, rawAnalysis);
|
|
230
270
|
return getSignatureHelp(doc, doc.offsetAt(params.position), analysis, imported);
|
|
231
271
|
});
|
|
232
272
|
connection.onDocumentSymbol((params) => {
|
|
@@ -240,8 +280,8 @@ connection.onDefinition((params) => {
|
|
|
240
280
|
const doc = documents.get(params.textDocument.uri);
|
|
241
281
|
if (!doc)
|
|
242
282
|
return null;
|
|
243
|
-
const
|
|
244
|
-
const imported =
|
|
283
|
+
const rawAnalysis = analysisCache.get(doc.uri) ?? analyzeDocument(doc);
|
|
284
|
+
const { analysis, imported } = resolveContext(doc.uri, rawAnalysis);
|
|
245
285
|
return getDefinition(doc, doc.offsetAt(params.position), analysis, imported, (atPath) => resolveImportUri(doc.uri, atPath));
|
|
246
286
|
});
|
|
247
287
|
connection.onCodeAction((params) => {
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Node } from "xq-parser";
|
|
2
|
+
import type { FileAnalysis } from "./types.ts";
|
|
3
|
+
export interface UndeclaredVariableDiagnostic {
|
|
4
|
+
message: string;
|
|
5
|
+
code: "XPST0008";
|
|
6
|
+
offset: number;
|
|
7
|
+
length: number;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Walk the AST and report every variable reference that cannot be resolved
|
|
11
|
+
* to a declared variable: function parameter, let/for binding, module-level
|
|
12
|
+
* `declare variable`, or an imported module variable.
|
|
13
|
+
*
|
|
14
|
+
* Variables whose namespace prefix is undeclared are skipped — XQST0081
|
|
15
|
+
* already fires for those, and adding XPST0008 on top would be noisy.
|
|
16
|
+
* Variables inside catch clauses are also skipped since the implicit
|
|
17
|
+
* `$err:*` bindings are not represented in the AST.
|
|
18
|
+
*/
|
|
19
|
+
export declare function checkUndeclaredVariables(ast: Node, analysis: FileAnalysis, importedAnalyses: Map<string, FileAnalysis>): UndeclaredVariableDiagnostic[];
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { qnameKey } from "./types.js";
|
|
2
|
+
import { isTerminal, directChildOf, directChildrenOf } from "./analyzer.js";
|
|
3
|
+
import { asFunctionDecl, asVarRef, asVarName, asBinding } from "./ast-nodes.js";
|
|
4
|
+
// ── Scope stack ───────────────────────────────────────────────────────────────
|
|
5
|
+
class ScopeStack {
|
|
6
|
+
frames = [];
|
|
7
|
+
push() { this.frames.push(new Set()); }
|
|
8
|
+
pop() { this.frames.pop(); }
|
|
9
|
+
add(key) { this.frames[this.frames.length - 1]?.add(key); }
|
|
10
|
+
has(key) { return this.frames.some((f) => f.has(key)); }
|
|
11
|
+
}
|
|
12
|
+
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
13
|
+
function isKnown(qname, scope, moduleVarKeys) {
|
|
14
|
+
// Variables with an undeclared-prefix URI are already caught by XQST0081; skip here.
|
|
15
|
+
if (qname.namespaceUri.startsWith("urn:xq-lsp:undeclared:"))
|
|
16
|
+
return true;
|
|
17
|
+
return scope.has(qnameKey(qname)) || moduleVarKeys.has(qnameKey(qname));
|
|
18
|
+
}
|
|
19
|
+
function reportVarRef(node, qname, out) {
|
|
20
|
+
const name = qname.prefix ? `${qname.prefix}:${qname.localName}` : qname.localName;
|
|
21
|
+
out.push({
|
|
22
|
+
message: `Variable '$${name}' is not declared`,
|
|
23
|
+
code: "XPST0008",
|
|
24
|
+
offset: node.start ?? 0,
|
|
25
|
+
length: name.length + 1, // +1 for "$"
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
function addBinding(bindingNode, scope, analysis, moduleVarKeys, out) {
|
|
29
|
+
const b = asBinding(bindingNode, analysis);
|
|
30
|
+
if (!b)
|
|
31
|
+
return;
|
|
32
|
+
if (b.initExpr)
|
|
33
|
+
walk(b.initExpr, scope, analysis, moduleVarKeys, out);
|
|
34
|
+
scope.add(qnameKey(b.qname));
|
|
35
|
+
if (b.positionalVar)
|
|
36
|
+
scope.add(qnameKey(b.positionalVar.qname));
|
|
37
|
+
}
|
|
38
|
+
// ── Scope-aware AST walker ────────────────────────────────────────────────────
|
|
39
|
+
function walk(node, scope, analysis, moduleVarKeys, out) {
|
|
40
|
+
if (isTerminal(node))
|
|
41
|
+
return;
|
|
42
|
+
const { children } = node;
|
|
43
|
+
switch (node.type) {
|
|
44
|
+
case "AnnotatedDecl": {
|
|
45
|
+
const fnDeclNode = directChildOf(node, "FunctionDecl");
|
|
46
|
+
const fn = fnDeclNode ? asFunctionDecl(fnDeclNode, analysis) : null;
|
|
47
|
+
if (fn) {
|
|
48
|
+
scope.push();
|
|
49
|
+
for (const p of fn.params)
|
|
50
|
+
scope.add(qnameKey(p.qname));
|
|
51
|
+
if (fn.body)
|
|
52
|
+
walk(fn.body, scope, analysis, moduleVarKeys, out);
|
|
53
|
+
scope.pop();
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
// Module variable declaration: walk the initializer expression only
|
|
57
|
+
const varDecl = directChildOf(node, "VarDecl");
|
|
58
|
+
if (varDecl && !isTerminal(varDecl)) {
|
|
59
|
+
for (const child of varDecl.children)
|
|
60
|
+
walk(child, scope, analysis, moduleVarKeys, out);
|
|
61
|
+
}
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
case "FLWORExpr": {
|
|
65
|
+
scope.push();
|
|
66
|
+
for (const child of children) {
|
|
67
|
+
if (isTerminal(child))
|
|
68
|
+
continue;
|
|
69
|
+
// Unwrap InitialClause/IntermediateClause wrappers (may be multiply nested)
|
|
70
|
+
let clause = child;
|
|
71
|
+
while (!isTerminal(clause) && (clause.type === "InitialClause" || clause.type === "IntermediateClause")) {
|
|
72
|
+
const inner = clause.children.find((c) => !isTerminal(c));
|
|
73
|
+
if (!inner)
|
|
74
|
+
break;
|
|
75
|
+
clause = inner;
|
|
76
|
+
}
|
|
77
|
+
switch (clause.type) {
|
|
78
|
+
case "ForClause":
|
|
79
|
+
for (const b of directChildrenOf(clause, "ForBinding"))
|
|
80
|
+
addBinding(b, scope, analysis, moduleVarKeys, out);
|
|
81
|
+
break;
|
|
82
|
+
case "LetClause":
|
|
83
|
+
for (const b of directChildrenOf(clause, "LetBinding"))
|
|
84
|
+
addBinding(b, scope, analysis, moduleVarKeys, out);
|
|
85
|
+
break;
|
|
86
|
+
case "CountClause":
|
|
87
|
+
addBinding(clause, scope, analysis, moduleVarKeys, out);
|
|
88
|
+
break;
|
|
89
|
+
case "GroupByClause": {
|
|
90
|
+
const specList = directChildOf(clause, "GroupingSpecList");
|
|
91
|
+
for (const spec of specList ? directChildrenOf(specList, "GroupingSpec") : [])
|
|
92
|
+
addBinding(spec, scope, analysis, moduleVarKeys, out);
|
|
93
|
+
break;
|
|
94
|
+
}
|
|
95
|
+
case "WindowClause": {
|
|
96
|
+
const inner = directChildOf(clause, "TumblingWindowClause") ??
|
|
97
|
+
directChildOf(clause, "SlidingWindowClause");
|
|
98
|
+
if (inner)
|
|
99
|
+
addBinding(inner, scope, analysis, moduleVarKeys, out);
|
|
100
|
+
for (const c of clause.children)
|
|
101
|
+
if (!isTerminal(c))
|
|
102
|
+
walk(c, scope, analysis, moduleVarKeys, out);
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
default:
|
|
106
|
+
walk(clause, scope, analysis, moduleVarKeys, out);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
scope.pop();
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
case "QuantifiedExpr": {
|
|
113
|
+
scope.push();
|
|
114
|
+
for (const child of children) {
|
|
115
|
+
if (isTerminal(child))
|
|
116
|
+
continue;
|
|
117
|
+
if (child.type === "VarName") {
|
|
118
|
+
const qname = asVarName(child, analysis);
|
|
119
|
+
if (qname)
|
|
120
|
+
scope.add(qnameKey(qname));
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
walk(child, scope, analysis, moduleVarKeys, out);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
scope.pop();
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
case "CatchClause":
|
|
130
|
+
// Skip: implicit $err:* variables inside catch clauses are not tracked in the AST.
|
|
131
|
+
return;
|
|
132
|
+
case "VarRef": {
|
|
133
|
+
const qname = asVarRef(node, analysis);
|
|
134
|
+
if (qname && !isKnown(qname, scope, moduleVarKeys))
|
|
135
|
+
reportVarRef(node, qname, out);
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
default:
|
|
139
|
+
for (const child of children)
|
|
140
|
+
walk(child, scope, analysis, moduleVarKeys, out);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
// ── Public entry point ────────────────────────────────────────────────────────
|
|
144
|
+
/**
|
|
145
|
+
* Walk the AST and report every variable reference that cannot be resolved
|
|
146
|
+
* to a declared variable: function parameter, let/for binding, module-level
|
|
147
|
+
* `declare variable`, or an imported module variable.
|
|
148
|
+
*
|
|
149
|
+
* Variables whose namespace prefix is undeclared are skipped — XQST0081
|
|
150
|
+
* already fires for those, and adding XPST0008 on top would be noisy.
|
|
151
|
+
* Variables inside catch clauses are also skipped since the implicit
|
|
152
|
+
* `$err:*` bindings are not represented in the AST.
|
|
153
|
+
*/
|
|
154
|
+
export function checkUndeclaredVariables(ast, analysis, importedAnalyses) {
|
|
155
|
+
const moduleVarKeys = new Set();
|
|
156
|
+
for (const v of analysis.moduleVariables)
|
|
157
|
+
moduleVarKeys.add(qnameKey(v.qname));
|
|
158
|
+
for (const imported of importedAnalyses.values())
|
|
159
|
+
for (const v of imported.moduleVariables)
|
|
160
|
+
moduleVarKeys.add(qnameKey(v.qname));
|
|
161
|
+
const out = [];
|
|
162
|
+
walk(ast, new ScopeStack(), analysis, moduleVarKeys, out);
|
|
163
|
+
return out;
|
|
164
|
+
}
|