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

Commit 1241b6e

Browse files
jakebaileyMikhail Arkhipov
and
Mikhail Arkhipov
authored
Fix import * over local declarations (#1433)
* Fix import * over local declarations (Socket) * Add a test * Add handling of import position relative to the variable Co-authored-by: Mikhail Arkhipov <[email protected]>
1 parent 4035a01 commit 1241b6e

File tree

4 files changed

+49
-9
lines changed

4 files changed

+49
-9
lines changed

src/Analysis/Ast/Impl/Analyzer/Evaluation/ExpressionEval.Scopes.cs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,15 +43,18 @@ public void DeclareVariable(string name, IMember value, VariableSource source)
4343
public void DeclareVariable(string name, IMember value, VariableSource source, IPythonModule module)
4444
=> DeclareVariable(name, value, source, new Location(module));
4545

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

49-
public void DeclareVariable(string name, IMember value, VariableSource source, Location location, bool overwrite = false) {
49+
public void DeclareVariable(string name, IMember value, VariableSource source, Location location, bool overwrite = true) {
50+
var member = GetInScope(name);
51+
if (member != null && !overwrite) {
52+
return;
53+
}
5054
if (source == VariableSource.Import && value is IVariable v) {
5155
CurrentScope.LinkVariable(name, v, location);
5256
return;
5357
}
54-
var member = GetInScope(name);
5558
if (member != null) {
5659
if (!value.IsUnknown()) {
5760
CurrentScope.DeclareVariable(name, value, source, location);

src/Analysis/Ast/Impl/Analyzer/Handlers/AssignmentHandler.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ private void TryHandleClassVariable(AssignmentStatement node, IMember value) {
111111
var cls = m.GetPythonType<IPythonClassType>();
112112
if (cls != null) {
113113
using (Eval.OpenScope(Eval.Module, cls.ClassDefinition, out _)) {
114-
Eval.DeclareVariable(mex.Name, value, VariableSource.Declaration, Eval.GetLocationOfName(mex), true);
114+
Eval.DeclareVariable(mex.Name, value, VariableSource.Declaration, Eval.GetLocationOfName(mex));
115115
}
116116
}
117117
}

src/Analysis/Ast/Impl/Analyzer/Handlers/FromImportHandler.cs

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ private void AssignVariables(FromImportStatement node, IImportSearchResult impor
5959
// TODO: warn this is not a good style per
6060
// TODO: https://docs.python.org/3/faq/programming.html#what-are-the-best-practices-for-using-import-in-a-module
6161
// TODO: warn this is invalid if not in the global scope.
62-
HandleModuleImportStar(variableModule, imports is ImplicitPackageImport);
62+
HandleModuleImportStar(variableModule, imports is ImplicitPackageImport, node.StartIndex);
6363
return;
6464
}
6565

@@ -68,14 +68,16 @@ private void AssignVariables(FromImportStatement node, IImportSearchResult impor
6868
if (!string.IsNullOrEmpty(memberName)) {
6969
var nameExpression = asNames[i] ?? names[i];
7070
var variableName = nameExpression?.Name ?? memberName;
71-
var exported = variableModule.Analysis?.GlobalScope.Variables[memberName] ?? variableModule.GetMember(memberName);
71+
var variable = variableModule.Analysis?.GlobalScope?.Variables[memberName];
72+
var exported = variable ?? variableModule.GetMember(memberName);
7273
var value = exported ?? GetValueFromImports(variableModule, imports as IImportChildrenSource, memberName);
73-
Eval.DeclareVariable(variableName, value, VariableSource.Import, nameExpression);
74+
// Do not allow imported variables to override local declarations
75+
Eval.DeclareVariable(variableName, value, VariableSource.Import, nameExpression, CanOverwriteVariable(variableName, node.StartIndex));
7476
}
7577
}
7678
}
7779

78-
private void HandleModuleImportStar(PythonVariableModule variableModule, bool isImplicitPackage) {
80+
private void HandleModuleImportStar(PythonVariableModule variableModule, bool isImplicitPackage, int importPosition) {
7981
if (variableModule.Module == Module) {
8082
// from self import * won't define any new members
8183
return;
@@ -100,10 +102,31 @@ private void HandleModuleImportStar(PythonVariableModule variableModule, bool is
100102
}
101103

102104
var variable = variableModule.Analysis?.GlobalScope?.Variables[memberName];
103-
Eval.DeclareVariable(memberName, variable ?? member, VariableSource.Import);
105+
// Do not allow imported variables to override local declarations
106+
Eval.DeclareVariable(memberName, variable ?? member, VariableSource.Import, Eval.DefaultLocation, CanOverwriteVariable(memberName, importPosition));
104107
}
105108
}
106109

110+
private bool CanOverwriteVariable(string name, int importPosition) {
111+
var v = Eval.CurrentScope.Variables[name];
112+
if(v == null) {
113+
return true; // Variable does not exist
114+
}
115+
// Allow overwrite if import is below the variable. Consider
116+
// x = 1
117+
// x = 2
118+
// from A import * # brings another x
119+
// x = 3
120+
var references = v.References.Where(r => r.DocumentUri == Module.Uri).ToArray();
121+
if(references.Length == 0) {
122+
// No references to the variable in this file - the variable
123+
// is imported from another module. OK to overwrite.
124+
return true;
125+
}
126+
var firstAssignmentPosition = references.Min(r => r.Span.ToIndexSpan(Ast).Start);
127+
return firstAssignmentPosition < importPosition;
128+
}
129+
107130
private IMember GetValueFromImports(PythonVariableModule parentModule, IImportChildrenSource childrenSource, string memberName) {
108131
if (childrenSource == null || !childrenSource.TryGetChildImport(memberName, out var childImport)) {
109132
return Interpreter.UnknownType;

src/Analysis/Ast/Test/ImportTests.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,5 +231,19 @@ public async Task PreferTypeToAny() {
231231
var a = analysis.Should().HaveClass("A").Which;
232232
a.GetMember("x").Should().HaveType(BuiltinTypeId.Int);
233233
}
234+
235+
[TestMethod, Priority(0)]
236+
public async Task StarImportDoesNotOverwriteFunction() {
237+
const string code = @"
238+
from sys import *
239+
240+
def exit():
241+
return 1234
242+
243+
x = exit()
244+
";
245+
var analysis = await GetAnalysisAsync(code);
246+
analysis.Should().HaveVariable("x").OfType(BuiltinTypeId.Int);
247+
}
234248
}
235249
}

0 commit comments

Comments
 (0)