diff --git a/server/src/analyzer/typeEvaluator.ts b/server/src/analyzer/typeEvaluator.ts index 473e7d019..d93e26ec1 100644 --- a/server/src/analyzer/typeEvaluator.ts +++ b/server/src/analyzer/typeEvaluator.ts @@ -3429,6 +3429,10 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { const expectedType = specializeType(argParam.paramType, typeVarMap, makeConcrete); const exprType = getTypeOfExpression(argParam.argument.valueExpression, expectedType); argType = exprType.type; + + if (argParam.argument && argParam.argument.name && !isSpeculativeMode) { + writeTypeCache(argParam.argument.name, argType); + } } else { argType = getTypeForArgument(argParam.argument); } @@ -7647,6 +7651,22 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { return AnalyzerNodeInfo.getFileInfo(node)!; } + function getDeclarationFromFunctionNamedParameter(type: FunctionType, paramName: string): Declaration | undefined { + if (type.category === TypeCategory.Function && type.details.declaration) { + const functionDecl = type.details.declaration; + if (functionDecl.type === DeclarationType.Method || functionDecl.type === DeclarationType.Function) { + const functionNode = functionDecl.node; + const functionScope = AnalyzerNodeInfo.getScope(functionNode)!; + const paramSymbol = functionScope.lookUpSymbol(paramName)!; + if (paramSymbol) { + return paramSymbol.getDeclarations().find(decl => decl.type === DeclarationType.Parameter); + } + } + } + + return undefined; + } + function getDeclarationsForNameNode(node: NameNode): Declaration[] | undefined { const declarations: Declaration[] = []; const nameValue = node.value; @@ -7724,6 +7744,32 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator { declarations.push(aliasDeclaration); } } + } else if (node.parent && node.parent.nodeType === ParseNodeType.Argument && node === node.parent.name) { + // The target node is the name in a named argument. We need to determine whether + // the corresponding named parameter can be determined from the context. + const argNode = node.parent; + const paramName = node.value; + if (argNode.parent && argNode.parent.nodeType === ParseNodeType.Call) { + const baseType = getType(argNode.parent.leftExpression, undefined, EvaluatorFlags.DoNotSpecialize); + + if (baseType.category === TypeCategory.Function && baseType.details.declaration) { + const paramDecl = getDeclarationFromFunctionNamedParameter(baseType, paramName); + if (paramDecl) { + declarations.push(paramDecl); + } + } else if (baseType.category === TypeCategory.Class) { + const initMethodType = getTypeFromObjectMember(argNode.parent.leftExpression, + ObjectType.create(baseType), '__init__', { method: 'get' }, + new DiagnosticAddendum(), + MemberAccessFlags.SkipForMethodLookup | MemberAccessFlags.SkipObjectBaseClass); + if (initMethodType && initMethodType.category === TypeCategory.Function) { + const paramDecl = getDeclarationFromFunctionNamedParameter(initMethodType, paramName); + if (paramDecl) { + declarations.push(paramDecl); + } + } + } + } } else { const scope = ScopeUtils.getScopeForNode(node); if (scope) { diff --git a/server/src/languageService/referencesProvider.ts b/server/src/languageService/referencesProvider.ts index 3ee8e099f..66636215b 100644 --- a/server/src/languageService/referencesProvider.ts +++ b/server/src/languageService/referencesProvider.ts @@ -132,7 +132,10 @@ export class ReferencesProvider { const symbolDeclType = resolvedDeclarations[0].type; // Parameters are local to a scope, so they don't require a global search. - const requiresGlobalSearch = symbolDeclType !== DeclarationType.Parameter; + // If it's a named argument referring to a parameter, we still need to perform + // the global search. + const requiresGlobalSearch = symbolDeclType !== DeclarationType.Parameter || + (node.parent !== undefined && node.parent.nodeType === ParseNodeType.Argument); const results: ReferencesResult = { requiresGlobalSearch,