fix(vipeye-internal): str/bytes 支持一元 '*' 解引用,结果为 char
Validation / Typecheck (push) Has been cancelled
Validation / Style (push) Has been cancelled
Validation / Test macos-latest (push) Has been cancelled
Validation / Test ubuntu-latest (push) Has been cancelled
Validation / Test windows-latest (push) Has been cancelled
Validation / Build (push) Has been cancelled
Validation / Required (push) Has been cancelled

Viper 里 str 与 bytes 都是 C 的 char*(i8*),`*s` 应取首字符且类型为 char(i8);
此前被当成非指针类型处理,报「类型 "str" 不支持运算符 "*"」(reportOperatorIssue)。
在 getTypeOfViperDeref 中把 str/bytes 与已有的 CPtr[X] 分支并列处理。

回归用例:viperUnaryPointer.test.ts 新增 *fmt(str)与 *buf(bytes)→ char,
并断言不报运算符错误。版本号提升至 1.1.419 并更新 CHANGELOG。
This commit is contained in:
TermiNexus committed 2026-09-22 21:01:18 +08:00
1 parent 4bc462e0f3
commit 00b781a5cc
9 files changed
+81 -9

No files matched your search

+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "vipeye-internal",
"version": "1.1.418",
"version": "1.1.419",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "vipeye-internal",
"version": "1.1.418",
"version": "1.1.419",
"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.418",
"version": "1.1.419",
"license": "MIT",
"private": true,
"files": [
@@ -850,6 +850,7 @@ export function getTypeOfUnaryOperation(
// Viper 扩展:一元 '*'(解引用)类型求值,与 C 语义一致。
// - CPtr[X](含 `X | t.CPtr` 化简形式)→ 返回目标类型 X(类对象转为实例)
// - str / bytes(C 的 char*,即 i8*)→ 返回 char(i8)
// - Unknown/Any → 透传
// - 非指针类型 → 报错(C 语义:invalid type argument of unary '*')
function getTypeOfViperDeref(
@@ -867,6 +868,12 @@ function getTypeOfViperDeref(
return subtype;
}
// Viper:str / bytes 都是 C 的 char*(i8*),解引用取首字符,类型为 char(i8)。
if (isClassInstance(subtype) && ClassType.isBuiltIn(subtype, ['str', 'bytes'])) {
resultTypes.push(evaluator.getBuiltInObject(node, 'char'));
return subtype;
}
if (isClassInstance(subtype) && isCPtrType(subtype)) {
const typeArgs = subtype.priv.typeArgs;
if (typeArgs && typeArgs.length > 0) {
@@ -458,3 +458,64 @@ def main() -> int:
program.dispose();
serviceProvider.dispose();
});
// Viper:str / bytes 都是 C 的 char*(i8*),`*s` 取首字符,类型为 char(i8)。
// 修复「类型 "str" 不支持运算符 "*"」(reportOperatorIssue)。
test('Viper 解引用 str/bytes 返回 char', () => {
const code = `import t
def test_str_deref(fmt: str) -> int:
c: char = *fmt
return 0
def test_bytes_deref(buf: bytes) -> int:
ch: char = *buf
return 0
`;
const { program, configOptions, parseResults, uri, serviceProvider } = buildProgramForCode(code);
const evaluator = program.evaluator as TypeEvaluator | undefined;
assert(evaluator, 'evaluator not available');
const ev = evaluator;
const derefs: { line: number; type: string }[] = [];
class UnaryCollector extends ParseTreeWalker {
override visitUnaryOperation(node: UnaryOperationNode) {
if (node.d.operator !== OperatorType.Multiply) {
return true;
}
const type = ev.getType(node as any);
derefs.push({
line: convertOffsetToPosition(node.start, parseResults.parserOutput.lines).line + 1,
type: type ? program.printType(type) : '(undefined)',
});
return true;
}
}
new UnaryCollector().walk(parseResults.parserOutput.parseTree);
console.log('=== str/bytes 解引用类型 ===');
derefs.forEach((d) => console.log(` L${d.line}: ${d.type}`));
// L4: *fmt(fmt: str,即 i8*)→ char
const strDeref = derefs.find((d) => d.line === 4);
assert(strDeref, '*fmt not found (L4)');
assert.strictEqual(strDeref.type, 'char', `*fmt 应为 char,实际: ${strDeref.type}`);
// L8: *buf(buf: bytes,即 i8*)→ char
const bytesDeref = derefs.find((d) => d.line === 8);
assert(bytesDeref, '*buf not found (L8)');
assert.strictEqual(bytesDeref.type, 'char', `*buf 应为 char,实际: ${bytesDeref.type}`);
// 不得报「类型 "str" / "bytes" 不支持运算符 "*"」
const diagnostics = getDiagnostics(program, configOptions, uri);
console.log('=== 诊断 ===');
diagnostics.forEach((d) => console.log(` ${d}`));
assert(
!diagnostics.some((d) => d.includes('不支持运算符')),
`str/bytes 解引用不应报运算符错误: ${JSON.stringify(diagnostics)}`
);
program.dispose();
serviceProvider.dispose();
});
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "vipeye",
"version": "1.1.418",
"version": "1.1.419",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "vipeye",
"version": "1.1.418",
"version": "1.1.419",
"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.418",
"version": "1.1.419",
"license": "MIT",
"author": {
"name": "TermiNexus"
+4
View File
@@ -1,5 +1,9 @@
# Change Log
## 1.1.419
- Dereferencing `str` / `bytes` with the unary `*` operator no longer reports `reportOperatorIssue`: both are C `char*` (`i8*`) in Viper, so `*s` yields `char` (`i8`). Mirrors `t.CPtr[X]` dereferencing, which already returned the pointee type
## 1.1.418
- **Built-in C basic type names**: `long` (`t.CLong`) and `longlong` (`t.CLongLong`) are now built-in typedefs, mirroring the compiler's `_CNAME_ALIAS` table (`char`, `short`, `long`, `unsigned` and friends are language built-ins that need no import). C's `long long` cannot be spelled with a space, so Viper writes `longlong`; the unsigned form stays available as `t.CUnsignedLongLong`
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "vscode-vipeye",
"version": "1.1.418",
"version": "1.1.419",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "vscode-vipeye",
"version": "1.1.418",
"version": "1.1.419",
"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.418",
"version": "1.1.419",
"private": true,
"license": "MIT",
"author": {