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

Commit

Permalink
Fix import * over local declarations (#1433)
Browse files Browse the repository at this point in the history
* Fix import * over local declarations (Socket)

* Add a test

* Add handling of import position relative to the variable


Co-authored-by: Mikhail Arkhipov <mikhaila@microsoft.com>
  • Loading branch information
jakebailey and Mikhail Arkhipov committed Aug 13, 2019
1 parent 4035a01 commit 1241b6e
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 9 deletions.
Expand Up @@ -43,15 +43,18 @@ public void DeclareVariable(string name, IMember value, VariableSource source)
public void DeclareVariable(string name, IMember value, VariableSource source, IPythonModule module)
=> DeclareVariable(name, value, source, new Location(module));

public void DeclareVariable(string name, IMember value, VariableSource source, Node location, bool overwrite = false)
public void DeclareVariable(string name, IMember value, VariableSource source, Node location, bool overwrite = true)
=> DeclareVariable(name, value, source, GetLocationOfName(location), overwrite);

public void DeclareVariable(string name, IMember value, VariableSource source, Location location, bool overwrite = false) {
public void DeclareVariable(string name, IMember value, VariableSource source, Location location, bool overwrite = true) {
var member = GetInScope(name);
if (member != null && !overwrite) {
return;
}
if (source == VariableSource.Import && value is IVariable v) {
CurrentScope.LinkVariable(name, v, location);
return;
}
var member = GetInScope(name);
if (member != null) {
if (!value.IsUnknown()) {
CurrentScope.DeclareVariable(name, value, source, location);
Expand Down
Expand Up @@ -111,7 +111,7 @@ internal sealed class AssignmentHandler : StatementHandler {
var cls = m.GetPythonType<IPythonClassType>();
if (cls != null) {
using (Eval.OpenScope(Eval.Module, cls.ClassDefinition, out _)) {
Eval.DeclareVariable(mex.Name, value, VariableSource.Declaration, Eval.GetLocationOfName(mex), true);
Eval.DeclareVariable(mex.Name, value, VariableSource.Declaration, Eval.GetLocationOfName(mex));
}
}
}
Expand Down
33 changes: 28 additions & 5 deletions src/Analysis/Ast/Impl/Analyzer/Handlers/FromImportHandler.cs
Expand Up @@ -59,7 +59,7 @@ internal sealed partial class ImportHandler {
// TODO: warn this is not a good style per
// TODO: https://docs.python.org/3/faq/programming.html#what-are-the-best-practices-for-using-import-in-a-module
// TODO: warn this is invalid if not in the global scope.
HandleModuleImportStar(variableModule, imports is ImplicitPackageImport);
HandleModuleImportStar(variableModule, imports is ImplicitPackageImport, node.StartIndex);
return;
}

Expand All @@ -68,14 +68,16 @@ internal sealed partial class ImportHandler {
if (!string.IsNullOrEmpty(memberName)) {
var nameExpression = asNames[i] ?? names[i];
var variableName = nameExpression?.Name ?? memberName;
var exported = variableModule.Analysis?.GlobalScope.Variables[memberName] ?? variableModule.GetMember(memberName);
var variable = variableModule.Analysis?.GlobalScope?.Variables[memberName];
var exported = variable ?? variableModule.GetMember(memberName);
var value = exported ?? GetValueFromImports(variableModule, imports as IImportChildrenSource, memberName);
Eval.DeclareVariable(variableName, value, VariableSource.Import, nameExpression);
// Do not allow imported variables to override local declarations
Eval.DeclareVariable(variableName, value, VariableSource.Import, nameExpression, CanOverwriteVariable(variableName, node.StartIndex));
}
}
}

private void HandleModuleImportStar(PythonVariableModule variableModule, bool isImplicitPackage) {
private void HandleModuleImportStar(PythonVariableModule variableModule, bool isImplicitPackage, int importPosition) {
if (variableModule.Module == Module) {
// from self import * won't define any new members
return;
Expand All @@ -100,10 +102,31 @@ internal sealed partial class ImportHandler {
}

var variable = variableModule.Analysis?.GlobalScope?.Variables[memberName];
Eval.DeclareVariable(memberName, variable ?? member, VariableSource.Import);
// Do not allow imported variables to override local declarations
Eval.DeclareVariable(memberName, variable ?? member, VariableSource.Import, Eval.DefaultLocation, CanOverwriteVariable(memberName, importPosition));
}
}

private bool CanOverwriteVariable(string name, int importPosition) {
var v = Eval.CurrentScope.Variables[name];
if(v == null) {
return true; // Variable does not exist
}
// Allow overwrite if import is below the variable. Consider
// x = 1
// x = 2
// from A import * # brings another x
// x = 3
var references = v.References.Where(r => r.DocumentUri == Module.Uri).ToArray();
if(references.Length == 0) {
// No references to the variable in this file - the variable
// is imported from another module. OK to overwrite.
return true;
}
var firstAssignmentPosition = references.Min(r => r.Span.ToIndexSpan(Ast).Start);
return firstAssignmentPosition < importPosition;
}

private IMember GetValueFromImports(PythonVariableModule parentModule, IImportChildrenSource childrenSource, string memberName) {
if (childrenSource == null || !childrenSource.TryGetChildImport(memberName, out var childImport)) {
return Interpreter.UnknownType;
Expand Down
14 changes: 14 additions & 0 deletions src/Analysis/Ast/Test/ImportTests.cs
Expand Up @@ -231,5 +231,19 @@ import sys
var a = analysis.Should().HaveClass("A").Which;
a.GetMember("x").Should().HaveType(BuiltinTypeId.Int);
}

[TestMethod, Priority(0)]
public async Task StarImportDoesNotOverwriteFunction() {
const string code = @"
from sys import *
def exit():
return 1234
x = exit()
";
var analysis = await GetAnalysisAsync(code);
analysis.Should().HaveVariable("x").OfType(BuiltinTypeId.Int);
}
}
}

0 comments on commit 1241b6e

Please sign in to comment.