Skip to content
This repository was archived by the owner on Apr 14, 2022. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
8c80eff
Fix #601
Feb 16, 2019
bbb23de
Fix #607
Feb 16, 2019
41cde05
Update test
Feb 16, 2019
57b82ce
Fix version conditionals
Feb 18, 2019
3d536ee
Optional/None
Feb 19, 2019
73ce5c6
Make sure constructors are processed
Feb 19, 2019
dcc7ef6
Null checks
Feb 19, 2019
5faad5a
Merge master
Feb 19, 2019
ec216d3
Merge issues
Feb 19, 2019
cc07cd3
Merge master
Feb 19, 2019
e0773df
Merge branch 'master' of https://github.com/Microsoft/python-language…
Feb 19, 2019
4cb5a4e
Using
Feb 19, 2019
94a8c00
Merge master
Feb 19, 2019
98cdb58
Merge master
Feb 20, 2019
4336c0b
Merge branch '607' into 462
Feb 20, 2019
b0ebb77
Attach object as base to 3x classes
Feb 20, 2019
2e327a8
using
Feb 20, 2019
c210c3d
Partial
Feb 20, 2019
2ce446e
Handle or expression in parameters
Feb 21, 2019
d2d31cb
Merge branch '462' of https://github.com/MikhailArkhipov/python-langu…
Feb 21, 2019
256d168
Optimistically return instance if callable turns out to be a property…
Feb 21, 2019
7964942
Remove async
Feb 22, 2019
e7e4c9c
Pass ctor arguments
Feb 24, 2019
dae1280
Test updates
Feb 25, 2019
26bb195
Merge master
Feb 25, 2019
9dc8fbf
Tests
Feb 25, 2019
bff4fa9
Fix SO
Feb 25, 2019
293c1b3
Generic functions - partial
Feb 25, 2019
1f65c44
Merge async removal
Feb 25, 2019
65dfb49
Generic args
Feb 25, 2019
29e0f28
Merge master
Feb 25, 2019
690be51
Merge branch 'master' of https://github.com/Microsoft/python-language…
Feb 26, 2019
bf0feaa
Tests and formatting
Feb 26, 2019
395e48d
Fix NRE
Feb 26, 2019
874f4a9
Merge master
Feb 26, 2019
6eaa7d9
Don't eval stub functions
Feb 26, 2019
b29e0c0
Merge branch 'master' of https://github.com/Microsoft/python-language…
Feb 26, 2019
79213ec
Merge branch 'master' into 462
Feb 26, 2019
0da60df
Merge master
Feb 26, 2019
dd04fac
Fix doc strings
Feb 26, 2019
5db9e30
Handle standalone generic + enable test
Feb 26, 2019
931c0a2
Tests update
Feb 26, 2019
38c9438
More gotodef tests
Feb 26, 2019
ff7b5db
Property handle constructors defining members
Feb 26, 2019
e083287
Merge master
Feb 26, 2019
1d7c946
fix function doc
Feb 27, 2019
9c01504
Debug code
Feb 27, 2019
2ab6aa1
Add as name check
Feb 27, 2019
1128e67
Fix import
Feb 27, 2019
5a808b6
Add AsyncLocal
Feb 28, 2019
57779f4
Revert "Add AsyncLocal"
Feb 28, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/Analysis/Ast/Impl/Analyzer/DocumentAnalysis.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ public DocumentAnalysis(IDocument document, int version, IGlobalScope globalScop
public IGlobalScope GlobalScope { get; }

/// <summary>
/// Expression evaluator used in the analysis.
/// ValueExpression evaluator used in the analysis.
/// </summary>
public IExpressionEvaluator ExpressionEvaluator { get; }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ public IMember GetValueFromCallable(CallExpression expr) {
}

var target = GetValueFromExpression(expr.Target);
// Try generics
var result = GetValueFromGeneric(target, expr);
if (result != null) {
return result;
Expand Down
220 changes: 142 additions & 78 deletions src/Analysis/Ast/Impl/Analyzer/Evaluation/ExpressionEval.Generics.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
// See the Apache Version 2.0 License for specific language governing
// permissions and limitations under the License.

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Python.Analysis.Specializations.Typing;
Expand All @@ -26,97 +27,72 @@ namespace Microsoft.Python.Analysis.Analyzer.Evaluation {
internal sealed partial class ExpressionEval {
/// <summary>
/// Creates specific type from expression that involves generic type
/// and the specific type arguments.
/// and the specific type arguments, such as Generic[T] or constructor
/// of a generic class.
/// </summary>
/// <example>
/// x = List[T]
/// </example>
/// <example>
/// T = TypeVar('T', Exception)
/// class A(Generic[T]): ...
/// x = A(TypeError)
/// </example>
private IMember GetValueFromGeneric(IMember target, Expression expr) {
if (!(target is PythonClassType c && c.IsGeneric()) && !(target is IGenericType)) {
return null;
}
// Evaluate index to check if the result is generic parameter.
// If it is, then this is a declaration expression such as Generic[T]
// rather than specific type instantiation as in List[str].

IPythonType[] specificTypes;
var returnInstance = false;
switch (expr) {
// Indexing returns type as from A[int]
case IndexExpression indexExpr:
// Generic[T1, T2, ...] or A[type]()
var indices = EvaluateIndex(indexExpr);
// See which ones are generic parameters as defined by TypeVar()
// and which are specific types. Normally there should not be a mix.
var genericTypeArgs = indices.OfType<IGenericTypeParameter>().ToArray();
specificTypes = indices.Where(i => !(i is IGenericTypeParameter)).OfType<IPythonType>().ToArray();

if (specificTypes.Length == 0 && genericTypeArgs.Length > 0) {
// The expression is still generic. For example, generic return
// annotation of a class method, such as 'def func(self) -> A[_E]: ...'.
// Leave it alone, we don't want resolve generic with generic.
return null;
}

if (genericTypeArgs.Length > 0 && genericTypeArgs.Length != indices.Count) {
// TODO: report that some type arguments are not declared with TypeVar.
}
using (OpenScope(target.GetPythonType()?.DeclaringModule, GetScope(target), out _)) {
Comment thread
AlexanderSher marked this conversation as resolved.
// Try generics
// Evaluate index to check if the result is a generic parameter.
// If it is, then this is a declaration expression such as Generic[T]
// rather than specific type instantiation as in List[str].
switch (expr) {
// Indexing returns type as from A[int]
case IndexExpression indexExpr when target is IGenericType gt:
// Generic[T1, T2, ...]
var indices = EvaluateIndex(indexExpr);
return CreateSpecificTypeFromIndex(gt, indices, expr);

if (specificTypes.Length > 0 && specificTypes.Length != indices.Count) {
// TODO: report that arguments are not specific types or are not declared.
}
case CallExpression callExpr when target is PythonClassType c1:
// Alternative instantiation:
// class A(Generic[T]): ...
// x = A(1234)
var arguments = EvaluateCallArgs(callExpr).ToArray();
return CreateClassInstance(c1, arguments, callExpr);
}
}
return null;
}

// Optimistically use what we have
if (target is IGenericType gt) {
if (gt.Name.EqualsOrdinal("Generic")) {
if (genericTypeArgs.Length > 0) {
// Generic[T1, T2, ...] expression. Create generic base for the class.
return new GenericClassBaseType(genericTypeArgs, Module, GetLoc(expr));
} else {
// TODO: report too few type arguments for Generic[].
return UnknownType;
}
}

if (specificTypes.Length > 0) {
// If target is a generic type and indexes are specific types, create specific class
return gt.CreateSpecificType(new ArgumentSet(specificTypes), Module, GetLoc(expr));
} else {
// TODO: report too few type arguments for the Generic[].
return UnknownType;
}
}
break;
/// <summary>
/// Given generic type and list of indices in the expression like
/// Generic[T1, T2, ...] or List[str] creates generic class base
/// (if the former) on specific type (if the latter).
/// </summary>
private IMember CreateSpecificTypeFromIndex(IGenericType gt, IReadOnlyList<IMember> indices, Expression expr) {
// See which ones are generic parameters as defined by TypeVar()
// and which are specific types. Normally there should not be a mix.
var genericTypeArgs = indices.OfType<IGenericTypeDefinition>().ToArray();
var specificTypes = indices.Where(i => !(i is IGenericTypeDefinition)).OfType<IPythonType>().ToArray();

case CallExpression callExpr:
// Alternative instantiation:
// class A(Generic[T]): ...
// x = A(1234)
specificTypes = EvaluateCallArgs(callExpr).Select(x => x.GetPythonType()).ToArray();
// Callable returns instance (as opposed to a type with index expression)
returnInstance = true;
break;
if (genericTypeArgs.Length > 0 && genericTypeArgs.Length != indices.Count) {
// TODO: report that some type arguments are not declared with TypeVar.
}
if (specificTypes.Length > 0 && specificTypes.Length != indices.Count) {
// TODO: report that arguments are not specific types or are not declared.
}

default:
return null;
if (gt.Name.EqualsOrdinal("Generic")) {
// Generic[T1, T2, ...] expression. Create generic base for the class.
if (genericTypeArgs.Length > 0) {
return new GenericClassParameter(genericTypeArgs, Module, GetLoc(expr));
} else {
// TODO: report too few type arguments for Generic[].
return UnknownType;
}
}

// This is a bit of a hack since we don't have GenericClassType at the moment.
// The reason is that PythonClassType is created before ClassDefinition is walked
// as we resolve classes on demand. Therefore we don't know if class is generic
// or not at the time of the PythonClassType creation.
// TODO: figure out if we could make GenericClassType: PythonClassType, IGenericType instead.
if (target is PythonClassType cls) {
var location = GetLoc(expr);
var type = cls.CreateSpecificType(new ArgumentSet(specificTypes), Module, location);
return returnInstance ? new PythonInstance(type, GetLoc(expr)) : (IMember)type;
// For other types just use supplied arguments
if (indices.Count > 0) {
return gt.CreateSpecificType(new ArgumentSet(indices), Module, GetLoc(expr));
}
return null;
// TODO: report too few type arguments for the generic expression.
return UnknownType;

}

private IReadOnlyList<IMember> EvaluateIndex(IndexExpression expr) {
Expand All @@ -141,5 +117,93 @@ private IReadOnlyList<IMember> EvaluateCallArgs(CallExpression expr) {
}
return indices;
}

/// <summary>
/// Given generic class type and the passed constructor arguments
/// creates specific type and instance of the type. Attempts to match
/// supplied arguments to either __init__ signature or to the
/// list of generic definitions in Generic[T1, T2, ...].
/// </summary>
private IMember CreateClassInstance(PythonClassType cls, IReadOnlyList<IMember> constructorArguments, CallExpression callExpr) {
// Look at the constructor arguments and create argument set
// based on the __init__ definition.
var location = GetLoc(callExpr);
var initFunc = cls.GetMember(@"__init__") as IPythonFunctionType;
var initOverload = initFunc?.DeclaringType == cls ? initFunc.Overloads.FirstOrDefault() : null;

var argSet = initOverload != null
? new ArgumentSet(initFunc, 0, null, callExpr, this)
: new ArgumentSet(constructorArguments);

argSet.Evaluate();
var specificType = cls.CreateSpecificType(argSet, Module, location);
return new PythonInstance(specificType, location);
}

private ScopeStatement GetScope(IMember m) {
switch (m.GetPythonType()) {
case IPythonClassType ct:
return ct.ClassDefinition;
case IPythonFunctionType ct:
return ct.FunctionDefinition;
}
return null;
}

public static IReadOnlyList<IPythonType> GetTypeArgumentsFromParameters(IPythonFunctionOverload o, IArgumentSet args) {
if (o.Parameters.Any(p => p.IsGeneric)) {
// Declaring class is not generic, but the function is and arguments
// should provide actual specific types.
// TODO: handle keyword and dict args
var list = new List<IPythonType>();
for (var i = 0; i < Math.Min(o.Parameters.Count, args.Arguments.Count); i++) {
if (o.Parameters[i].IsGeneric) {
list.AddRange(GetSpecificTypeFromArgumentValue(args.Arguments[i].Value));
}
}
return list;
}
return null;
}

/// <summary>
/// Given argument attempts to extract specific types for the function generic
/// parameter(s). Handles common cases such as dictionary, list and tuple.
/// Typically used on a value that is being passed to the function in place
/// of the generic parameter.
/// </summary>
/// <remarks>
/// Consider 'def func(x: Mapping[K, V]) -> K: ...'
/// </remarks>
private static IReadOnlyList<IPythonType> GetSpecificTypeFromArgumentValue(object argumentValue) {
var specificTypes = new List<IPythonType>();
switch (argumentValue) {
case IPythonDictionary dict:
var keyType = dict.Keys.FirstOrDefault()?.GetPythonType();
var valueType = dict.Values.FirstOrDefault()?.GetPythonType();
if (!keyType.IsUnknown()) {
specificTypes.Add(keyType);
}
if (!valueType.IsUnknown()) {
specificTypes.Add(valueType);
}
break;
case IPythonCollection coll:
specificTypes.AddRange(coll.Contents.Select(m => m.GetPythonType()));
break;
case IPythonIterable iter:
var itemType = iter.GetIterator().Next.GetPythonType();
if (!itemType.IsUnknown()) {
specificTypes.Add(itemType);
}
break;
case IMember m:
if (!m.IsUnknown()) {
specificTypes.Add(m.GetPythonType());
}
break;
}
return specificTypes;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,20 @@ private IMember GetValueFromUnaryOp(UnaryExpression expr, string op) {
}

private IMember GetValueFromBinaryOp(Expression expr) {
if (expr is AndExpression || expr is OrExpression) {
if (expr is AndExpression) {
return Interpreter.GetBuiltinType(BuiltinTypeId.Bool);
}

if (expr is OrExpression orexp) {
// Consider 'self.__params = types.MappingProxyType(params or {})'
var leftSide = GetValueFromExpression(orexp.Left);
if (!leftSide.IsUnknown()) {
return leftSide;
}
var rightSide = GetValueFromExpression(orexp.Right);
return rightSide.IsUnknown() ? Interpreter.GetBuiltinType(BuiltinTypeId.Bool) : rightSide;
}

if (!(expr is BinaryExpression binop) || binop.Left == null) {
return null;
}
Expand Down
37 changes: 20 additions & 17 deletions src/Analysis/Ast/Impl/Analyzer/Evaluation/ExpressionEval.Scopes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -89,23 +89,26 @@ public IMember LookupNameInScopes(string name, out IScope scope, LookupOptions o
return value;
}

public IPythonType GetTypeFromAnnotation(Expression expr, LookupOptions options = LookupOptions.Global | LookupOptions.Builtins) {
if (expr == null) {
return null;
}

if (expr is CallExpression callExpr) {
// x: NamedTuple(...)
return GetValueFromCallable(callExpr)?.GetPythonType() ?? UnknownType;
}

if (expr is IndexExpression indexExpr) {
// Try generics
var target = GetValueFromExpression(indexExpr.Target);
var result = GetValueFromGeneric(target, indexExpr);
if (result != null) {
return result.GetPythonType();
}
public IPythonType GetTypeFromAnnotation(Expression expr, LookupOptions options = LookupOptions.Global | LookupOptions.Builtins)
=> GetTypeFromAnnotation(expr, out _, options);

public IPythonType GetTypeFromAnnotation(Expression expr, out bool isGeneric, LookupOptions options = LookupOptions.Global | LookupOptions.Builtins) {
isGeneric = false;
switch (expr) {
case null:
return null;
case CallExpression callExpr:
// x: NamedTuple(...)
return GetValueFromCallable(callExpr)?.GetPythonType() ?? UnknownType;
case IndexExpression indexExpr:
// Try generics
var target = GetValueFromExpression(indexExpr.Target);
var result = GetValueFromGeneric(target, indexExpr);
if (result != null) {
isGeneric = true;
return result.GetPythonType();
}
break;
}

// Look at specialization and typing first
Expand Down
4 changes: 4 additions & 0 deletions src/Analysis/Ast/Impl/Analyzer/Evaluation/ExpressionEval.cs
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,10 @@ private IMember GetValueFromMember(MemberExpression expr) {
var type = m?.GetPythonType(); // Try inner type
var value = type?.GetMember(expr.Name);

if (type is IPythonModule) {
return value;
}

// Class type GetMember returns a type. However, class members are
// mostly instances (consider self.x = 1, x is an instance of int).
// However, it is indeed possible to have them as types, like in
Expand Down
16 changes: 16 additions & 0 deletions src/Analysis/Ast/Impl/Analyzer/Handlers/AssignmentHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
using Microsoft.Python.Analysis.Analyzer.Evaluation;
using Microsoft.Python.Analysis.Types;
using Microsoft.Python.Analysis.Values;
using Microsoft.Python.Core;
using Microsoft.Python.Parsing.Ast;

namespace Microsoft.Python.Analysis.Analyzer.Handlers {
Expand Down Expand Up @@ -81,6 +82,8 @@ public void HandleAssignment(AssignmentStatement node) {
var source = value.IsGeneric() ? VariableSource.Generic : VariableSource.Declaration;
Eval.DeclareVariable(ne.Name, value ?? Module.Interpreter.UnknownType, source, Eval.GetLoc(ne));
}

TryHandleClassVariable(node, value);
}

public void HandleAnnotatedExpression(ExpressionWithAnnotation expr, IMember value) {
Expand All @@ -96,6 +99,19 @@ public void HandleAnnotatedExpression(ExpressionWithAnnotation expr, IMember val
HandleTypedVariable(variableType, value, expr.Expression);
}

private void TryHandleClassVariable(AssignmentStatement node, IMember value) {
var mex = node.Left.OfType<MemberExpression>().FirstOrDefault();
if (!string.IsNullOrEmpty(mex?.Name) && mex?.Target is NameExpression nex && nex.Name.EqualsOrdinal("self")) {
var m = Eval.LookupNameInScopes(nex.Name, out var scope, LookupOptions.Local);
var cls = m.GetPythonType<IPythonClassType>();
if (cls != null) {
using (Eval.OpenScope(Eval.Module, cls.ClassDefinition, out _)) {
Eval.DeclareVariable(mex.Name, value, VariableSource.Declaration, Eval.GetLoc(node), true);
}
}
}
}

private void HandleTypedVariable(IPythonType variableType, IMember value, Expression expr) {
// Check value type for compatibility
IMember instance = null;
Expand Down
2 changes: 1 addition & 1 deletion src/Analysis/Ast/Impl/Analyzer/Handlers/ImportHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ private IPythonModule HandlePossibleImport(PossibleModuleImport possibleModuleIm
private void AssignImportedVariables(IPythonModule module, DottedName moduleImportExpression, NameExpression asNameExpression) {
// "import fob.oar as baz" is handled as
// baz = import_module('fob.oar')
if (asNameExpression != null) {
if (!string.IsNullOrEmpty(asNameExpression?.Name)) {
Eval.DeclareVariable(asNameExpression.Name, module, VariableSource.Import, asNameExpression);
return;
}
Expand Down
Loading