fix: Array move ownership, selfhost -g flags, LSP workspace/symbol
Session 36: prevent UAF when Array is moved into a struct field (Nexus headers), align selfhost cc flags with bootstrap debug/release, and add workspace/symbol search to bux-lsp 0.6. - Parser: zero headers after embedding so auto-drop is a no-op - Request_WantsKeepAlive uses RequestHeader_Get again safely - Selfhost: default -O0 -g; --release -O2 -DNDEBUG; BUX_CFLAGS - LSP: workspace/symbol + smoke; version 0.6.0
This commit is contained in:
+57
-2
@@ -7,6 +7,7 @@
|
||||
# Hover uses real bootstrap sema types when possible (globals + stdlib).
|
||||
# Locals are position-sensitive (scoped) and include inferred `let` types (v0.4.0).
|
||||
# v0.5.0: textDocument/references + rename (scoped locals + workspace globals).
|
||||
# v0.6.0: workspace/symbol search.
|
||||
|
||||
import std/[json, os, strutils, streams, tables, osproc, sequtils, sets]
|
||||
import lexer, parser, ast, sema, types, scope, source_location
|
||||
@@ -1556,6 +1557,56 @@ proc handleDocumentSymbol(stream: FileStream, id: JsonNode, paramsNode: JsonNode
|
||||
})
|
||||
sendResponse(stream, id, arr)
|
||||
|
||||
proc handleWorkspaceSymbol(stream: FileStream, id: JsonNode, paramsNode: JsonNode) =
|
||||
## workspace/symbol — fuzzy-ish substring filter over workspace + open docs.
|
||||
let query = if paramsNode.hasKey("query"): paramsNode["query"].getStr().toLowerAscii() else: ""
|
||||
var arr = newJArray()
|
||||
var seen = initHashSet[string]() # name@uri
|
||||
|
||||
proc maybeAdd(name, uri: string, info: SymbolInfo) =
|
||||
if query.len > 0 and query notin name.toLowerAscii() and
|
||||
query notin info.detail.toLowerAscii() and
|
||||
query notin info.kind.toLowerAscii():
|
||||
return
|
||||
let key = name & "@" & uri
|
||||
if seen.contains(key): return
|
||||
seen.incl(key)
|
||||
var item = %*{
|
||||
"name": name,
|
||||
"kind": symbolKindLsp(info.kind),
|
||||
"location": {
|
||||
"uri": uri,
|
||||
"range": {
|
||||
"start": {"line": info.line, "character": info.col},
|
||||
"end": {"line": info.line, "character": info.col + name.len}
|
||||
}
|
||||
}
|
||||
}
|
||||
if info.detail.len > 0:
|
||||
item["containerName"] = %info.kind
|
||||
arr.add(item)
|
||||
|
||||
# Open documents first (freshest)
|
||||
for uri, doc in documents.pairs:
|
||||
ensureAnalyzed(doc)
|
||||
for name, info in doc.symbols.pairs:
|
||||
maybeAdd(name, uri, info)
|
||||
|
||||
# Workspace index from scan
|
||||
for name, ws in workspaceSymbols.pairs:
|
||||
maybeAdd(name, ws.uri, ws.info)
|
||||
|
||||
# Cap result size for IDE responsiveness
|
||||
if arr.len > 200:
|
||||
var capped = newJArray()
|
||||
var i = 0
|
||||
while i < 200:
|
||||
capped.add(arr[i])
|
||||
inc i
|
||||
arr = capped
|
||||
|
||||
sendResponse(stream, id, arr)
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Main message loop
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -1578,9 +1629,10 @@ proc handleMessage(stream: FileStream, msg: JsonNode) =
|
||||
"hoverProvider": true,
|
||||
"documentSymbolProvider": true,
|
||||
"referencesProvider": true,
|
||||
"renameProvider": {"prepareProvider": true}
|
||||
"renameProvider": {"prepareProvider": true},
|
||||
"workspaceSymbolProvider": true
|
||||
},
|
||||
"serverInfo": {"name": "bux-lsp", "version": "0.5.0"}
|
||||
"serverInfo": {"name": "bux-lsp", "version": "0.6.0"}
|
||||
})
|
||||
if paramsNode.hasKey("rootPath") and paramsNode["rootPath"].kind != JNull:
|
||||
rootPath = paramsNode["rootPath"].getStr()
|
||||
@@ -1664,6 +1716,9 @@ proc handleMessage(stream: FileStream, msg: JsonNode) =
|
||||
of "textDocument/rename":
|
||||
handleRename(stream, id, paramsNode)
|
||||
|
||||
of "workspace/symbol":
|
||||
handleWorkspaceSymbol(stream, id, paramsNode)
|
||||
|
||||
else:
|
||||
if id != nil:
|
||||
sendError(stream, id, -32601, "method not found: " & methodName)
|
||||
|
||||
@@ -59,8 +59,8 @@ if ! grep -q 'renameProvider' "$TMP/out.txt"; then
|
||||
echo "FAIL: initialize missing renameProvider"
|
||||
exit 1
|
||||
fi
|
||||
if ! grep -q '0.5.0' "$TMP/out.txt"; then
|
||||
echo "WARN: version not 0.5.0 in initialize (may be ok)"
|
||||
if ! grep -qE '0\.[56]\.0' "$TMP/out.txt"; then
|
||||
echo "WARN: unexpected bux-lsp version in initialize"
|
||||
fi
|
||||
|
||||
# Rename workspace edit should propose "total"
|
||||
|
||||
Executable
+67
@@ -0,0 +1,67 @@
|
||||
#!/usr/bin/env bash
|
||||
# Smoke: workspace/symbol (bux-lsp 0.6.0)
|
||||
set -euo pipefail
|
||||
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
LSP="$ROOT/tools/bux-lsp"
|
||||
TMP=$(mktemp -d)
|
||||
trap 'rm -rf "$TMP"' EXIT
|
||||
|
||||
if [[ ! -x "$LSP" ]]; then
|
||||
(cd "$ROOT" && make lsp >/dev/null)
|
||||
fi
|
||||
|
||||
mkdir -p "$TMP/src"
|
||||
cat > "$TMP/src/Main.bux" <<'EOF'
|
||||
func Helper() -> int { return 1; }
|
||||
func Main() -> int {
|
||||
return Helper();
|
||||
}
|
||||
EOF
|
||||
cat > "$TMP/src/Util.bux" <<'EOF'
|
||||
func Util_Max(a: int, b: int) -> int {
|
||||
if a > b { return a; }
|
||||
return b;
|
||||
}
|
||||
EOF
|
||||
|
||||
rpc() {
|
||||
local body="$1"
|
||||
local len
|
||||
len=$(printf '%s' "$body" | wc -c)
|
||||
printf 'Content-Length: %s\r\n\r\n%s' "$len" "$body"
|
||||
}
|
||||
|
||||
CONTENT_JSON=$(python3 -c 'import json,sys; print(json.dumps(open(sys.argv[1]).read()))' "$TMP/src/Main.bux")
|
||||
URI="file://$TMP/src/Main.bux"
|
||||
|
||||
{
|
||||
rpc '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"capabilities":{},"rootUri":"file://'"$TMP"'"}}'
|
||||
rpc '{"jsonrpc":"2.0","method":"initialized","params":{}}'
|
||||
rpc '{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"'"$URI"'","languageId":"bux","version":1,"text":'"$CONTENT_JSON"'}}}'
|
||||
# workspace symbol query "Helper"
|
||||
rpc '{"jsonrpc":"2.0","id":2,"method":"workspace/symbol","params":{"query":"Helper"}}'
|
||||
# query "Util"
|
||||
rpc '{"jsonrpc":"2.0","id":3,"method":"workspace/symbol","params":{"query":"Util"}}'
|
||||
rpc '{"jsonrpc":"2.0","id":4,"method":"shutdown","params":null}'
|
||||
rpc '{"jsonrpc":"2.0","method":"exit","params":null}'
|
||||
} | "$LSP" 2>/dev/null | tr '\r' '\n' > "$TMP/out.txt"
|
||||
|
||||
if ! grep -q 'workspaceSymbolProvider' "$TMP/out.txt"; then
|
||||
echo "FAIL: missing workspaceSymbolProvider"
|
||||
cat "$TMP/out.txt"
|
||||
exit 1
|
||||
fi
|
||||
if ! grep -q '0.6.0' "$TMP/out.txt"; then
|
||||
echo "WARN: version not 0.6.0"
|
||||
fi
|
||||
if ! grep -q 'Helper' "$TMP/out.txt"; then
|
||||
echo "FAIL: workspace/symbol did not find Helper"
|
||||
cat "$TMP/out.txt"
|
||||
exit 1
|
||||
fi
|
||||
# Util may come from workspace scan of Util.bux
|
||||
if ! grep -q 'Util' "$TMP/out.txt"; then
|
||||
echo "WARN: Util not in workspace results (scan depth/path?)"
|
||||
fi
|
||||
|
||||
echo "PASS: LSP workspace/symbol smoke"
|
||||
Reference in New Issue
Block a user