chore: 版本号提升至 1.1.427,移除函数 hover 调试信息并更新 README

- 移除 hover 函数时附加的「函数调试信息」区块(位置/修饰符/泛型参数/
  参数/返回类型),hover 只保留签名;删除 _addViperFunctionDebugPart /
  _getViperParamDebugText / _getViperFunctionReturnType 与随之无用的
  ParamCategory / ParameterNode 导入
- viperFunctionHover.test.ts 改为断言「只显示签名、不再含调试信息字段」
- CHANGELOG 拆分:baec16e(1.1.426 发布)之后 14 个提交追加的 16 条
  未发布条目移入新增的 ## 1.1.427 段,## 1.1.426 段保留发布时内容
- 版本号 1.1.426 → 1.1.427(vipeye / vscode-vipeye / vipeye-internal 的
  package.json 与 package-lock.json 共 6 个文件)
- README(根 + 扩展)更新:修正指针隐式转换规则(同级 + pointee 兼容,
  结构体仅允许上转型),补充声明/注解修饰符、[SIZE]T 与 C 风格强转、
  virtual class 方法槽赋值、属性名补全/悬停、状态栏分析进度与 LOC 命令
This commit is contained in:
TermiNexus committed 2026-09-28 17:01:18 +08:00
1 parent bc500d060f
commit f31ff4904c
11 files changed
+52 -174

No files matched your search

+7 -3
View File
@@ -29,9 +29,12 @@ Vipeye exists to provide type checking, diagnostics and editor intelligence for
- Pointer and memory semantics: `CPtr[T]` / `rawptr[T]`, `c.Deref`, address-of, pointer arithmetic and indexing; a `__new__` returning a raw pointer is auto-wrapped into `CPtr[T]`.
- C declaration modifiers (`t.CInline`, `t.CStatic`, `t.CConst` …) are filtered out during type merging — only the underlying data type is kept.
- Declaration modifiers on functions, with `fn` / `fun` / `func` / `function` as aliases of `def`.
- Declaration and annotation modifiers: `const` / `volatile` type descriptors (`a: const int`, `p: const t.CPtr`), `static` (C internal linkage — a static symbol cannot be used from another module), `export` / `extern` variables (`extern` takes no initializer), `virtual` / `novirtual` classes and methods, and `__attribute__(...)` in both the decorator and the annotation-modifier spelling.
- `[SIZE]T` fixed-length array types (plus `[]T` and `typedef T1 = T2`), and C-style casts in every spelling TransPyC accepts: `(T)x`, `(T*)x`, `(T*)(x)` and `T*(x)`.
- A `virtual class` method slot can be filled with a plain function (`obj.method = func`), matching how the compiler writes the vtable slot: the assigned function's `self` is a generic pointer (`rawptr` / `t.CPtr`) or a pointer to that class / one of its base classes, and the rest of the signature must match (`*args` / `**kwargs` may differ).
- `struct` / `union` / `enum` / `rnum`, including nested `t.REnum` variants destructured by field order.
- Variadic `*args` bound as `va_list` in the function body (`va_arg`, and pointer passthrough such as `vsnprintf(buf, size, fmt, args)`).
- Implicit conversion rules: integer/float convert in all directions (safe narrowing, lossless widening); integer → float requires float width ≥ integer width; float → integer is rejected; pointers of the same level may convert, different levels may not; explicit casts are always allowed.
- Implicit conversion rules: integer/float convert in all directions (safe narrowing, lossless widening); integer → float requires float width ≥ integer width; float → integer is rejected; pointers convert only when the level matches **and** the pointee is compatible — either side being `void*`, identical pointees, and basic (numeric) pointees which follow the pointee's own conversion rules — while struct pointers must be the same type or an **upcast** (subclass → base class); different levels and unrelated structs are rejected; explicit casts are always allowed.
- Global/local variable rules: reading a global never errors; assigning an undeclared global without `global` does; a local redeclaration shadows the global; and so on.
- Type representations: `bool` / `t.CBool` map to `i1`; `str` / `bytes` are `i8*`, while `str | t.CPtr` / `bytes | t.CPtr` are `i8**`.
- PEP 695 syntax is pre-processed for Viper: type parameter lists are stripped and unknown `T | t.CPtr` unions are folded to `i8*`.
@@ -39,9 +42,10 @@ Vipeye exists to provide type checking, diagnostics and editor intelligence for
### Editor features
- Diagnostics with human-readable messages, plus Viper-tuned semantic highlighting (keywords, decorators, `t.CDefine` constants, and undefined symbols rendered in white).
- Completion, hover, go-to-definition, find references, rename, document symbols, and Organize Imports.
- Diagnostics with human-readable messages, plus Viper-tuned semantic highlighting (keywords, storage/type modifiers, decorators, `t.CDefine` constants, `__attribute__(...)` attribute names, and undefined symbols rendered in white).
- Completion, hover, go-to-definition, find references, rename, document symbols, and Organize Imports. Inside `__attribute__(...)`, both completion and hover list the available attribute names (including ones the compiler supports but that the `attr` stub does not declare).
- Activity-bar UI (Project / Toolchain / Settings), `.vpj` project files with a dedicated custom editor, and a status-bar compiler indicator.
- Analysis progress in the status bar (the file being analyzed, `N / M`, percentage) while the workspace is being checked, and a **Count Project LOC** report (KPIs, per-language chart, detail table) for `.vpj` projects.
- Import resolution over `VIPER_HOME` (stdlib), `viperconfig.json` extra paths, the workspace root, and recursively discovered child project configurations.
## Packages
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "vipeye-internal",
"version": "1.1.426",
"version": "1.1.427",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "vipeye-internal",
"version": "1.1.426",
"version": "1.1.427",
"license": "MIT",
"dependencies": {
"@yarnpkg/fslib": "2.10.4",
+1 -1
View File
@@ -2,7 +2,7 @@
"name": "vipeye-internal",
"displayName": "vipeye",
"description": "Type checker for the Viper language",
"version": "1.1.426",
"version": "1.1.427",
"license": "MIT",
"private": true,
"files": [
@@ -53,8 +53,6 @@ import {
FunctionNode,
IndexNode,
NameNode,
ParamCategory,
ParameterNode,
ParseNode,
ParseNodeType,
StringNode,
@@ -754,9 +752,6 @@ export class HoverProvider {
this._addResultsPart(parts, signatureString, /* python */ true);
// Viper 扩展:hover 函数时附加详尽调试信息(仅 Viper 方言)。
this._addViperFunctionDebugPart(parts, resolvedDecl);
this._addDocumentationPart(parts, node, resolvedDecl);
break;
}
@@ -781,120 +776,6 @@ export class HoverProvider {
}
}
// Viper 扩展:hover 函数时附加详尽调试信息(仅 Viper 方言)。
// 显示:定义位置、修饰符(extern/export/inline)、泛型参数、参数列表(类型/默认值)、返回类型。
private _addViperFunctionDebugPart(parts: HoverTextPart[], resolvedDecl: FunctionDeclaration): void {
const funcNode = resolvedDecl.node;
if (funcNode.nodeType !== ParseNodeType.Function) {
return;
}
const fileInfo = getFileInfo(funcNode);
if (!IsViperDialect(fileInfo.executionEnvironment.dialect)) {
return;
}
// 不对 stub 文件中的声明附加调试信息(如 stdlib .vpi / typeshed .pyi 类型定义),
// 避免对纯声明函数产生噪声。
const declPathLower = fileInfo.fileUri.getPath().toLowerCase();
if (declPathLower.endsWith('.pyi') || declPathLower.endsWith('.vpi') || declPathLower.endsWith('.vpnci')) {
return;
}
const declUri = fileInfo.fileUri;
const declParseResults = this._program.getParseResults(declUri);
if (!declParseResults) {
return;
}
const declText = declParseResults.text;
const declLines = declParseResults.parserOutput.lines;
const isMarkdown = this._format === MarkupKind.Markdown;
const code = (s: string) => (isMarkdown ? `\`${s}\`` : s);
const bold = (s: string) => (isMarkdown ? `**${s}**` : s);
const lines: string[] = [];
// 定义位置(文件:行:列)
const namePos = convertOffsetToPosition(funcNode.d.name.start, declLines);
const locDisplay = declUri.key === this._fileUri.key ? '此文件' : declUri.getPath();
lines.push(`${bold('位置')}: ${code(`${locDisplay}:${namePos.line + 1}:${namePos.character + 1}`)}`);
// 修饰符(extern/export/inline 等 C 修饰关键字)
if (funcNode.d.viperModifier) {
lines.push(`${bold('修饰符')}: ${code(funcNode.d.viperModifier)}`);
}
// 泛型参数(PEP 695 type 参数)
if (funcNode.d.typeParams && funcNode.d.typeParams.d.params.length > 0) {
const typeParamNames = funcNode.d.typeParams.d.params.map((p) => p.d.name.d.value).join(', ');
lines.push(`${bold('泛型参数')}: ${code(`[${typeParamNames}]`)}`);
}
// 参数列表(名称、类型、默认值、*args/**kwargs 类别)
if (funcNode.d.params.length > 0) {
lines.push(`${bold('参数')}:`);
for (const param of funcNode.d.params) {
const paramText = this._getViperParamDebugText(param, declText);
if (paramText) {
lines.push(` ${code(paramText)}`);
}
}
}
// 返回类型
const returnType = this._getViperFunctionReturnType(funcNode);
if (returnType) {
lines.push(`${bold('返回类型')}: ${code(this._printType(convertToInstance(returnType)))}`);
}
if (lines.length === 0) {
return;
}
const heading = isMarkdown ? '**函数调试信息**' : '函数调试信息';
parts.push({ text: heading + '\n' + lines.join('\n'), python: false });
}
// 构造单个参数的调试文本:`*名称: 类型 = 默认值`。
private _getViperParamDebugText(param: ParameterNode, declText: string): string | undefined {
let text = '';
if (param.d.category === ParamCategory.ArgsList) {
text += '*';
} else if (param.d.category === ParamCategory.KwargsDict) {
text += '**';
}
text += param.d.name?.d.value ?? '(匿名)';
let paramType: Type | undefined;
if (param.d.annotation) {
paramType = this._evaluator.getTypeOfAnnotation(param.d.annotation);
} else if (param.d.name) {
paramType = this._evaluator.getTypeOfExpression(param.d.name).type;
}
if (paramType) {
text += ': ' + this._printType(convertToInstance(paramType));
}
if (param.d.defaultValue) {
const defaultText = declText.slice(param.d.defaultValue.start, TextRange.getEnd(param.d.defaultValue));
text += ` = ${defaultText}`;
}
return text;
}
// 返回类型:优先取注解类型,无注解时从函数类型的声明/推断返回类型提取。
private _getViperFunctionReturnType(funcNode: FunctionNode): Type | undefined {
if (funcNode.d.returnAnnotation) {
return this._evaluator.getTypeOfAnnotation(funcNode.d.returnAnnotation);
}
const fnType = this._getType(funcNode.d.name);
if (isFunction(fnType)) {
return fnType.shared.declaredReturnType ?? fnType.shared.inferredReturnType?.type;
}
return undefined;
}
private _addResultsForSynthesizedType(parts: HoverTextPart[], typeInfo: SynthesizedTypeInfo, hoverNode: NameNode) {
let typeText: string | undefined;
@@ -1,12 +1,8 @@
/*
* viperFunctionHover.test.ts
*
* 验证 Viper 方言下 hover 函数时显示详尽调试信息:
* - 定义位置(文件:行:列)
* - 修饰符(extern/export/inline)
* - 泛型参数
* - 参数列表(名称、类型、默认值、*args/**kwargs 类别)
* - 返回类型
* 验证 Viper 方言下 hover 函数时只显示**签名**(不再附加「函数调试信息」区块):
* 原实现会在签名后追加位置/修饰符/泛型参数/参数/返回类型等调试信息,现已移除。
*
* 运行:npx jest src/tests/viperFunctionHover.test.ts --runInBand
*/
@@ -67,7 +63,7 @@ function buildProgramForCode(code: string) {
return { program, configOptions, uri, testDir, serviceProvider };
}
test('Viper hover 函数显示详尽调试信息', () => {
test('Viper hover 函数只显示签名(不再附加函数调试信息)', () => {
const code = `import t, c
export def add[T](a: t.CInt, b: t.CInt = 10) -> t.CInt:
@@ -94,24 +90,14 @@ def use():
assert(contents.includes('```python'), `should use python code fence: ${contents}`);
assert(!contents.includes('```viper'), `should not use viper code fence: ${contents}`);
assert(contents.includes('(function)'), `should include signature: ${contents}`);
// 调试信息标题
assert(contents.includes('函数调试信息'), `should include debug heading: ${contents}`);
// 定义位置(此文件:3:17 —— `add` 在 L3 第 17 列)
assert(contents.includes('位置'), `should include 位置: ${contents}`);
assert(contents.includes('此文件'), `should include 此文件: ${contents}`);
// 修饰符
assert(contents.includes('修饰符'), `should include 修饰符: ${contents}`);
assert(contents.includes('export'), `should include export modifier: ${contents}`);
// 泛型参数
assert(contents.includes('泛型参数'), `should include 泛型参数: ${contents}`);
assert(contents.includes('[T]'), `should include [T]: ${contents}`);
// 参数(类型按源码书写的完整 module 路径展示:注解写的是 `t.CInt`)
assert(contents.includes('参数'), `should include 参数: ${contents}`);
assert(contents.includes('a: t.CInt'), `should include param a: ${contents}`);
assert(contents.includes('b: t.CInt = 10'), `should include param b with default: ${contents}`);
// 返回类型
assert(contents.includes('返回类型'), `should include 返回类型: ${contents}`);
assert(contents.includes('t.CInt'), `should include return type: ${contents}`);
// 已移除「函数调试信息」区块:标题与各字段均不应再出现。
for (const removed of ['函数调试信息', '修饰符', '泛型参数', '返回类型']) {
assert(
!contents.includes(removed),
`hover 不应再包含已移除的调试信息字段「${removed}」:${contents}`
);
}
program.dispose();
serviceProvider.dispose();
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "vipeye",
"version": "1.1.426",
"version": "1.1.427",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "vipeye",
"version": "1.1.426",
"version": "1.1.427",
"license": "MIT",
"bin": {
"vipeye": "index.js",
+1 -1
View File
@@ -2,7 +2,7 @@
"name": "vipeye",
"displayName": "Vipeye",
"description": "Type checker for the Viper language",
"version": "1.1.426",
"version": "1.1.427",
"license": "MIT",
"author": {
"name": "TermiNexus"
+19 -16
View File
@@ -1,5 +1,24 @@
# Change Log
## 1.1.427
- `__attribute__` no longer requires the file to `import t` / `import c`: the `t` / `c` module roots that `__attribute__(...)` desugars into are synthesized as **implicit module names** (zero-length, marked on the AST node — the compiler's attribute handling is AST-level and never checks symbol visibility either). When such a synthetic name cannot be resolved through the lexical scope chain, it falls back to the same-named binding in the built-in `builtins.vpi` stub; the fallback only applies to the desugared nodes, so a hand-written bare `t` / `c` in a file without the import still reports "未定义"
- **Analysis progress in the status bar**: while the language server builds the dependency graph and type-checks the workspace, the bottom-left status bar now shows `$(sync~spin) C:\xxx\1.vp (4/90) 4.44% loading...` — the file currently being analyzed, how many files have been processed out of how many, and the percentage. The worker reports per-file progress (throttled to one message every 50 ms) through a new `analysisProgress` background message and a new `viper/analysisStatus` notification; the client shows a left-aligned `StatusBarItem` and hides it as soon as analysis finishes (or when there is nothing to analyze). Previously the only feedback was the work-done progress bar for open-files analysis, which said nothing about which file was being processed, so a long full-workspace pass was indistinguishable from a hung server
- The **`static` modifier** is now supported (LSP side only; TransPyC/TransPyV are unchanged). It is a *soft* keyword: `static def f(...)` / `static fn f(...)` marks a function (recorded on `FunctionNode.d.viperModifier`, exactly like `virtual`) and `static x: T = v` marks a variable (the annotation is rewritten to `t.CStatic | T`, the same tree TransPyC's `_parse_type_ann` builds by inserting `t.CStatic` at the left of the union, so `static SECRET: int` renders as `static int`). Anywhere else `static` stays an ordinary identifier — `static = 1`, `static: int`, `static(x)`, `def f(static: int)` and `self.static` all parse as before. Its meaning is C's **internal linkage**: a static symbol **cannot be used across modules**. Referencing one from another module is reported as `'lib.helper' 是 static 符号,禁止跨模块使用(static 仅限定义模块内可见)` for both `mod.name` / `mod.fn()` (checked in the module member-access path) and `from mod import name` (checked on the import statement), while same-module references are allowed — the decision is made per declaration file, so a self-import is not flagged either. `static` is also offered by completion and highlighted as a modifier (`storage.modifier.viper`, matched in front of a `def`-family keyword or a type atom)
- The `f` prefix of an f-string is now highlighted as `storage.type.string.viper` (blue in the default dark theme) instead of being left unstyled. The rule matches the `r` / `u` / `b` / `f` prefix combination immediately preceding a quote, so it covers `f"..."`, `f'''...'''` and mixed spellings like `rf"..."`; a bare `r`/`f`/`b` identifier not followed by a quote is unaffected, and the string body keeps the usual `string.quoted.*` scope
- The **size of a fixed-length array type may be any integer constant expression** now: `part_types: [MAX_VOLUMES * MAX_PARTITIONS]uint8`, `part_lit: [MAX_VOLUMES * 2]uint8`, `part_left: [MAX_VOLUMES - 1]uint8` and `part_shift: [1 << 3]uint8` all parse and check cleanly. Two bugs made this fail before: the parser parsed the `[SIZE]` part while still flagged as a type annotation, so `_parseArithmeticTerm` deliberately stopped at the `*` (it is the C pointer suffix marker there) — the bracket was reported as "“[" 未关闭" and the leftover `* MAX_PARTITIONS]uint8` as "类型表达式中不允许使用解包运算符"; and the type evaluator then evaluated that arithmetic with `EvalFlags.TypeExpression`, reporting "类型表达式中不允许使用二元运算符". The size is now parsed as the value expression it is (the flag is cleared for its duration), and the evaluator allows arithmetic/bitwise operators in a type expression when **both operands are Viper integers** (the case the enclosing comment already described, e.g. `t.CArray[T, MAX_ORDER + 1]`); non-integer operands (`C1 + C2`) are still reported
- **`const` pointers are read-only for dereference assignment**: `lfn: const str` (`CConst | str`, i.e. `const char *`) now allows dereferencing — `*lfn` and `lfn[0]` evaluate to `char` — but writing through the dereference is reported as "不能给 const 指针的解引用赋值("const str" 指向的数据为只读)". This covers **both** spellings, `*lfn = char(65)` (dereference) and `lfn[0] = char(65)` (**subscript assignment**), which share the same diagnostic. Previously the dereference itself was rejected with a bogus "类型"const str"不支持运算符"*"" (the `CConst` modifier in the union was mistaken for a non-pointer operand), and there was no read-only check at all. A non-`const` pointer (`buf: str`) still accepts `*buf = char(65)` and `buf[0] = char(65)`
- C type descriptors are **skipped** when checking iterability, so a `const` pointer can be iterated: `for ch in path:` with `path: const str` (and `const t.CPtr`) no longer reports "“CConst”不可迭代 / 未定义“__iter__”方法". The predicate now recognizes every Viper pointer (CPtr, `str`, `bytes`) rather than only `CPtr`, which is why `const str` had been missed — `CConst` fell through to the "not iterable" path
- The `static` modifier is **only** accepted at the start of a statement, before the variable name: `static memory_regions: memory_region* = None`, the spelling TransPyC's `_IsStaticModifier` understands. Writing it **inside the type annotation** (`memory_regions: static memory_region* = None`) is reported as a syntax error — `static 修饰符不支持写在类型注解里,请写成「static 变量名: 类型」(修饰符必须位于变量名之前)` — instead of being silently accepted (it used to desugar to the same `t.CStatic | T`, so the same meaning had two spellings and only one of them compiled). The descriptor is consumed and the remaining type is still parsed, so the same annotation no longer additionally reports `"static" is not defined`. `static` stays a **soft** keyword: it is special only when a type atom follows, so `static: int = 0`, `static = 1` and `def f(static: int)` are untouched
- `*args` is bound to the `va_list` **pointer** even when the file doesn't `import t`: the Viper var-arg rule used to look up `t.CPtr` through the source-visible `t` module only, so in a file like ViperOS `drivers/serial/uart/serial.vp` (no `import t`) it fell back to Python's `tuple` semantics and `viperlib.vsnprintf(&buf, 1024, fmt, args)` was reported as "无法将"tuple"类型的参数分配给函数"vsnprintf"中类型为"str"的参数"ap"". The lookup now falls back to the compiler built-in `rawptr` from `builtins.vpi` (= `t.CPtr`), the same mechanism `T*` annotations and `[SIZE]T` already use
- Indexing a C array now yields the **element type**, so member access on an element resolves: with `kbd_buf: [KBD_BUF_SIZE]usb_kbd_event`, `kbd_buf[i].modifier` used to report "无法解析类型上的成员"modifier"访问,对象可能未定义" and the member name got no semantic token at all (plain white). The CArray stub declares `class CArray(CType, Generic[T, N])` with `def __getitem__(self, index: int) -> T`, but Viper-side `TypeVar(...)` does not produce substitutable type variables (the class's type parameters end up as synthesized `__P0` / `__P1`), so the return type never became concrete. The Viper index path now reads the **first type argument** directly (C semantics: `arr[i]` has the element type), the same way the `CPtr[T]` branch reads `priv.typeArgs[0]`; this covers both `[SIZE]T` and the explicit `t.CArray[T, N]` spelling, and works for a union base too (`CArray[T, N] | t.CExport`)
- **`export` / `extern` on variable declarations** are now supported: `export gdt: [7]gdt_entry = []`, `export tss_buf: [104]uint8` and `extern ext_flag: int`. The variable form is told apart from the function form (`export def f()` / `export f(...)`) by the third token — `:` is a variable (type annotation), `(` / `[` is a function, the same criterion as TransPyC's `_is_export_extern_var`). They desugar exactly like TransPyC's `_parse_export_extern_var`: `export NAME: T [= v]` becomes `T | t.CExport` (bare-named definition, **no module SHA1 header**), `extern NAME: T` becomes `T | t.State` (= `CExtern | CExport`, declaration only). An **initializer is allowed for `export` but rejected for `extern`** — `extern a: int = 1` reports "extern 变量声明不能带初始值(符号定义在外部)", matching the compiler's fail-fast. Storage-class markers render as a prefix qualifier (`export CArray[gdt_entry, 7]`, `extern int`), and the marker is treated as a modifier everywhere else: in the `Struct | CPtr` fold (so `extern p: t.CPtr` stays a pointer instead of degrading to `CPtr[State]`), in the union index access, and as a CArray element access (so `gdt[i].access` still resolves to `uint8` and the zero-initializer `= []` is accepted)
- `[SIZE]T` array annotations no longer require the file to `import t`. The parser desugars `coroutines: [8]Coroutine` into a synthetic `t.CArray[Coroutine, 8]` member access; when that synthetic `t` could not be resolved through the lexical scope it silently degraded to `Unknown`, so the whole array annotation collapsed and `m.coroutines[i].rsp` reported "无法解析类型上的成员“rsp”访问" with `rsp` getting no semantic token at all (rendered unhighlighted, no jump-to-definition). The synthetic `t` now falls back to the `t` module bound in the built-in `builtins.vpi` scope — the same mechanism `T*` uses for `rawptr` and `__attribute__(...)` uses for its implicit module roots — so a file without `import t` still gets the real `CArray[Coroutine, Literal[8]]` and the element's member resolves
- Attribute names inside `__attribute__(...)` are highlighted **structurally**, so a name that the compiler supports but that isn't declared in the `attr` class still colours like the declared ones. `stackrealign` (supported by TransPyV's code generator — `FuncGenBase.vp` maps it to the LLVM `"stackrealign"` attribute — and used by ViperOS `VKernel/Kernel/main.vp`) is declared in neither `t.py` nor the `t.vpi` stub, so `t.attr.stackrealign` used to be unresolvable: the name got no semantic token (rendered unhighlighted) and the file was additionally flagged with "无法访问类"type[attr]"的属性"stackrealign"". The parser now marks the synthesized attribute-name node, the semantic token provider colours it as `function` (exactly like a declared attribute such as `section` / `aligned` / `naked`), and the member-access diagnostic is suppressed for these names — the compiler silently drops unknown attributes rather than rejecting them, so reporting them was a false positive. Declared names keep resolving through `t.attr` as before, so hover and jump-to-definition still work for them
- `__attribute__(...)` **completion and hover** now list attribute names that the compiler supports but that the `attr` class doesn't declare, instead of only mirroring `t.attr`. `stackrealign` is offered by typing inside the parentheses and appears in the hover list of available names; the list is a single shared union (`viperAttributeUtils.getViperAttributeNames`) of `t.attr`'s declared names plus these extra names, so completion and hover cannot drift apart. Declared names keep coming from the stub symbol table (so they still show their signature and support jump-to-definition)
- Pointer-to-pointer implicit conversion is now **strict** instead of "same level is always allowed". A pointer converts implicitly to another pointer only when the **level** matches **and** the pointee is compatible: either side being `void*` / undetermined (`rawptr`) always allows it, identical pointees are allowed, two basic (numeric scalar) pointees follow the very same rules as the values themselves (`uint32* → uint64*` and `uint32* → uint16*` are allowed because integers convert in every direction; `float* → int*` is still reported), and for **structs** only an **upcast** is allowed (`Sub* → Node*`; `memhub` / `requests` depend on `MemBuddy* → MemManager*`), while two unrelated structs (`Node* → Other*`), a downcast, or a struct↔basic mix (`Node* → uint64*`) are rejected. Single-level pointers also convert to and from `void*` (`t.CVoid`) in both directions, while multi-level ones still don't (`Node** → void*` is reported). `&x` / `*x` already produced the right types (`&x` is `T*` for `x: T`, `T**` for `x: T*`; `*x` is `T` for `x: T*`) and now take part in these checks too — `a(&x)` with `x: Node*` (a `Node**` into a `void*` parameter) is reported as before, and so is `a(&x)` where the parameter is a single-level pointer
- Assigning a **plain function to a method slot of a `virtual class`** is now accepted: `serial0_file.open = serial0_open` (ViperOS `devfs.vp`, where `devfs_file` is a `virtual class` and `serial0_open(self: rawptr, path: const str, flags: int) -> int` is a module-level function) used to be reported as "无法为类"devfs_file"的属性"open"赋值 / 参数名称不匹配: "path" versus "self" / 额外参数 "flags"". Such an assignment writes a **vtable slot**, and the compiler (TPC `HandlesAssign` / TransPyV `CallGenVt._write_vtable_slot`) just bitcasts the function to `i8*` and stores it — it never validates the signature or `self` — whereas the language server compared the *bound* method type (with `self` already stripped), so the explicitly written `self` looked like a spurious extra parameter. The check now follows the vtable semantics: the class must be a `virtual class` (`virtual class X` or `@t.CVTable class X`), the member must be a method declared in it, the assigned function's `self` must be a generic pointer (`rawptr` / `t.CPtr`) or a pointer to that class itself or one of its base classes, and every remaining parameter must match by name and type with an identical return type (`*args` / `**kwargs` are not compared, so they may be present on either side). Everything else — a non-virtual class, `self` that isn't such a pointer, or a parameter/return mismatch — is still reported
## 1.1.426
- **Type descriptors**: `const` / `volatile` may prefix any type annotation — `a: const int`, `b: volatile int`, `def f() -> volatile int`, `p: const t.CPtr`, `q: const int*`, in parameters, return types, class members and type aliases. Both are **hard keywords** now (reserved words): the only legal use is a type-annotation prefix, anything else (`const = 1`, `a: const` with no type) is reported as a syntax error with a dedicated message. They desugar in the parser into the equivalent type-set annotation `CConst | T` / `CVolatile | T`, so they weigh exactly the same as the hand-written form in assignment compatibility, operator handling and diagnostics — no second modifier mechanism. The desugared `CConst` / `CVolatile` names live in the built-in `builtins.vpi` stub, so no `import t` is required
@@ -27,22 +46,6 @@
- `__attribute__` is highlighted as a type modifier (same scope as `const` / `volatile`): the TextMate grammar matches it when followed by `(`, in both the decorator and the modifier spelling
- `__attribute__` is also offered by completion (typing `__a` lists it) and **hovering inside `__attribute__(...)`** shows the full list of available attribute names (`section` / `aligned` / `always_inline` / `weak` / …), taken from the `attr` class of the `t` stub. Both share one helper (`viperAttributeUtils`) for locating the parentheses and enumerating `t.attr`, so the completion list and the hover list cannot drift apart
- Lowercase **`callable[[A, B], R]`** is now supported in a type annotation and means exactly `t.Callable[[A, B], R]` — all three spellings (`callable[...]`, `Callable[...]`, `t.Callable[...]`) and all the usual argument forms (`callable[[int, str], bool]`, `callable[..., bool]`, `callable[[], bool]`, as a parameter, return or variable annotation) evaluate to the same callable type. The parser rewrites the bare name `callable` to `Callable` **only in type-annotation position**, so the built-in function is untouched: `callable(x)` / `if callable(x):` still behave as before. `Callable` is declared in the built-in `builtins.vpi` stub (`Callable: t.CTypedef = t.Callable`, next to `rawptr` / `CConst`), so no `import t` is required. Previously `callable[[int], bool]` silently degraded to the type of the `callable` *function* (the Viper-dialect rule that ignores explicit type arguments on a callable), and bare `Callable` was reported as undefined
- `__attribute__` no longer requires the file to `import t` / `import c`: the `t` / `c` module roots that `__attribute__(...)` desugars into are synthesized as **implicit module names** (zero-length, marked on the AST node — the compiler's attribute handling is AST-level and never checks symbol visibility either). When such a synthetic name cannot be resolved through the lexical scope chain, it falls back to the same-named binding in the built-in `builtins.vpi` stub; the fallback only applies to the desugared nodes, so a hand-written bare `t` / `c` in a file without the import still reports "未定义"
- **Analysis progress in the status bar**: while the language server builds the dependency graph and type-checks the workspace, the bottom-left status bar now shows `$(sync~spin) C:\xxx\1.vp (4/90) 4.44% loading...` — the file currently being analyzed, how many files have been processed out of how many, and the percentage. The worker reports per-file progress (throttled to one message every 50 ms) through a new `analysisProgress` background message and a new `viper/analysisStatus` notification; the client shows a left-aligned `StatusBarItem` and hides it as soon as analysis finishes (or when there is nothing to analyze). Previously the only feedback was the work-done progress bar for open-files analysis, which said nothing about which file was being processed, so a long full-workspace pass was indistinguishable from a hung server
- The **`static` modifier** is now supported (LSP side only; TransPyC/TransPyV are unchanged). It is a *soft* keyword: `static def f(...)` / `static fn f(...)` marks a function (recorded on `FunctionNode.d.viperModifier`, exactly like `virtual`) and `static x: T = v` marks a variable (the annotation is rewritten to `t.CStatic | T`, the same tree TransPyC's `_parse_type_ann` builds by inserting `t.CStatic` at the left of the union, so `static SECRET: int` renders as `static int`). Anywhere else `static` stays an ordinary identifier — `static = 1`, `static: int`, `static(x)`, `def f(static: int)` and `self.static` all parse as before. Its meaning is C's **internal linkage**: a static symbol **cannot be used across modules**. Referencing one from another module is reported as `'lib.helper' 是 static 符号,禁止跨模块使用(static 仅限定义模块内可见)` for both `mod.name` / `mod.fn()` (checked in the module member-access path) and `from mod import name` (checked on the import statement), while same-module references are allowed — the decision is made per declaration file, so a self-import is not flagged either. `static` is also offered by completion and highlighted as a modifier (`storage.modifier.viper`, matched in front of a `def`-family keyword or a type atom)
- The `f` prefix of an f-string is now highlighted as `storage.type.string.viper` (blue in the default dark theme) instead of being left unstyled. The rule matches the `r` / `u` / `b` / `f` prefix combination immediately preceding a quote, so it covers `f"..."`, `f'''...'''` and mixed spellings like `rf"..."`; a bare `r`/`f`/`b` identifier not followed by a quote is unaffected, and the string body keeps the usual `string.quoted.*` scope
- The **size of a fixed-length array type may be any integer constant expression** now: `part_types: [MAX_VOLUMES * MAX_PARTITIONS]uint8`, `part_lit: [MAX_VOLUMES * 2]uint8`, `part_left: [MAX_VOLUMES - 1]uint8` and `part_shift: [1 << 3]uint8` all parse and check cleanly. Two bugs made this fail before: the parser parsed the `[SIZE]` part while still flagged as a type annotation, so `_parseArithmeticTerm` deliberately stopped at the `*` (it is the C pointer suffix marker there) — the bracket was reported as "“[" 未关闭" and the leftover `* MAX_PARTITIONS]uint8` as "类型表达式中不允许使用解包运算符"; and the type evaluator then evaluated that arithmetic with `EvalFlags.TypeExpression`, reporting "类型表达式中不允许使用二元运算符". The size is now parsed as the value expression it is (the flag is cleared for its duration), and the evaluator allows arithmetic/bitwise operators in a type expression when **both operands are Viper integers** (the case the enclosing comment already described, e.g. `t.CArray[T, MAX_ORDER + 1]`); non-integer operands (`C1 + C2`) are still reported
- **`const` pointers are read-only for dereference assignment**: `lfn: const str` (`CConst | str`, i.e. `const char *`) now allows dereferencing — `*lfn` and `lfn[0]` evaluate to `char` — but writing through the dereference is reported as "不能给 const 指针的解引用赋值("const str" 指向的数据为只读)". This covers **both** spellings, `*lfn = char(65)` (dereference) and `lfn[0] = char(65)` (**subscript assignment**), which share the same diagnostic. Previously the dereference itself was rejected with a bogus "类型"const str"不支持运算符"*"" (the `CConst` modifier in the union was mistaken for a non-pointer operand), and there was no read-only check at all. A non-`const` pointer (`buf: str`) still accepts `*buf = char(65)` and `buf[0] = char(65)`
- C type descriptors are **skipped** when checking iterability, so a `const` pointer can be iterated: `for ch in path:` with `path: const str` (and `const t.CPtr`) no longer reports "“CConst”不可迭代 / 未定义“__iter__”方法". The predicate now recognizes every Viper pointer (CPtr, `str`, `bytes`) rather than only `CPtr`, which is why `const str` had been missed — `CConst` fell through to the "not iterable" path
- The `static` modifier is **only** accepted at the start of a statement, before the variable name: `static memory_regions: memory_region* = None`, the spelling TransPyC's `_IsStaticModifier` understands. Writing it **inside the type annotation** (`memory_regions: static memory_region* = None`) is reported as a syntax error — `static 修饰符不支持写在类型注解里,请写成「static 变量名: 类型」(修饰符必须位于变量名之前)` — instead of being silently accepted (it used to desugar to the same `t.CStatic | T`, so the same meaning had two spellings and only one of them compiled). The descriptor is consumed and the remaining type is still parsed, so the same annotation no longer additionally reports `"static" is not defined`. `static` stays a **soft** keyword: it is special only when a type atom follows, so `static: int = 0`, `static = 1` and `def f(static: int)` are untouched
- `*args` is bound to the `va_list` **pointer** even when the file doesn't `import t`: the Viper var-arg rule used to look up `t.CPtr` through the source-visible `t` module only, so in a file like ViperOS `drivers/serial/uart/serial.vp` (no `import t`) it fell back to Python's `tuple` semantics and `viperlib.vsnprintf(&buf, 1024, fmt, args)` was reported as "无法将"tuple"类型的参数分配给函数"vsnprintf"中类型为"str"的参数"ap"". The lookup now falls back to the compiler built-in `rawptr` from `builtins.vpi` (= `t.CPtr`), the same mechanism `T*` annotations and `[SIZE]T` already use
- Indexing a C array now yields the **element type**, so member access on an element resolves: with `kbd_buf: [KBD_BUF_SIZE]usb_kbd_event`, `kbd_buf[i].modifier` used to report "无法解析类型上的成员"modifier"访问,对象可能未定义" and the member name got no semantic token at all (plain white). The CArray stub declares `class CArray(CType, Generic[T, N])` with `def __getitem__(self, index: int) -> T`, but Viper-side `TypeVar(...)` does not produce substitutable type variables (the class's type parameters end up as synthesized `__P0` / `__P1`), so the return type never became concrete. The Viper index path now reads the **first type argument** directly (C semantics: `arr[i]` has the element type), the same way the `CPtr[T]` branch reads `priv.typeArgs[0]`; this covers both `[SIZE]T` and the explicit `t.CArray[T, N]` spelling, and works for a union base too (`CArray[T, N] | t.CExport`)
- **`export` / `extern` on variable declarations** are now supported: `export gdt: [7]gdt_entry = []`, `export tss_buf: [104]uint8` and `extern ext_flag: int`. The variable form is told apart from the function form (`export def f()` / `export f(...)`) by the third token — `:` is a variable (type annotation), `(` / `[` is a function, the same criterion as TransPyC's `_is_export_extern_var`). They desugar exactly like TransPyC's `_parse_export_extern_var`: `export NAME: T [= v]` becomes `T | t.CExport` (bare-named definition, **no module SHA1 header**), `extern NAME: T` becomes `T | t.State` (= `CExtern | CExport`, declaration only). An **initializer is allowed for `export` but rejected for `extern`** — `extern a: int = 1` reports "extern 变量声明不能带初始值(符号定义在外部)", matching the compiler's fail-fast. Storage-class markers render as a prefix qualifier (`export CArray[gdt_entry, 7]`, `extern int`), and the marker is treated as a modifier everywhere else: in the `Struct | CPtr` fold (so `extern p: t.CPtr` stays a pointer instead of degrading to `CPtr[State]`), in the union index access, and as a CArray element access (so `gdt[i].access` still resolves to `uint8` and the zero-initializer `= []` is accepted)
- `[SIZE]T` array annotations no longer require the file to `import t`. The parser desugars `coroutines: [8]Coroutine` into a synthetic `t.CArray[Coroutine, 8]` member access; when that synthetic `t` could not be resolved through the lexical scope it silently degraded to `Unknown`, so the whole array annotation collapsed and `m.coroutines[i].rsp` reported "无法解析类型上的成员“rsp”访问" with `rsp` getting no semantic token at all (rendered unhighlighted, no jump-to-definition). The synthetic `t` now falls back to the `t` module bound in the built-in `builtins.vpi` scope — the same mechanism `T*` uses for `rawptr` and `__attribute__(...)` uses for its implicit module roots — so a file without `import t` still gets the real `CArray[Coroutine, Literal[8]]` and the element's member resolves
- Attribute names inside `__attribute__(...)` are highlighted **structurally**, so a name that the compiler supports but that isn't declared in the `attr` class still colours like the declared ones. `stackrealign` (supported by TransPyV's code generator — `FuncGenBase.vp` maps it to the LLVM `"stackrealign"` attribute — and used by ViperOS `VKernel/Kernel/main.vp`) is declared in neither `t.py` nor the `t.vpi` stub, so `t.attr.stackrealign` used to be unresolvable: the name got no semantic token (rendered unhighlighted) and the file was additionally flagged with "无法访问类"type[attr]"的属性"stackrealign"". The parser now marks the synthesized attribute-name node, the semantic token provider colours it as `function` (exactly like a declared attribute such as `section` / `aligned` / `naked`), and the member-access diagnostic is suppressed for these names — the compiler silently drops unknown attributes rather than rejecting them, so reporting them was a false positive. Declared names keep resolving through `t.attr` as before, so hover and jump-to-definition still work for them
- `__attribute__(...)` **completion and hover** now list attribute names that the compiler supports but that the `attr` class doesn't declare, instead of only mirroring `t.attr`. `stackrealign` is offered by typing inside the parentheses and appears in the hover list of available names; the list is a single shared union (`viperAttributeUtils.getViperAttributeNames`) of `t.attr`'s declared names plus these extra names, so completion and hover cannot drift apart. Declared names keep coming from the stub symbol table (so they still show their signature and support jump-to-definition)
- Pointer-to-pointer implicit conversion is now **strict** instead of "same level is always allowed". A pointer converts implicitly to another pointer only when the **level** matches **and** the pointee is compatible: either side being `void*` / undetermined (`rawptr`) always allows it, identical pointees are allowed, two basic (numeric scalar) pointees follow the very same rules as the values themselves (`uint32* → uint64*` and `uint32* → uint16*` are allowed because integers convert in every direction; `float* → int*` is still reported), and for **structs** only an **upcast** is allowed (`Sub* → Node*`; `memhub` / `requests` depend on `MemBuddy* → MemManager*`), while two unrelated structs (`Node* → Other*`), a downcast, or a struct↔basic mix (`Node* → uint64*`) are rejected. Single-level pointers also convert to and from `void*` (`t.CVoid`) in both directions, while multi-level ones still don't (`Node** → void*` is reported). `&x` / `*x` already produced the right types (`&x` is `T*` for `x: T`, `T**` for `x: T*`; `*x` is `T` for `x: T*`) and now take part in these checks too — `a(&x)` with `x: Node*` (a `Node**` into a `void*` parameter) is reported as before, and so is `a(&x)` where the parameter is a single-level pointer
- Assigning a **plain function to a method slot of a `virtual class`** is now accepted: `serial0_file.open = serial0_open` (ViperOS `devfs.vp`, where `devfs_file` is a `virtual class` and `serial0_open(self: rawptr, path: const str, flags: int) -> int` is a module-level function) used to be reported as "无法为类"devfs_file"的属性"open"赋值 / 参数名称不匹配: "path" versus "self" / 额外参数 "flags"". Such an assignment writes a **vtable slot**, and the compiler (TPC `HandlesAssign` / TransPyV `CallGenVt._write_vtable_slot`) just bitcasts the function to `i8*` and stores it — it never validates the signature or `self` — whereas the language server compared the *bound* method type (with `self` already stripped), so the explicitly written `self` looked like a spurious extra parameter. The check now follows the vtable semantics: the class must be a `virtual class` (`virtual class X` or `@t.CVTable class X`), the member must be a method declared in it, the assigned function's `self` must be a generic pointer (`rawptr` / `t.CPtr`) or a pointer to that class itself or one of its base classes, and every remaining parameter must match by name and type with an identical return type (`*args` / `**kwargs` are not compared, so they may be present on either side). Everything else — a non-virtual class, `self` that isn't such a pointer, or a parameter/return mismatch — is still reported
## 1.1.425
+6 -2
View File
@@ -9,12 +9,16 @@ Viper 是一门基于 CPython 语法的 AOT 静态类型语言(编译器为 Tr
- **语法高亮**:`.vp` / `.vpi` 文件的完整 Viper 语法高亮(关键字、类型、装饰器 `@`、f-string、常量、未定义符号等语义化区分)
- **语义高亮**:基于 LSP 3.16 SemanticTokens 协议,区分已定义/未定义符号、类/函数/变量/模块/参数/常量
- **类型检查与诊断**:遵循 Viper 语义的类型推断与静态检查
- 指针语义:`CPtr` / `CArray`(数组名退化、形参调整、派生类指针 upcast)
- 指针语义:`CPtr` / `CArray`(数组名退化、形参调整)、指针隐式转换规则(级数须相同且 pointee 兼容:`void*` 双向放行、基本类型按自身规则、结构体仅允许上转型)
- 声明/注解修饰符:`const` / `volatile` 类型描述符、`static`(C internal linkage,禁止跨模块取参)、`export` / `extern` 变量(`extern` 不允许初始值)、`virtual` / `novirtual`、`__attribute__(...)`(装饰器与注解修饰器两种写法)
- `virtual class` 方法槽赋值:`obj.method = func` 按 vtable 槽语义校验(self 为泛指针或该类/父类指针,其余签名相同)
- `[SIZE]T` 定长数组类型、`typedef T1 = T2`、C 风格强转 `(T)x` / `(T*)x` / `(T*)(x)` / `T*(x)`
- 类型别名与 C 类型系统:`t.CInt` / `t.CTypedef` / `t.CDefine` / `t.CEnum` 等
- Viper 特有的 `T | t.CPtr` 指针折叠表示
- 全局变量访问规则(读/写/global 声明判定)
- **项目支持**:`.vpj` 项目配置文件、`viperconfig.json` 配置
- **代码补全与悬停信息**
- **代码补全与悬停信息**(`__attribute__(...)` 括号内可直接补全/悬停查看可用属性名)
- **状态栏分析进度**:分析期间显示当前文件与 `N / M` 进度;`viper.countLines` 命令可统计 `.vpj` 项目的代码行数(KPI、分语言图表、明细表)
- **导航**:定义跳转、查找引用、重命名
## 项目架构
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "vscode-vipeye",
"version": "1.1.426",
"version": "1.1.427",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "vscode-vipeye",
"version": "1.1.426",
"version": "1.1.427",
"license": "MIT",
"dependencies": {
"mermaid": "^11.12.0",
+1 -1
View File
@@ -2,7 +2,7 @@
"name": "vscode-vipeye",
"displayName": "Vipeye",
"description": "VS Code language support for Viper (LSP client)",
"version": "1.1.426",
"version": "1.1.427",
"private": true,
"license": "MIT",
"author": {