Skip to content

Commit

Permalink
Did some cleanup and maintenance for the `adjustMemberAccessTypeForSe…
Browse files Browse the repository at this point in the history
…t` function. (#6340)
  • Loading branch information
erictraut authored Nov 6, 2023
1 parent aada758 commit 8eed1c1
Showing 1 changed file with 30 additions and 86 deletions.
116 changes: 30 additions & 86 deletions packages/pyright-internal/src/analyzer/typeEvaluator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5732,28 +5732,43 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
return resultType;
}

if (!isDescriptorApplied && memberInfo?.symbol.isClassVar()) {
if (flags & MemberAccessFlags.DisallowClassVarWrites) {
diag?.addMessage(Localizer.DiagnosticAddendum.memberSetClassVar().format({ name: memberName }));
// Check for an attempt to overwrite a ClassVar member from an instance.
if (
!isDescriptorApplied &&
memberInfo?.symbol.isClassVar() &&
(flags & MemberAccessFlags.DisallowClassVarWrites) !== 0
) {
diag?.addMessage(Localizer.DiagnosticAddendum.memberSetClassVar().format({ name: memberName }));
isDescriptorError = true;
}

// Check for an attempt to overwrite a final member variable.
const finalVarTypeDecl = memberInfo?.symbol
.getDeclarations()
.find((decl) => isFinalVariableDeclaration(decl));

if (finalVarTypeDecl && !ParseTreeUtils.isNodeContainedWithin(errorNode, finalVarTypeDecl.node)) {
// If a Final instance variable is declared in the class body but is
// being assigned within an __init__ method, it's allowed.
const enclosingFunctionNode = ParseTreeUtils.getEnclosingFunction(errorNode);
if (!enclosingFunctionNode || enclosingFunctionNode.name.value !== '__init__') {
diag?.addMessage(Localizer.Diagnostic.finalReassigned().format({ name: memberName }));
isDescriptorError = true;
return resultType;
}
}

const typeResult = adjustMemberAccessTypeForSet(
resultType,
memberInfo,
classType,
errorNode,
memberName,
diag
);

if (typeResult.typeErrors) {
// Check for an attempt to overwrite an instance variable that is
// read-only (e.g. in a named tuple).
if (
memberInfo?.isInstanceMember &&
isClass(memberInfo.classType) &&
ClassType.isReadOnlyInstanceVariables(memberInfo.classType)
) {
diag?.addMessage(Localizer.DiagnosticAddendum.readOnlyAttribute().format({ name: memberName }));
isDescriptorError = true;
}

return typeResult.type;
return resultType;
});

if (!isDescriptorError && usage.method === 'set' && usage.setType) {
Expand Down Expand Up @@ -6116,77 +6131,6 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
return { type: boundType ?? UnknownType.create(), typeErrors: !boundType };
}

function adjustMemberAccessTypeForSet(
type: Type,
memberInfo: ClassMember | undefined,
classType: ClassType,
errorNode: ExpressionNode,
memberName: string,
diag: DiagnosticAddendum | undefined
): TypeResult {
const isAccessedThroughObject = TypeBase.isInstance(classType);
const concreteSubtype = makeTopLevelTypeVarsConcrete(type);

// Check for an attempt to overwrite a final member variable.
const finalVarTypeDecl = memberInfo?.symbol.getDeclarations().find((decl) => isFinalVariableDeclaration(decl));

if (finalVarTypeDecl && !ParseTreeUtils.isNodeContainedWithin(errorNode, finalVarTypeDecl.node)) {
// If a Final instance variable is declared in the class body but is
// being assigned within an __init__ method, it's allowed.
const enclosingFunctionNode = ParseTreeUtils.getEnclosingFunction(errorNode);
if (!enclosingFunctionNode || enclosingFunctionNode.name.value !== '__init__') {
diag?.addMessage(Localizer.Diagnostic.finalReassigned().format({ name: memberName }));
return { type, typeErrors: true };
}
}

// Check for an attempt to overwrite an instance variable that is
// read-only (e.g. in a named tuple).
if (
memberInfo?.isInstanceMember &&
isClass(memberInfo.classType) &&
ClassType.isReadOnlyInstanceVariables(memberInfo.classType)
) {
diag?.addMessage(Localizer.DiagnosticAddendum.readOnlyAttribute().format({ name: memberName }));
return { type, typeErrors: true };
}

let enforceTargetType = false;

if (memberInfo && memberInfo.symbol.hasTypedDeclarations()) {
// If the member has a declared type, we will enforce it.
enforceTargetType = true;
} else {
// If the member has no declared type, we will enforce it
// if this assignment isn't within the enclosing class. If
// it is within the enclosing class, the assignment is used
// to infer the type of the member.
if (memberInfo && !memberInfo.symbol.getDeclarations().some((decl) => decl.node === errorNode)) {
enforceTargetType = true;
}
}

if (enforceTargetType) {
let effectiveType = type;

// If the code is patching a method (defined on the class)
// with an object-level function, strip the "self" parameter
// off the original type. This is sometimes done for test
// purposes to override standard behaviors of specific methods.
if (isAccessedThroughObject) {
if (!memberInfo!.isInstanceMember && isFunction(concreteSubtype)) {
if (FunctionType.isClassMethod(concreteSubtype) || FunctionType.isInstanceMethod(concreteSubtype)) {
effectiveType = FunctionType.clone(concreteSubtype, /* stripFirstParam */ true);
}
}
}

return { type: effectiveType };
}

return { type };
}

function isAsymmetricDescriptorClass(classType: ClassType): boolean {
// If the value has already been cached in this type, return the cached value.
if (classType.isAsymmetricDescriptor !== undefined) {
Expand Down

0 comments on commit 8eed1c1

Please sign in to comment.