mirror of
https://github.com/microsoft/pyright.git
synced 2025-12-23 09:19:29 +00:00
Fix completions for typeddicts with errors (#11177)
Some checks failed
Run mypy_primer on push / Run mypy_primer on push (push) Has been cancelled
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
Some checks failed
Run mypy_primer on push / Run mypy_primer on push (push) Has been cancelled
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
This commit is contained in:
parent
45159a96da
commit
1576956c32
2 changed files with 136 additions and 5 deletions
|
|
@ -12503,13 +12503,28 @@ export function createTypeEvaluator(
|
|||
|
||||
if (matchResults.argumentErrors) {
|
||||
// Evaluate types of all args. This will ensure that referenced symbols are
|
||||
// not reported as unaccessed.
|
||||
argList.forEach((arg) => {
|
||||
if (arg.valueExpression && !isSpeculativeModeInUse(arg.valueExpression)) {
|
||||
getTypeOfExpression(arg.valueExpression);
|
||||
// not reported as unaccessed. Also pass the expected parameter type as
|
||||
// inference context to enable proper completions even when there are errors.
|
||||
matchResults.argParams.forEach((argParam) => {
|
||||
if (argParam.argument.valueExpression && !isSpeculativeModeInUse(argParam.argument.valueExpression)) {
|
||||
getTypeOfExpression(
|
||||
argParam.argument.valueExpression,
|
||||
/* flags */ undefined,
|
||||
makeInferenceContext(argParam.paramType)
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
// Also evaluate any arguments that weren't matched to parameters
|
||||
argList.forEach((arg) => {
|
||||
if (arg.valueExpression && !isSpeculativeModeInUse(arg.valueExpression)) {
|
||||
// Check if this argument was already evaluated above
|
||||
const wasEvaluated = matchResults.argParams.some((argParam) => argParam.argument === arg);
|
||||
if (!wasEvaluated) {
|
||||
getTypeOfExpression(arg.valueExpression);
|
||||
}
|
||||
}
|
||||
});
|
||||
// Use a return type of Unknown but attach a "possible type" to it
|
||||
// so the completion provider can suggest better completions.
|
||||
const possibleType = FunctionType.getEffectiveReturnType(typeResult.type);
|
||||
|
|
@ -12786,7 +12801,10 @@ export function createTypeEvaluator(
|
|||
if (argParam.argType) {
|
||||
argType = argParam.argType;
|
||||
} else {
|
||||
const argTypeResult = getTypeOfArg(argParam.argument, /* inferenceContext */ undefined);
|
||||
const argTypeResult = getTypeOfArg(
|
||||
argParam.argument,
|
||||
makeInferenceContext(argParam.paramType, isTypeIncomplete)
|
||||
);
|
||||
argType = argTypeResult.type;
|
||||
if (argTypeResult.isIncomplete) {
|
||||
isTypeIncomplete = true;
|
||||
|
|
|
|||
|
|
@ -1480,3 +1480,116 @@ test('overloaded Literal[...] suggestions in call arguments', async () => {
|
|||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('nested TypedDict completion with Unpack - without other fields', async () => {
|
||||
const code = `
|
||||
// @filename: test.py
|
||||
//// from typing import Unpack, TypedDict
|
||||
////
|
||||
//// class InnerDict(TypedDict):
|
||||
//// a: int
|
||||
//// b: str
|
||||
////
|
||||
//// class OuterDict(TypedDict):
|
||||
//// inner: InnerDict
|
||||
//// field_1: str
|
||||
////
|
||||
//// def test_inner_dict(**kwargs: Unpack[OuterDict]):
|
||||
//// pass
|
||||
////
|
||||
//// test_inner_dict(inner={[|/*marker*/|]})
|
||||
`;
|
||||
|
||||
const state = parseAndGetTestState(code).state;
|
||||
|
||||
await state.verifyCompletion('included', 'markdown', {
|
||||
marker: {
|
||||
completions: [
|
||||
{
|
||||
kind: CompletionItemKind.Constant,
|
||||
label: "'a'",
|
||||
textEdit: { range: state.getPositionRange('marker'), newText: "'a'" },
|
||||
},
|
||||
{
|
||||
kind: CompletionItemKind.Constant,
|
||||
label: "'b'",
|
||||
textEdit: { range: state.getPositionRange('marker'), newText: "'b'" },
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('nested TypedDict completion with Unpack - with other fields', async () => {
|
||||
const code = `
|
||||
// @filename: test.py
|
||||
//// from typing import Unpack, TypedDict
|
||||
////
|
||||
//// class InnerDict(TypedDict):
|
||||
//// a: int
|
||||
//// b: str
|
||||
////
|
||||
//// class OuterDict(TypedDict):
|
||||
//// inner: InnerDict
|
||||
//// field_1: str
|
||||
////
|
||||
//// def test_inner_dict(**kwargs: Unpack[OuterDict]):
|
||||
//// pass
|
||||
////
|
||||
//// test_inner_dict(field_1="test", inner={[|/*marker*/|]})
|
||||
`;
|
||||
|
||||
const state = parseAndGetTestState(code).state;
|
||||
|
||||
await state.verifyCompletion('included', 'markdown', {
|
||||
marker: {
|
||||
completions: [
|
||||
{
|
||||
kind: CompletionItemKind.Constant,
|
||||
label: '"a"',
|
||||
textEdit: { range: state.getPositionRange('marker'), newText: '"a"' },
|
||||
},
|
||||
{
|
||||
kind: CompletionItemKind.Constant,
|
||||
label: '"b"',
|
||||
textEdit: { range: state.getPositionRange('marker'), newText: '"b"' },
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('simple nested TypedDict completion - no Unpack', async () => {
|
||||
const code = `
|
||||
// @filename: test.py
|
||||
//// from typing import TypedDict
|
||||
////
|
||||
//// class InnerDict(TypedDict):
|
||||
//// a: int
|
||||
//// b: str
|
||||
////
|
||||
//// def test_func(inner: InnerDict):
|
||||
//// pass
|
||||
////
|
||||
//// test_func(inner={[|/*marker*/|]})
|
||||
`;
|
||||
|
||||
const state = parseAndGetTestState(code).state;
|
||||
|
||||
await state.verifyCompletion('included', 'markdown', {
|
||||
marker: {
|
||||
completions: [
|
||||
{
|
||||
kind: CompletionItemKind.Constant,
|
||||
label: "'a'",
|
||||
textEdit: { range: state.getPositionRange('marker'), newText: "'a'" },
|
||||
},
|
||||
{
|
||||
kind: CompletionItemKind.Constant,
|
||||
label: "'b'",
|
||||
textEdit: { range: state.getPositionRange('marker'), newText: "'b'" },
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue