fix(vipeye-internal): 泛型函数显式类型实参调用不再误报不可下标

PEP 695 风格 fn[T] 的显式类型实参 fn[t.CInt] 在 Viper 方言下直接忽略并按普通函数求值,避免把函数误报为不可下标。新增 double[t.CInt](21) 回归测试;临时测试目录改到工作区内以适配沙箱写权限限制。
This commit is contained in:
TermiNexus committed 2026-08-08 13:01:17 +08:00
1 parent a20c643b72
commit ad3eecb077
2 files changed
+78 -1

No files matched your search

@@ -8093,6 +8093,18 @@ export function createTypeEvaluator(
isCArrayType(subtype))
);
// Viper 扩展:PEP 695 风格显式类型实参 `fn[TypeArgs]`。
// Viper 的 `def double[T](x: int) -> int` 中 `[T]` 已被预处理移除,
// 类型系统中 double 是无类型参数的普通函数;调用处 `double[t.CInt](21)`
// 的 `[t.CInt]` 只是显式类型标注,直接忽略并按 `double` 处理,
// 否则会把函数误报为 "不可下标"。
if (
IsViperDialect(viperIndexFileInfo.executionEnvironment.dialect) &&
(isFunction(baseTypeResult.type) || isOverloaded(baseTypeResult.type))
) {
return { type: baseTypeResult.type, isIncomplete };
}
const type = mapSubtypesExpandTypeVars(
baseTypeResult.type,
/* options */ undefined,
@@ -126,7 +126,7 @@ def make_box():
b = Box(42)
return b.get()
`;
const testDir = path.join(TRANSPYC_INCLUDES, '__typevar_call_test__');
const testDir = path.join(__dirname, '__typevar_call_test__');
const fs = require('fs');
fs.mkdirSync(testDir, { recursive: true });
const testFile = path.join(testDir, 'typevar.vp');
@@ -160,3 +160,68 @@ def make_box():
}
serviceProvider.dispose();
});
test('PEP 695 显式类型实参 double[t.CInt](21) 不报不可下标', () => {
const tempFile = new RealTempFile();
const serviceProvider = createServiceProvider(createFromRealFileSystem(tempFile), new NullConsole(), tempFile);
const projectRoot = UriEx.file(TRANSPYC_INCLUDES);
const configOptions = new ConfigOptions(projectRoot);
configOptions.internalTestMode = true;
configOptions.initializeTypeCheckingMode('standard');
const execEnv = new ExecutionEnvironment(
'python',
projectRoot,
configOptions.diagnosticRuleSet,
configOptions.defaultPythonVersion,
configOptions.defaultPythonPlatform,
[projectRoot],
/* skipNativeLibraries */ false,
/* defaultDialect */ 'viper'
);
configOptions.executionEnvironments = [execEnv];
const code = `import t
def double[T](x: int) -> int:
return x * 2
def test_explicit_type_arg():
r: t.CInt = double[t.CInt](21)
`;
const testDir = path.join(__dirname, '__typevar_call_test__');
const fs = require('fs');
fs.mkdirSync(testDir, { recursive: true });
const testFile = path.join(testDir, 'explicit_type_arg.vp');
fs.writeFileSync(testFile, code, 'utf8');
try {
const importResolver = new ImportResolver(serviceProvider, configOptions, new FullAccessHost(serviceProvider));
const program = new Program(importResolver, configOptions, serviceProvider);
const uri = UriEx.file(testFile);
program.setTrackedFiles([uri]);
while (program.analyze()) {
// 循环
}
const diagnostics = getDiagnostics(program, configOptions, uri);
console.log('=== explicit_type_arg.vp diagnostics ===');
diagnostics.forEach((d) => console.log(` ${d}`));
const notSubscriptable = diagnostics.filter(
(d) => d.includes('not subscriptable') || d.includes('不可下标')
);
assert.strictEqual(
notSubscriptable.length,
0,
`unexpected subscriptable errors: ${JSON.stringify(diagnostics)}`
);
program.dispose();
} finally {
fs.rmSync(testDir, { recursive: true, force: true });
}
serviceProvider.dispose();
});