fix(vipeye-internal): 泛型函数显式类型实参调用不再误报不可下标
PEP 695 风格 fn[T] 的显式类型实参 fn[t.CInt] 在 Viper 方言下直接忽略并按普通函数求值,避免把函数误报为不可下标。新增 double[t.CInt](21) 回归测试;临时测试目录改到工作区内以适配沙箱写权限限制。
This commit is contained in:
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();
|
||||
});
|
||||
Reference in new issue
Block a user