fix(vipeye-internal): 支持注解内 static 前缀;*args 在未 import t 时仍是 va_list 指针
- `x: static T = v`(ViperOS VKernel/Kernel/drivers/** 全仓写法,如 `buf_head: static uint32 = 0`)脱糖为与语句起始形态相同的 `t.CStatic | T`, 并在声明节点打 isViperStatic,跨模块取参检查同判据;此前 `static` 在注解里 被当普通名引用,误报「"static" is not defined」。软关键字边界不变 (`static: int` / `static = 1` 仍按标识符),tmLanguage 一并高亮注解内 static - `getViperCPtrType` 增加 builtins `rawptr` 回退:文件没有 `import t` 时 `*args` 不再退回 Python tuple 语义,`viperlib.vsnprintf(&buf, 1024, fmt, args)` 不再误报「tuple 不能赋给参数 ap(str)」(与 T* 注解 / [SIZE]T 同机制) - 回归用例 +3:注解内 static 解析/展示/跨模块拦截 + 软关键字边界、 未 import t 的 *args 透传;jest 805 -> 808
This commit is contained in:
1 parent
2a7757e3b7
commit
5808a76b23
7 files changed
+238
-3
No files matched your search
@@ -154,6 +154,7 @@ packages/vipeye-internal/src/tests/__viper_type_binop_bound__/
|
||||
packages/vipeye-internal/src/tests/__viper_const_ptr_deref__/
|
||||
packages/vipeye-internal/src/tests/__viper_const_ptr_index__/
|
||||
packages/vipeye-internal/src/tests/__viper_const_ptr_iter__/
|
||||
packages/vipeye-internal/src/tests/__viper_variadic_noimport__/
|
||||
|
||||
# perfCompare.py cached commit bundles
|
||||
/build/perfCompare/
|
||||
|
||||
@@ -8746,6 +8746,22 @@ export function createTypeEvaluator(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 回退:builtins stub 的编译器内置 `rawptr`(= t.CPtr,见 builtins.vpi)。
|
||||
// 文件没 `import t` 时(如 ViperOS 的 serial.vp)也要能用 `*args` 的
|
||||
// va_list 指针语义,与 `T*` 注解 / `[SIZE]T` 一致(不强制源码 import t)。
|
||||
const rawptrSymbolWithScope = lookUpSymbolRecursive(node, 'rawptr', /* honorCodeFlow */ true);
|
||||
if (rawptrSymbolWithScope) {
|
||||
const rawptrType = getEffectiveTypeOfSymbolForUsage(
|
||||
rawptrSymbolWithScope.symbol,
|
||||
/* usageNode */ undefined,
|
||||
/* useLastDecl */ true
|
||||
).type;
|
||||
if (rawptrType && isInstantiableClass(rawptrType)) {
|
||||
return convertToInstance(ClassType.specialize(rawptrType, [UnknownType.create()]));
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
||||
@@ -257,6 +257,10 @@ export class Parser {
|
||||
// 不因 `*` 之后是行尾还是下一行内容而在两条解析路径间摇摆
|
||||
// (见 _isAtViperPointerTypeSuffix 对换行的处理)。
|
||||
private _isParsingViperTypeDescriptorInner = false;
|
||||
// Viper 扩展:本次注解解析里消费过 `static` 类型描述符(`x: static T = v`)。
|
||||
// 注解解析结束后由 _parseExpressionStatement 读取一次并清零:命中时把该声明
|
||||
// 标为 static 符号(isViperStatic),供「禁止跨模块取参」检查使用。
|
||||
private _viperStaticDescriptorInAnnotation = false;
|
||||
// Viper 扩展:当前所处的类声明形态,用于校验 vtable 修饰方法的出现位置:
|
||||
// `virtual def` 仅允许位于 class / virtual class 内;
|
||||
// `novirtual def` 允许位于 class / virtual class / novirtual class 内。
|
||||
@@ -5121,8 +5125,18 @@ export class Parser {
|
||||
if (this._consumeTokenIfType(TokenType.Colon)) {
|
||||
// 保留原始赋值目标(NameNode),后续 `*=` 拆分重包装时避免嵌套 TypeAnnotationNode。
|
||||
const targetExpr = leftExpr;
|
||||
// Viper 扩展:注解内 `static` 描述符(`x: static T = v`)→ 该声明是 static 符号。
|
||||
this._viperStaticDescriptorInAnnotation = false;
|
||||
annotationExpr = this._parseTypeAnnotation();
|
||||
leftExpr = TypeAnnotationNode.create(targetExpr, annotationExpr);
|
||||
const annotationNode = TypeAnnotationNode.create(targetExpr, annotationExpr);
|
||||
leftExpr = annotationNode;
|
||||
if (this._viperStaticDescriptorInAnnotation) {
|
||||
annotationNode.d.isViperStatic = true;
|
||||
if (targetExpr.nodeType === ParseNodeType.Name) {
|
||||
targetExpr.d.isViperStatic = true;
|
||||
}
|
||||
this._viperStaticDescriptorInAnnotation = false;
|
||||
}
|
||||
|
||||
if (
|
||||
!this._parseOptions.isStubFile &&
|
||||
@@ -5367,6 +5381,10 @@ export class Parser {
|
||||
if (attributeNode) {
|
||||
return attributeNode;
|
||||
}
|
||||
const staticNode = this._parseViperStaticTypeDescriptorPrefix();
|
||||
if (staticNode) {
|
||||
return staticNode;
|
||||
}
|
||||
const descriptorNode = this._parseViperTypeDescriptorPrefix();
|
||||
if (descriptorNode) {
|
||||
return descriptorNode;
|
||||
@@ -5374,6 +5392,55 @@ export class Parser {
|
||||
return this._parseViperBracketArrayTypeIfNeeded();
|
||||
}
|
||||
|
||||
// Viper 扩展:注解位置的 `static` 类型描述符前缀 `x: static T = v`。
|
||||
// 与语句起始形态 `static x: T = v`(见 _parseViperStaticAnnotatedStatement)
|
||||
// 脱糖为同一形状 `t.CStatic | T`(存储类标记:TypeSet 语义里只作修饰,
|
||||
// 不改变数据类型;展示为 `static T`)。ViperOS 全仓写法都是注解内前缀,
|
||||
// 故两种位置都支持。
|
||||
//
|
||||
// `static` 是**软关键字**:只有「后随类型原子起点」时才在此消费,
|
||||
// 其余(`x: static`、`def f(static: int)`、`self.static`、`x: static.y`)
|
||||
// 仍按普通标识符处理(与 TPC `_IsStaticModifier` 的软关键字边界同口径)。
|
||||
// 消费成功后置 _viperStaticDescriptorInAnnotation,供 _parseExpressionStatement
|
||||
// 把该变量声明标为 static 符号(禁止跨模块取参)。
|
||||
private _parseViperStaticTypeDescriptorPrefix(): ExpressionNode | undefined {
|
||||
if (this._parseOptions.dialect !== 'viper') {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const staticToken = this._peekToken();
|
||||
if (staticToken.type !== TokenType.Identifier) {
|
||||
return undefined;
|
||||
}
|
||||
if (this._fileContents!.slice(staticToken.start, staticToken.start + staticToken.length) !== 'static') {
|
||||
return undefined;
|
||||
}
|
||||
if (
|
||||
!this._isViperTypeAtomStartToken(this._peekToken(1)) &&
|
||||
!this._isAtViperAttributePrefix(1)
|
||||
) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
this._getNextToken(); // 消费 static
|
||||
|
||||
// 同 const / volatile:解析被修饰的主体期间禁止就地消费指针后缀 `*`,
|
||||
// 使 `static int*` 恒为 `(CStatic | int)*`。
|
||||
const wasDescriptorInner = this._isParsingViperTypeDescriptorInner;
|
||||
this._isParsingViperTypeDescriptorInner = true;
|
||||
const innerType = this._parseViperTypeAnnotationCore();
|
||||
this._isParsingViperTypeDescriptorInner = wasDescriptorInner;
|
||||
|
||||
this._viperStaticDescriptorInAnnotation = true;
|
||||
|
||||
return BinaryOperationNode.create(
|
||||
this._createViperStaticMarker(staticToken.start),
|
||||
innerType,
|
||||
this._createViperSyntheticBarToken(staticToken),
|
||||
OperatorType.BitwiseOr
|
||||
);
|
||||
}
|
||||
|
||||
// Viper 扩展:注解位置的 `__attribute__(...)` 修饰器前缀。
|
||||
// 解析层脱糖为等价的联合类型注解,与手写 `T | t.attr.section(".x")` 的 AST 同形
|
||||
// (编译器按 AST 扫描属性:TPC HandlesAssign._ExtractGlobalAttrs /
|
||||
|
||||
@@ -263,3 +263,89 @@ def f(static: int) -> int:
|
||||
require('fs').rmSync(testRoot, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
// ViperOS 全仓实际写法是**注解内前缀**(`buf_head: static uint32 = 0`,
|
||||
// 见 VKernel/Kernel/drivers/**),此前 LSP 只认语句起始形态 `static x: T`,
|
||||
// 导致 `static` 在注解里被当普通名引用 → 误报「"static" is not defined」。
|
||||
test('注解内 static 前缀(x: static T = v)解析、展示与跨模块拦截', () => {
|
||||
const { program, configOptions, uris, testRoot, serviceProvider } = buildProgram({
|
||||
'lib.vp': `buf_head: static int = 0
|
||||
|
||||
def touch() -> int:
|
||||
return buf_head
|
||||
`,
|
||||
'main.vp': `import lib
|
||||
|
||||
def use() -> int:
|
||||
return lib.buf_head
|
||||
`,
|
||||
});
|
||||
|
||||
try {
|
||||
const libDiags = getDiagnostics(program, configOptions, uris['lib.vp']);
|
||||
console.log('=== 注解内 static 诊断 ===');
|
||||
libDiags.forEach((d) => console.log(` L${d.line} [category=${d.category}] ${d.message}`));
|
||||
|
||||
assert.strictEqual(
|
||||
libDiags.filter((d) => d.category === 0).length,
|
||||
0,
|
||||
`注解内 static 不应报语法/未定义错误:${JSON.stringify(libDiags)}`
|
||||
);
|
||||
assert.strictEqual(
|
||||
libDiags.filter((d) => d.message.includes('static 符号')).length,
|
||||
0,
|
||||
`同模块自引用不应报 static 跨模块错误:${JSON.stringify(libDiags)}`
|
||||
);
|
||||
|
||||
// 注解脱糖为 `t.CStatic | int`,与语句起始形态一样展示为 `static int`。
|
||||
const types = collectAnnotatedVarTypes(program, uris['lib.vp']);
|
||||
console.log('=== 注解变量类型 ===');
|
||||
types.forEach((v, k) => console.log(` ${k}: ${v}`));
|
||||
assert(
|
||||
types.get('buf_head')!.includes('static') && types.get('buf_head')!.includes('int'),
|
||||
`buf_head 应展示为 static int,实际: ${types.get('buf_head')}`
|
||||
);
|
||||
|
||||
// 跨模块取参仍须报错(与语句起始形态同判据)。
|
||||
const mainDiags = getDiagnostics(program, configOptions, uris['main.vp']);
|
||||
console.log('=== 跨模块取参诊断 ===');
|
||||
mainDiags.forEach((d) => console.log(` L${d.line} [category=${d.category}] ${d.message}`));
|
||||
const staticDiags = mainDiags.filter((d) => d.message.includes('static 符号'));
|
||||
assert.strictEqual(
|
||||
staticDiags.length,
|
||||
1,
|
||||
`lib.buf_head 取用应报一条 static 跨模块错误:${JSON.stringify(mainDiags)}`
|
||||
);
|
||||
assert(
|
||||
staticDiags[0].message.includes('lib.buf_head'),
|
||||
`诊断文案应含 lib.buf_head:${JSON.stringify(staticDiags)}`
|
||||
);
|
||||
} finally {
|
||||
program.dispose();
|
||||
serviceProvider.dispose();
|
||||
require('fs').rmSync(testRoot, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
test('注解内 static 仍受软关键字边界约束:static 单独作为注解名不变', () => {
|
||||
const code = `static: int = 0
|
||||
static = 1
|
||||
`;
|
||||
const { program, configOptions, uris, testRoot, serviceProvider } = buildProgram({ 'main.vp': code });
|
||||
|
||||
try {
|
||||
const diagnostics = getDiagnostics(program, configOptions, uris['main.vp']);
|
||||
console.log('=== 注解内 static 边界诊断 ===');
|
||||
diagnostics.forEach((d) => console.log(` L${d.line} [category=${d.category}] ${d.message}`));
|
||||
|
||||
assert.strictEqual(
|
||||
diagnostics.filter((d) => d.category === 0).length,
|
||||
0,
|
||||
`static 作为普通标识符/变量名不应报语法错误:${JSON.stringify(diagnostics)}`
|
||||
);
|
||||
} finally {
|
||||
program.dispose();
|
||||
serviceProvider.dispose();
|
||||
require('fs').rmSync(testRoot, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
@@ -128,6 +128,69 @@ test('viperlib.vp 中 *args 透传 vsnprintf 不报 tuple 类型错误', () => {
|
||||
serviceProvider.dispose();
|
||||
});
|
||||
|
||||
/*
|
||||
* ViperOS VKernel/Kernel/drivers/serial/uart/serial.vp 场景:
|
||||
* export def printf(fmt: const str, *args) -> int:
|
||||
* ...
|
||||
* n: int = viperlib.vsnprintf(&buf, 1024, fmt, args)
|
||||
* 该文件**没有** `import t`,此前 getViperCPtrType 只认源码可见的 `t.CPtr`,
|
||||
* 取不到就退回 Python 语义的 tuple → 误报
|
||||
* 「无法将"tuple"类型的参数分配给函数"vsnprintf"中类型为"str"的参数"ap"」。
|
||||
* 修法:回退 builtins stub 的 `rawptr`(= t.CPtr,与 `T*` 注解同机制)。
|
||||
*/
|
||||
test('未 import t 的文件里 *args 仍是 va_list 指针(serial.vp 场景)', () => {
|
||||
const fs = require('fs');
|
||||
const testRoot = path.join(__dirname, '__viper_variadic_noimport__');
|
||||
fs.rmSync(testRoot, { recursive: true, force: true });
|
||||
fs.mkdirSync(testRoot, { recursive: true });
|
||||
const filePath = path.join(testRoot, 'main.vp');
|
||||
fs.writeFileSync(
|
||||
filePath,
|
||||
`import viperlib
|
||||
|
||||
export def printf(fmt: const str, *args) -> int:
|
||||
if fmt is None:
|
||||
return 0
|
||||
buf: [1024]char
|
||||
n: int = viperlib.vsnprintf(&buf, 1024, fmt, args)
|
||||
return n
|
||||
`,
|
||||
'utf8'
|
||||
);
|
||||
|
||||
const { program, serviceProvider, uri, configOptions } = buildProgram(filePath, testRoot, [
|
||||
TRANSPYC_INCLUDES,
|
||||
]);
|
||||
|
||||
try {
|
||||
const diagnostics = getDiagnostics(program, configOptions, uri);
|
||||
console.log('=== *args 无 import t 诊断 ===');
|
||||
diagnostics.forEach((d) => console.log(` ${d.range} [category=${d.category}] ${d.message}`));
|
||||
|
||||
const variadicErrors = diagnostics.filter(
|
||||
(d) =>
|
||||
d.message.includes('tuple') ||
|
||||
d.message.includes('参数 "ap"') ||
|
||||
d.message.includes('is not defined')
|
||||
);
|
||||
assert.strictEqual(
|
||||
variadicErrors.length,
|
||||
0,
|
||||
`*args 应绑定为 va_list 指针(rawptr),不应报 tuple/未定义:${JSON.stringify(variadicErrors)}`
|
||||
);
|
||||
|
||||
assert.strictEqual(
|
||||
diagnostics.filter((d) => d.category === 0).length,
|
||||
0,
|
||||
`不应有任何 error:${JSON.stringify(diagnostics)}`
|
||||
);
|
||||
} finally {
|
||||
program.dispose();
|
||||
serviceProvider.dispose();
|
||||
fs.rmSync(testRoot, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
test('memhub.vp 中 viperio.Buf.__sizeof__() 不报 Argument missing for parameter "self"', () => {
|
||||
const { program, serviceProvider, uri, configOptions } = buildProgram(MEMHUB_PATH);
|
||||
|
||||
|
||||
@@ -29,11 +29,13 @@
|
||||
- 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 only in front of `def`-family or `name :`)
|
||||
- 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 also accepted **inside a type annotation**: `buf_head: static int = 0` — the spelling actually used throughout ViperOS (`VKernel/Kernel/drivers/**`). It desugars to the very same `t.CStatic | T` and marks the declaration as a static symbol, so it renders as `static int` and the cross-module rule applies identically; previously the `static` inside the annotation was read as an ordinary name and reported `"static" is not defined`. `static` stays a **soft** keyword in this position too — it is consumed only when a type atom follows, so `static` as a variable name (`static: int = 0`, `static = 1`) is untouched. It is now highlighted as a modifier inside annotations as well
|
||||
- `*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
|
||||
|
||||
## 1.1.425
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@
|
||||
},
|
||||
{
|
||||
"name": "storage.modifier.viper",
|
||||
"match": "\\bstatic\\b(?=\\s+(?:def|fn|fun|func|function)\\b|\\s+[A-Za-z_][A-Za-z0-9_]*\\s*:)"
|
||||
"match": "\\bstatic\\b(?=\\s+(?:def|fn|fun|func|function)\\b|\\s*[A-Za-z_\\[])"
|
||||
},
|
||||
{
|
||||
"name": "storage.modifier.viper",
|
||||
|
||||
Reference in new issue
Block a user