Skip to content
This repository was archived by the owner on Apr 14, 2022. It is now read-only.

Add IsExact to collection interface, check when deciding to use __all__ #886

Merged
merged 4 commits into from
Apr 8, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public IMember GetValueFromList(ListExpression expression) {
var value = GetValueFromExpression(item) ?? UnknownType;
contents.Add(value);
}
return PythonCollectionType.CreateList(Module.Interpreter, GetLoc(expression), contents);
return PythonCollectionType.CreateList(Module.Interpreter, GetLoc(expression), contents, exact: true);
}

public IMember GetValueFromDictionary(DictionaryExpression expression) {
Expand All @@ -70,7 +70,7 @@ public IMember GetValueFromDictionary(DictionaryExpression expression) {
var value = GetValueFromExpression(item.SliceStop) ?? UnknownType;
contents[key] = value;
}
return new PythonDictionary(Interpreter, GetLoc(expression), contents);
return new PythonDictionary(Interpreter, GetLoc(expression), contents, exact: true);
}

private IMember GetValueFromTuple(TupleExpression expression) {
Expand All @@ -79,7 +79,7 @@ private IMember GetValueFromTuple(TupleExpression expression) {
var value = GetValueFromExpression(item) ?? UnknownType;
contents.Add(value);
}
return PythonCollectionType.CreateTuple(Module.Interpreter, GetLoc(expression), contents);
return PythonCollectionType.CreateTuple(Module.Interpreter, GetLoc(expression), contents, exact: true);
}

public IMember GetValueFromSet(SetExpression expression) {
Expand All @@ -88,7 +88,7 @@ public IMember GetValueFromSet(SetExpression expression) {
var value = GetValueFromExpression(item) ?? UnknownType;
contents.Add(value);
}
return PythonCollectionType.CreateSet(Interpreter, GetLoc(expression), contents);
return PythonCollectionType.CreateSet(Interpreter, GetLoc(expression), contents, exact: true);
}

public IMember GetValueFromGenerator(GeneratorExpression expression) {
Expand All @@ -103,6 +103,8 @@ public IMember GetValueFromComprehension(Comprehension node) {
var oldVariables = CurrentScope.Variables.OfType<Variable>().ToDictionary(k => k.Name, v => v);
try {
ProcessComprehension(node);

// TODO: Evaluate comprehensions to produce exact contents, if possible.
switch (node) {
case ListComprehension lc:
var v1 = GetValueFromExpression(lc.Item) ?? UnknownType;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ private IMember GetValueFromBinaryOp(Expression expr) {
&& leftType?.TypeId == BuiltinTypeId.List && rightType?.TypeId == BuiltinTypeId.List
&& left is IPythonCollection lc && right is IPythonCollection rc) {

return PythonCollectionType.CreateConcatenatedList(Module.Interpreter, GetLoc(expr), lc.Contents, rc.Contents);
return PythonCollectionType.CreateConcatenatedList(Module.Interpreter, GetLoc(expr), lc, rc);
}

return left.IsUnknown() ? right : left;
Expand Down
29 changes: 16 additions & 13 deletions src/Analysis/Ast/Impl/Analyzer/ModuleWalker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,14 +76,14 @@ private void HandleAugmentedAllAssign(AugmentedAssignStatement node) {
}

var rightVar = Eval.GetValueFromExpression(node.Right);
var rightContents = (rightVar as IPythonCollection)?.Contents;
var right = rightVar as IPythonCollection;

if (rightContents == null) {
if (right == null) {
_allIsUsable = false;
return;
}

ExtendAll(node.Left, rightContents);
ExtendAll(node.Left, right);
}

private void HandleAllAppendExtend(CallExpression node) {
Expand All @@ -99,41 +99,43 @@ private void HandleAllAppendExtend(CallExpression node) {
return;
}

IReadOnlyList<IMember> contents = null;
var v = Eval.GetValueFromExpression(node.Args[0].Expression);
var arg = node.Args[0].Expression;
var v = Eval.GetValueFromExpression(arg);
if (v == null) {
_allIsUsable = false;
return;
}

IPythonCollection values = null;

switch (me.Name) {
case "append":
contents = new List<IMember>() { v };
values = PythonCollectionType.CreateList(Module.Interpreter, Eval.GetLoc(arg), new List<IMember>() { v }, exact: true);
break;
case "extend":
contents = (v as IPythonCollection)?.Contents;
values = v as IPythonCollection;
break;
}

if (contents == null) {
if (values == null) {
_allIsUsable = false;
return;
}

ExtendAll(node, contents);
ExtendAll(node, values);
}

private void ExtendAll(Node declNode, IReadOnlyList<IMember> values) {
private void ExtendAll(Node declNode, IPythonCollection values) {
Eval.LookupNameInScopes(AllVariableName, out var scope, LookupOptions.Normal);
if (scope == null) {
return;
}

var loc = Eval.GetLoc(declNode);

var allContents = (scope.Variables[AllVariableName].Value as IPythonCollection)?.Contents;
var all = scope.Variables[AllVariableName].Value as IPythonCollection;

var list = PythonCollectionType.CreateConcatenatedList(Module.Interpreter, loc, allContents, values);
var list = PythonCollectionType.CreateConcatenatedList(Module.Interpreter, loc, all, values);
var source = list.IsGeneric() ? VariableSource.Generic : VariableSource.Declaration;

Eval.DeclareVariable(AllVariableName, list, source, loc);
Expand Down Expand Up @@ -199,7 +201,8 @@ public void Complete() {
SymbolTable.ReplacedByStubs.Clear();
MergeStub();

if (_allIsUsable && _allReferencesCount >= 1 && GlobalScope.Variables.TryGetVariable(AllVariableName, out var variable) && variable?.Value is IPythonCollection collection) {
if (_allIsUsable && _allReferencesCount >= 1 && GlobalScope.Variables.TryGetVariable(AllVariableName, out var variable)
&& variable?.Value is IPythonCollection collection && collection.IsExact) {
ExportedMemberNames = collection.Contents
.OfType<IPythonConstant>()
.Select(c => c.GetString())
Expand Down
31 changes: 18 additions & 13 deletions src/Analysis/Ast/Impl/Types/Collections/PythonCollectionType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,34 +84,39 @@ public override IMember Index(IPythonInstance instance, object index)
#endregion

public static IPythonCollection CreateList(IPythonInterpreter interpreter, LocationInfo location, IArgumentSet args) {
IReadOnlyList<IMember> contents = null;
var exact = true;
IReadOnlyList<IMember> contents;

if (args.Arguments.Count > 1) {
// self and list like in list.__init__ and 'list([1, 'str', 3.0])'
contents = (args.Arguments[1].Value as PythonCollection)?.Contents;
var arg = args.Arguments[1].Value as PythonCollection;
exact = arg?.IsExact ?? false;
contents = arg?.Contents;
} else {
// Try list argument as n '__init__(self, *args, **kwargs)'
contents = args.ListArgument?.Values;
}
return CreateList(interpreter, location, contents ?? Array.Empty<IMember>());

return CreateList(interpreter, location, contents ?? Array.Empty<IMember>(), exact: exact);
}

public static IPythonCollection CreateList(IPythonInterpreter interpreter, LocationInfo location, IReadOnlyList<IMember> contents, bool flatten = true) {
public static IPythonCollection CreateList(IPythonInterpreter interpreter, LocationInfo location, IReadOnlyList<IMember> contents, bool flatten = true, bool exact = false) {
var collectionType = new PythonCollectionType(null, BuiltinTypeId.List, interpreter, true);
return new PythonCollection(collectionType, location, contents, flatten);
return new PythonCollection(collectionType, location, contents, flatten, exact);
}

public static IPythonCollection CreateConcatenatedList(IPythonInterpreter interpreter, LocationInfo location, params IReadOnlyList<IMember>[] manyContents) {
var contents = manyContents?.ExcludeDefault().SelectMany().ToList() ?? new List<IMember>();
return CreateList(interpreter, location, contents);
public static IPythonCollection CreateConcatenatedList(IPythonInterpreter interpreter, LocationInfo location, params IPythonCollection[] many) {
var exact = many?.All(c => c != null && c.IsExact) ?? false;
var contents = many?.ExcludeDefault().Select(c => c.Contents).SelectMany().ToList() ?? new List<IMember>();
return CreateList(interpreter, location, contents, false, exact: exact);
}

public static IPythonCollection CreateTuple(IPythonInterpreter interpreter, LocationInfo location, IReadOnlyList<IMember> contents) {
public static IPythonCollection CreateTuple(IPythonInterpreter interpreter, LocationInfo location, IReadOnlyList<IMember> contents, bool exact = false) {
var collectionType = new PythonCollectionType(null, BuiltinTypeId.Tuple, interpreter, false);
return new PythonCollection(collectionType, location, contents);
return new PythonCollection(collectionType, location, contents, exact: exact);
}
public static IPythonCollection CreateSet(IPythonInterpreter interpreter, LocationInfo location, IReadOnlyList<IMember> contents, bool flatten = true) {
public static IPythonCollection CreateSet(IPythonInterpreter interpreter, LocationInfo location, IReadOnlyList<IMember> contents, bool flatten = true, bool exact = false) {
var collectionType = new PythonCollectionType(null, BuiltinTypeId.Set, interpreter, true);
return new PythonCollection(collectionType, location, contents, flatten);
return new PythonCollection(collectionType, location, contents, flatten, exact: exact);
}

public override bool Equals(object obj)
Expand Down
9 changes: 7 additions & 2 deletions src/Analysis/Ast/Impl/Values/Collections/PythonCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,21 +25,24 @@ internal class PythonCollection : PythonInstance, IPythonCollection {
/// <param name="collectionType">Collection type.</param>
/// <param name="contents">Contents of the collection (typically elements from the initialization).</param>
/// <param name="location">Declaring location.</param>
/// <param name="flatten">If true and contents is a single element
/// <param name="flatten">If true and contents is a single element</param>
/// <param name="exact">True if the contents are an exact representation of the collection contents.</param>
/// and is a sequence, the sequence elements are copied rather than creating
/// a sequence of sequences with a single element.</param>
public PythonCollection(
IPythonType collectionType,
LocationInfo location,
IReadOnlyList<IMember> contents,
bool flatten = true
bool flatten = true,
bool exact = false
) : base(collectionType, location) {
var c = contents ?? Array.Empty<IMember>();
if (flatten && c.Count == 1 && c[0] is IPythonCollection seq) {
Contents = seq.Contents;
} else {
Contents = c;
}
IsExact = exact;
}

/// <summary>
Expand Down Expand Up @@ -72,5 +75,7 @@ public static int GetIndex(object index) {
return 0;
}
}

public bool IsExact { get; }
}
}
12 changes: 6 additions & 6 deletions src/Analysis/Ast/Impl/Values/Collections/PythonDictionary.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,16 @@ internal class PythonDictionary : PythonCollection, IPythonDictionary {
private readonly Dictionary<IMember, IMember> _contents = new Dictionary<IMember, IMember>(new KeyComparer());
private readonly IPythonInterpreter _interpreter;

public PythonDictionary(PythonDictionaryType dictType, LocationInfo location, IReadOnlyDictionary<IMember, IMember> contents) :
base(dictType, location, contents.Keys.ToArray()) {
public PythonDictionary(PythonDictionaryType dictType, LocationInfo location, IReadOnlyDictionary<IMember, IMember> contents, bool exact = false) :
base(dictType, location, contents.Keys.ToArray(), exact: exact) {
foreach (var kvp in contents) {
_contents[kvp.Key] = kvp.Value;
}
_interpreter = dictType.DeclaringModule.Interpreter;
}

public PythonDictionary(IPythonInterpreter interpreter, LocationInfo location, IMember contents) :
base(new PythonDictionaryType(interpreter), location, Array.Empty<IMember>()) {
public PythonDictionary(IPythonInterpreter interpreter, LocationInfo location, IMember contents, bool exact = false) :
base(new PythonDictionaryType(interpreter), location, Array.Empty<IMember>(), exact: exact) {
if (contents is IPythonDictionary dict) {
foreach (var key in dict.Keys) {
_contents[key] = dict[key];
Expand All @@ -47,8 +47,8 @@ public PythonDictionary(IPythonInterpreter interpreter, LocationInfo location, I
_interpreter = interpreter;
}

public PythonDictionary(IPythonInterpreter interpreter, LocationInfo location, IReadOnlyDictionary<IMember, IMember> contents) :
this(new PythonDictionaryType(interpreter), location, contents) {
public PythonDictionary(IPythonInterpreter interpreter, LocationInfo location, IReadOnlyDictionary<IMember, IMember> contents, bool exact = false) :
this(new PythonDictionaryType(interpreter), location, contents, exact: exact) {
_interpreter = interpreter;
}

Expand Down
5 changes: 5 additions & 0 deletions src/Analysis/Ast/Impl/Values/Definitions/IPythonCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,10 @@ public interface IPythonCollection : IPythonInstance {
/// Collection contents
/// </summary>
IReadOnlyList<IMember> Contents { get; }

/// <summary>
/// True if the collection contents contain an accurate representation of the collection's contents.
/// </summary>
bool IsExact { get; }
}
}
2 changes: 2 additions & 0 deletions src/LanguageServer/Test/ImportsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -586,6 +586,8 @@ from module1 import *
[DataRow(@"
__all__ = ['A']
__all__.extend(nothing)")]
[DataRow(@"
__all__ = [chr(x + 65) for x in range(1, 2)]")]
[DataTestMethod, Priority(0)]
public async Task AllUnsupported(string allCode) {
var module1Code = @"
Expand Down