mirror of
https://github.com/microsoft/pyright.git
synced 2024-10-05 20:38:25 +03:00
Added support for validating type of property setter.
This commit is contained in:
parent
1432bf5a2f
commit
0f38625e0a
@ -67,15 +67,9 @@ export enum EvaluatorFlags {
|
||||
DoNotSpecialize = 2
|
||||
}
|
||||
|
||||
export enum EvaluatorUsage {
|
||||
// The expression is being read.
|
||||
Get,
|
||||
|
||||
// The expression is being written.
|
||||
Set,
|
||||
|
||||
// The expression is being deleted.
|
||||
Delete
|
||||
interface EvaluatorUsage {
|
||||
method: 'get' | 'set' | 'del';
|
||||
typeToSet?: Type;
|
||||
}
|
||||
|
||||
export enum MemberAccessFlags {
|
||||
@ -135,14 +129,14 @@ export class ExpressionEvaluator {
|
||||
this._writeTypeToCache = writeTypeCallback;
|
||||
}
|
||||
|
||||
getType(node: ExpressionNode, usage = EvaluatorUsage.Get, flags = EvaluatorFlags.None): Type {
|
||||
getType(node: ExpressionNode, usage: EvaluatorUsage = { method: 'get' }, flags = EvaluatorFlags.None): Type {
|
||||
let typeResult = this._getTypeFromExpression(node, usage, flags);
|
||||
return typeResult.type;
|
||||
}
|
||||
|
||||
getTypeFromDecorator(node: DecoratorNode, functionType: Type): Type {
|
||||
const baseTypeResult = this._getTypeFromExpression(
|
||||
node.leftExpression, EvaluatorUsage.Get, EvaluatorFlags.DoNotSpecialize);
|
||||
node.leftExpression, { method: 'get' }, EvaluatorFlags.DoNotSpecialize);
|
||||
|
||||
let decoratorCall = baseTypeResult;
|
||||
|
||||
@ -172,10 +166,10 @@ export class ExpressionEvaluator {
|
||||
|
||||
// Gets a member type from an object and if it's a function binds
|
||||
// it to the object.
|
||||
getTypeFromObjectMember(memberName: string, usage: EvaluatorUsage,
|
||||
getTypeFromObjectMember(errorNode: ParseNode, memberName: string, usage: EvaluatorUsage,
|
||||
objectType: ObjectType): Type | undefined {
|
||||
|
||||
const memberType = this._getTypeFromClassMemberName(
|
||||
const memberType = this._getTypeFromClassMemberName(errorNode,
|
||||
objectType.getClassType(), memberName, usage, MemberAccessFlags.None);
|
||||
|
||||
let resultType = memberType;
|
||||
@ -332,13 +326,13 @@ export class ExpressionEvaluator {
|
||||
}
|
||||
|
||||
variableType = this.getType(statement.rightExpression,
|
||||
EvaluatorUsage.Get, EvaluatorFlags.None);
|
||||
{ method: 'get' }, EvaluatorFlags.None);
|
||||
hasDefaultValue = true;
|
||||
} else if (statement instanceof TypeAnnotationExpressionNode) {
|
||||
if (statement.valueExpression instanceof NameNode) {
|
||||
variableNameNode = statement.valueExpression;
|
||||
variableType = TypeUtils.convertClassToObject(
|
||||
this.getType(statement.typeAnnotation, EvaluatorUsage.Get,
|
||||
this.getType(statement.typeAnnotation, { method: 'get' },
|
||||
EvaluatorFlags.ConvertEllipsisToAny));
|
||||
}
|
||||
}
|
||||
@ -424,7 +418,7 @@ export class ExpressionEvaluator {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private _getTypeFromExpression(node: ExpressionNode, usage = EvaluatorUsage.Get,
|
||||
private _getTypeFromExpression(node: ExpressionNode, usage: EvaluatorUsage = { method: 'get' },
|
||||
flags = EvaluatorFlags.None): TypeResult {
|
||||
|
||||
// Is this type already cached?
|
||||
@ -481,7 +475,7 @@ export class ExpressionEvaluator {
|
||||
typeResult = this._getTypeFromSliceExpression(node);
|
||||
} else if (node instanceof AwaitExpressionNode) {
|
||||
typeResult = this._getTypeFromExpression(
|
||||
node.expression, EvaluatorUsage.Get, flags);
|
||||
node.expression, { method: 'get' }, flags);
|
||||
typeResult = {
|
||||
type: this.getTypeFromAwaitable(typeResult.type, node.expression),
|
||||
node
|
||||
@ -620,8 +614,8 @@ export class ExpressionEvaluator {
|
||||
if (baseType.isAny()) {
|
||||
type = baseType;
|
||||
} else if (baseType instanceof ClassType) {
|
||||
type = this._getTypeFromClassMemberName(baseType, node.memberName.nameToken.value,
|
||||
usage, MemberAccessFlags.SkipInstanceMembers);
|
||||
type = this._getTypeFromClassMemberName(node.memberName, baseType,
|
||||
node.memberName.nameToken.value, usage, MemberAccessFlags.SkipInstanceMembers);
|
||||
if (type) {
|
||||
type = TypeUtils.bindFunctionToClassOrObject(baseType, type);
|
||||
}
|
||||
@ -633,7 +627,7 @@ export class ExpressionEvaluator {
|
||||
return this._getTypeFromMemberAccessExpressionWithBaseType(node,
|
||||
{ type: classFromTypeObject, node: baseTypeResult.node }, usage, flags);
|
||||
} else {
|
||||
type = this._getTypeFromClassMemberName(baseType.getClassType(),
|
||||
type = this._getTypeFromClassMemberName(node.memberName, baseType.getClassType(),
|
||||
node.memberName.nameToken.value, usage, MemberAccessFlags.None);
|
||||
if (type) {
|
||||
type = TypeUtils.bindFunctionToClassOrObject(baseType, type);
|
||||
@ -688,7 +682,7 @@ export class ExpressionEvaluator {
|
||||
// If we're assigning a value to the __defaults__ member of a function,
|
||||
// note that the default value processing for that function should be disabled.
|
||||
if (baseType instanceof FunctionType && memberName === '__defaults__') {
|
||||
if (usage === EvaluatorUsage.Set) {
|
||||
if (usage.method === 'set') {
|
||||
baseType.setDefaultParameterCheckDisabled();
|
||||
}
|
||||
}
|
||||
@ -700,14 +694,14 @@ export class ExpressionEvaluator {
|
||||
|
||||
if (!type) {
|
||||
let operationName = 'access';
|
||||
if (usage === EvaluatorUsage.Set) {
|
||||
if (usage.method === 'set') {
|
||||
operationName = 'set';
|
||||
} else if (usage === EvaluatorUsage.Delete) {
|
||||
} else if (usage.method === 'del') {
|
||||
operationName = 'delete';
|
||||
}
|
||||
|
||||
this._addError(
|
||||
`Cannot ${ operationName } member '${ memberName }' for type '${ baseType.asString() }'`,
|
||||
`Cannot ${ operationName } member '${ memberName }' to type '${ baseType.asString() }'`,
|
||||
node.memberName);
|
||||
type = UnknownType.create();
|
||||
}
|
||||
@ -742,7 +736,7 @@ export class ExpressionEvaluator {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private _getTypeFromClassMemberName(classType: ClassType, memberName: string,
|
||||
private _getTypeFromClassMemberName(errorNode: ParseNode, classType: ClassType, memberName: string,
|
||||
usage: EvaluatorUsage, flags: MemberAccessFlags): Type | undefined {
|
||||
|
||||
// If this is a special type (like "List") that has an alias
|
||||
@ -780,14 +774,25 @@ export class ExpressionEvaluator {
|
||||
|
||||
if (!(flags & MemberAccessFlags.SkipGetCheck)) {
|
||||
if (type instanceof PropertyType) {
|
||||
if (usage === EvaluatorUsage.Get) {
|
||||
if (usage.method === 'get') {
|
||||
return type.getEffectiveReturnType();
|
||||
} else if (usage === EvaluatorUsage.Set) {
|
||||
// The type isn't important for set or delete usage.
|
||||
// We just need to return some defined type.
|
||||
return type.hasSetter() ? AnyType.create() : undefined;
|
||||
} else if (usage.method === 'set') {
|
||||
let setterFunctionType = type.getSetter();
|
||||
if (setterFunctionType) {
|
||||
// Strip off the "self" parameter.
|
||||
setterFunctionType = TypeUtils.stripFirstParameter(setterFunctionType);
|
||||
|
||||
// Validate that we can call the setter with the specified type.
|
||||
assert(usage.typeToSet !== undefined);
|
||||
const argList: FunctionArgument[] = [];
|
||||
argList.push({ argumentCategory: ArgumentCategory.Simple, type: usage.typeToSet! });
|
||||
return this._validateFunctionArguments(errorNode,
|
||||
argList, setterFunctionType, new TypeVarMap());
|
||||
}
|
||||
|
||||
return undefined;
|
||||
} else {
|
||||
assert(usage === EvaluatorUsage.Delete);
|
||||
assert(usage.method === 'del');
|
||||
return type.hasDeleter() ? AnyType.create() : undefined;
|
||||
}
|
||||
} else if (type instanceof ObjectType) {
|
||||
@ -795,9 +800,9 @@ export class ExpressionEvaluator {
|
||||
// method on the object.
|
||||
let accessMethodName: string;
|
||||
|
||||
if (usage === EvaluatorUsage.Get) {
|
||||
if (usage.method === 'get') {
|
||||
accessMethodName = '__get__';
|
||||
} else if (usage === EvaluatorUsage.Set) {
|
||||
} else if (usage.method === 'set') {
|
||||
accessMethodName = '__set__';
|
||||
} else {
|
||||
accessMethodName = '__del__';
|
||||
@ -808,7 +813,7 @@ export class ExpressionEvaluator {
|
||||
ClassMemberLookupFlags.SkipInstanceVariables);
|
||||
if (getMember) {
|
||||
if (getMember.symbolType instanceof FunctionType) {
|
||||
if (usage === EvaluatorUsage.Get) {
|
||||
if (usage.method === 'get') {
|
||||
type = getMember.symbolType.getEffectiveReturnType();
|
||||
} else {
|
||||
// The type isn't important for set or delete usage.
|
||||
@ -824,11 +829,11 @@ export class ExpressionEvaluator {
|
||||
}
|
||||
|
||||
if (!(flags & MemberAccessFlags.SkipGetAttributeCheck)) {
|
||||
if (usage === EvaluatorUsage.Get) {
|
||||
if (usage.method === 'get') {
|
||||
// See if the class has a "__getattribute__" or "__getattr__" method.
|
||||
// If so, arbitrary members are supported.
|
||||
let getAttribType = this._getTypeFromClassMemberName(classType,
|
||||
'__getattribute__', EvaluatorUsage.Get,
|
||||
let getAttribType = this._getTypeFromClassMemberName(errorNode, classType,
|
||||
'__getattribute__', { method: 'get' },
|
||||
MemberAccessFlags.SkipForMethodLookup |
|
||||
MemberAccessFlags.SkipObjectBaseClass);
|
||||
|
||||
@ -836,23 +841,23 @@ export class ExpressionEvaluator {
|
||||
return getAttribType.getEffectiveReturnType();
|
||||
}
|
||||
|
||||
let getAttrType = this._getTypeFromClassMemberName(classType,
|
||||
'__getattr__', EvaluatorUsage.Get, MemberAccessFlags.SkipForMethodLookup);
|
||||
let getAttrType = this._getTypeFromClassMemberName(errorNode, classType,
|
||||
'__getattr__', { method: 'get' }, MemberAccessFlags.SkipForMethodLookup);
|
||||
if (getAttrType && getAttrType instanceof FunctionType) {
|
||||
return getAttrType.getEffectiveReturnType();
|
||||
}
|
||||
} else if (usage === EvaluatorUsage.Set) {
|
||||
let setAttrType = this._getTypeFromClassMemberName(classType,
|
||||
'__setattr__', EvaluatorUsage.Get, MemberAccessFlags.SkipForMethodLookup);
|
||||
} else if (usage.method === 'set') {
|
||||
let setAttrType = this._getTypeFromClassMemberName(errorNode, classType,
|
||||
'__setattr__', { method: 'get' }, MemberAccessFlags.SkipForMethodLookup);
|
||||
if (setAttrType) {
|
||||
// The type doesn't matter for a set usage. We just need
|
||||
// to return a defined type.
|
||||
return AnyType.create();
|
||||
}
|
||||
} else {
|
||||
assert(usage === EvaluatorUsage.Delete);
|
||||
let delAttrType = this._getTypeFromClassMemberName(classType,
|
||||
'__detattr__', EvaluatorUsage.Get, MemberAccessFlags.SkipForMethodLookup);
|
||||
assert(usage.method === 'del');
|
||||
let delAttrType = this._getTypeFromClassMemberName(errorNode, classType,
|
||||
'__detattr__', { method: 'get' }, MemberAccessFlags.SkipForMethodLookup);
|
||||
if (delAttrType) {
|
||||
// The type doesn't matter for a delete usage. We just need
|
||||
// to return a defined type.
|
||||
@ -866,7 +871,7 @@ export class ExpressionEvaluator {
|
||||
|
||||
private _getTypeFromIndexExpression(node: IndexExpressionNode, usage: EvaluatorUsage): TypeResult {
|
||||
const baseTypeResult = this._getTypeFromExpression(node.baseExpression,
|
||||
EvaluatorUsage.Get, EvaluatorFlags.DoNotSpecialize);
|
||||
{ method: 'get' }, EvaluatorFlags.DoNotSpecialize);
|
||||
|
||||
const type = TypeUtils.doForSubtypes(baseTypeResult.type, subtype => {
|
||||
if (subtype.isAny()) {
|
||||
@ -903,17 +908,17 @@ export class ExpressionEvaluator {
|
||||
baseType: ObjectType, usage: EvaluatorUsage): Type {
|
||||
|
||||
let magicMethodName: string;
|
||||
if (usage === EvaluatorUsage.Get) {
|
||||
if (usage.method === 'get') {
|
||||
magicMethodName = '__getitem__';
|
||||
} else if (usage === EvaluatorUsage.Set) {
|
||||
} else if (usage.method === 'set') {
|
||||
magicMethodName = '__setitem__';
|
||||
} else {
|
||||
assert(usage === EvaluatorUsage.Delete);
|
||||
assert(usage.method === 'del');
|
||||
magicMethodName = '__delitem__';
|
||||
}
|
||||
|
||||
let itemMethodType = this._getTypeFromClassMemberName(
|
||||
baseType.getClassType(), magicMethodName, EvaluatorUsage.Get,
|
||||
let itemMethodType = this._getTypeFromClassMemberName(node,
|
||||
baseType.getClassType(), magicMethodName, { method: 'get' },
|
||||
MemberAccessFlags.SkipForMethodLookup);
|
||||
|
||||
if (!itemMethodType) {
|
||||
@ -941,7 +946,7 @@ export class ExpressionEvaluator {
|
||||
type: indexType
|
||||
}];
|
||||
|
||||
if (usage === EvaluatorUsage.Set) {
|
||||
if (usage.method === 'set') {
|
||||
argList.push({
|
||||
argumentCategory: ArgumentCategory.Simple,
|
||||
type: AnyType.create()
|
||||
@ -1119,8 +1124,8 @@ export class ExpressionEvaluator {
|
||||
}
|
||||
} else {
|
||||
const classType = callType.getClassType();
|
||||
let memberType = this._getTypeFromClassMemberName(
|
||||
classType, '__call__', EvaluatorUsage.Get,
|
||||
let memberType = this._getTypeFromClassMemberName(errorNode,
|
||||
classType, '__call__', { method: 'get' },
|
||||
MemberAccessFlags.SkipForMethodLookup);
|
||||
if (memberType && memberType instanceof FunctionType) {
|
||||
const callMethodType = TypeUtils.bindFunctionToClassOrObject(callType, memberType);
|
||||
@ -1207,8 +1212,8 @@ export class ExpressionEvaluator {
|
||||
let reportedErrorsForNewCall = false;
|
||||
|
||||
// Validate __new__
|
||||
let constructorMethodType = this._getTypeFromClassMemberName(type, '__new__',
|
||||
EvaluatorUsage.Get, MemberAccessFlags.SkipForMethodLookup |
|
||||
let constructorMethodType = this._getTypeFromClassMemberName(errorNode,
|
||||
type, '__new__', { method: 'get' }, MemberAccessFlags.SkipForMethodLookup |
|
||||
MemberAccessFlags.SkipObjectBaseClass);
|
||||
if (constructorMethodType) {
|
||||
constructorMethodType = TypeUtils.bindFunctionToClassOrObject(
|
||||
@ -1225,8 +1230,8 @@ export class ExpressionEvaluator {
|
||||
// Don't report errors for __init__ if __new__ already generated errors. They're
|
||||
// probably going to be entirely redundant anyway.
|
||||
if (!reportedErrorsForNewCall) {
|
||||
let initMethodType = this._getTypeFromClassMemberName(type, '__init__',
|
||||
EvaluatorUsage.Get, MemberAccessFlags.SkipForMethodLookup |
|
||||
let initMethodType = this._getTypeFromClassMemberName(errorNode,
|
||||
type, '__init__', { method: 'get' }, MemberAccessFlags.SkipForMethodLookup |
|
||||
MemberAccessFlags.SkipObjectBaseClass);
|
||||
if (initMethodType) {
|
||||
initMethodType = TypeUtils.bindFunctionToClassOrObject(
|
||||
@ -1290,8 +1295,8 @@ export class ExpressionEvaluator {
|
||||
errorNode);
|
||||
}
|
||||
} else if (callType instanceof ObjectType) {
|
||||
let memberType = this._getTypeFromClassMemberName(
|
||||
callType.getClassType(), '__call__', EvaluatorUsage.Get,
|
||||
let memberType = this._getTypeFromClassMemberName(errorNode,
|
||||
callType.getClassType(), '__call__', { method: 'get' },
|
||||
MemberAccessFlags.SkipForMethodLookup);
|
||||
|
||||
if (memberType && memberType instanceof FunctionType) {
|
||||
@ -1796,9 +1801,9 @@ export class ExpressionEvaluator {
|
||||
}
|
||||
|
||||
private _reportUsageErrorForReadOnly(node: ParseNode, usage: EvaluatorUsage) {
|
||||
if (usage === EvaluatorUsage.Set) {
|
||||
if (usage.method === 'set') {
|
||||
this._addError(`Constant value cannot be assigned`, node);
|
||||
} else if (usage === EvaluatorUsage.Delete) {
|
||||
} else if (usage.method === 'del') {
|
||||
this._addError(`Constant value cannot be deleted`, node);
|
||||
}
|
||||
}
|
||||
@ -2077,8 +2082,9 @@ export class ExpressionEvaluator {
|
||||
|
||||
// Create a helper lambda for object subtypes.
|
||||
let handleObjectSubtype = (subtype: ObjectType) => {
|
||||
let magicMethodType = this._getTypeFromClassMemberName(subtype.getClassType(),
|
||||
magicMethodName, EvaluatorUsage.Get, MemberAccessFlags.SkipForMethodLookup);
|
||||
let magicMethodType = this._getTypeFromClassMemberName(errorNode,
|
||||
subtype.getClassType(), magicMethodName,
|
||||
{ method: 'get' }, MemberAccessFlags.SkipForMethodLookup);
|
||||
|
||||
if (magicMethodType) {
|
||||
let functionArgs = args.map(arg => {
|
||||
@ -2212,13 +2218,13 @@ export class ExpressionEvaluator {
|
||||
let ifType: TypeResult | undefined;
|
||||
this._useExpressionTypeConstraint(typeConstraints, true, () => {
|
||||
ifType = this._getTypeFromExpression(node.ifExpression,
|
||||
EvaluatorUsage.Get, flags);
|
||||
{ method: 'get' }, flags);
|
||||
});
|
||||
|
||||
let elseType: TypeResult | undefined;
|
||||
this._useExpressionTypeConstraint(typeConstraints, false, () => {
|
||||
elseType = this._getTypeFromExpression(node.elseExpression,
|
||||
EvaluatorUsage.Get, flags);
|
||||
{ method: 'get' }, flags);
|
||||
});
|
||||
|
||||
let type = TypeUtils.combineTypes([ifType!.type, elseType!.type]);
|
||||
|
@ -35,7 +35,7 @@ import { StringTokenFlags } from '../parser/tokenizerTypes';
|
||||
import { ScopeUtils } from '../scopeUtils';
|
||||
import { AnalyzerFileInfo } from './analyzerFileInfo';
|
||||
import { AnalyzerNodeInfo } from './analyzerNodeInfo';
|
||||
import { EvaluatorFlags, EvaluatorUsage, ExpressionEvaluator } from './expressionEvaluator';
|
||||
import { EvaluatorFlags, ExpressionEvaluator } from './expressionEvaluator';
|
||||
import { ExpressionUtils } from './expressionUtils';
|
||||
import { ImportType } from './importResult';
|
||||
import { DefaultTypeSourceId } from './inferredType';
|
||||
@ -182,7 +182,7 @@ export abstract class SemanticAnalyzer extends ParseTreeWalker {
|
||||
argType = UnknownType.create();
|
||||
} else {
|
||||
argType = evaluator.getType(arg.valueExpression,
|
||||
EvaluatorUsage.Get, EvaluatorFlags.None);
|
||||
{ method: 'get' }, EvaluatorFlags.None);
|
||||
}
|
||||
|
||||
let isMetaclass = false;
|
||||
|
@ -32,7 +32,7 @@ import { KeywordType } from '../parser/tokenizerTypes';
|
||||
import { ScopeUtils } from '../scopeUtils';
|
||||
import { AnalyzerFileInfo } from './analyzerFileInfo';
|
||||
import { AnalyzerNodeInfo } from './analyzerNodeInfo';
|
||||
import { EvaluatorFlags, EvaluatorUsage, ExpressionEvaluator } from './expressionEvaluator';
|
||||
import { EvaluatorFlags, ExpressionEvaluator } from './expressionEvaluator';
|
||||
import { ExpressionUtils } from './expressionUtils';
|
||||
import { ImportResult, ImportType } from './importResult';
|
||||
import { DefaultTypeSourceId, TypeSourceId } from './inferredType';
|
||||
@ -652,8 +652,8 @@ export class TypeAnalyzer extends ParseTreeWalker {
|
||||
|
||||
if (subtype instanceof ObjectType) {
|
||||
let evaluator = this._createEvaluator();
|
||||
let memberType = evaluator.getTypeFromObjectMember(
|
||||
enterMethodName, EvaluatorUsage.Get, subtype);
|
||||
let memberType = evaluator.getTypeFromObjectMember(item.expression,
|
||||
enterMethodName, { method: 'get' }, subtype);
|
||||
|
||||
if (memberType) {
|
||||
let memberReturnType: Type;
|
||||
@ -2184,13 +2184,13 @@ export class TypeAnalyzer extends ParseTreeWalker {
|
||||
private _getTypeOfAnnotation(node: ExpressionNode): Type {
|
||||
let evaluator = this._createEvaluator();
|
||||
return TypeUtils.convertClassToObject(
|
||||
evaluator.getType(node, EvaluatorUsage.Get,
|
||||
evaluator.getType(node, { method: 'get' },
|
||||
EvaluatorFlags.None));
|
||||
}
|
||||
|
||||
private _getTypeOfExpression(node: ExpressionNode, specialize = true): Type {
|
||||
let evaluator = this._createEvaluator();
|
||||
return evaluator.getType(node, EvaluatorUsage.Get,
|
||||
return evaluator.getType(node, { method: 'get' },
|
||||
specialize ?
|
||||
EvaluatorFlags.ConvertEllipsisToAny :
|
||||
EvaluatorFlags.DoNotSpecialize | EvaluatorFlags.ConvertEllipsisToAny);
|
||||
@ -2198,12 +2198,12 @@ export class TypeAnalyzer extends ParseTreeWalker {
|
||||
|
||||
private _evaluateExpressionForAssignment(node: ExpressionNode, type: Type) {
|
||||
let evaluator = this._createEvaluator();
|
||||
evaluator.getType(node, EvaluatorUsage.Set, EvaluatorFlags.None);
|
||||
evaluator.getType(node, { method: 'set', typeToSet: type }, EvaluatorFlags.None);
|
||||
}
|
||||
|
||||
private _evaluateExpressionForDeletion(node: ExpressionNode): Type {
|
||||
let evaluator = this._createEvaluator();
|
||||
return evaluator.getType(node, EvaluatorUsage.Delete, EvaluatorFlags.None);
|
||||
return evaluator.getType(node, { method: 'del' }, EvaluatorFlags.None);
|
||||
}
|
||||
|
||||
private _updateExpressionTypeForNode(node: ExpressionNode, exprType: Type) {
|
||||
|
@ -923,10 +923,18 @@ export class PropertyType extends Type {
|
||||
this._getter = getter;
|
||||
}
|
||||
|
||||
getGetter() {
|
||||
return this._getter;
|
||||
}
|
||||
|
||||
hasSetter() {
|
||||
return this._setter !== undefined;
|
||||
}
|
||||
|
||||
getSetter() {
|
||||
return this._setter;
|
||||
}
|
||||
|
||||
setSetter(setter: FunctionType) {
|
||||
this._setter = setter;
|
||||
}
|
||||
@ -935,6 +943,10 @@ export class PropertyType extends Type {
|
||||
return this._deleter !== undefined;
|
||||
}
|
||||
|
||||
getDeleter() {
|
||||
return this._deleter;
|
||||
}
|
||||
|
||||
setDeleter(deleter: FunctionType) {
|
||||
this._deleter = deleter;
|
||||
}
|
||||
|
@ -39,6 +39,10 @@ val = a.read_write_prop
|
||||
|
||||
a.read_write_prop = 'hello'
|
||||
|
||||
# This should generate an error because the type
|
||||
# is incorrect.
|
||||
a.read_write_prop = ClassA()
|
||||
|
||||
# This should generate an error because this
|
||||
# property has no deleter.
|
||||
del a.read_write_prop
|
||||
|
@ -214,7 +214,7 @@ test('Execution1', () => {
|
||||
test('Properties1', () => {
|
||||
let analysisResults = TestUtils.typeAnalyzeSampleFiles(['properties1.py']);
|
||||
|
||||
validateResults(analysisResults, 4);
|
||||
validateResults(analysisResults, 5);
|
||||
});
|
||||
|
||||
test('Operators1', () => {
|
||||
|
Loading…
Reference in New Issue
Block a user