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

Propagate known parameter types to derived functions #200

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
4 changes: 4 additions & 0 deletions src/.editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,7 @@ csharp_new_line_before_finally = false
csharp_new_line_before_members_in_anonymous_types = false

csharp_new_line_before_members_in_object_initializers = false

dotnet_style_qualification_for_field = false:suggestion
dotnet_style_qualification_for_property = false:none

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd rather keep these as they were. We are slowly converting code to C# 7+ so we do want to see suggestion/information for => style so we can fix them as we go.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are controlling "this." prefixing, not '=>'. PLS appears to use underscore instead of 'this.' for fields. I don't know what's the guidance for properties.

dotnet_style_qualification_for_method = false:none
6 changes: 5 additions & 1 deletion src/Analysis/Engine/Impl/Analyzer/DDG.cs
Original file line number Diff line number Diff line change
Expand Up @@ -474,7 +474,7 @@ public override bool Walk(FunctionDefinition node) {
InterpreterScope funcScope;
if (_unit.InterpreterScope.TryGetNodeScope(node, out funcScope)) {
var function = ((FunctionScope)funcScope).Function;
var analysisUnit = (FunctionAnalysisUnit)((FunctionScope)funcScope).Function.AnalysisUnit;
var analysisUnit = (FunctionAnalysisUnit)function.AnalysisUnit;

var curClass = Scope as ClassScope;
if (curClass != null) {
Expand All @@ -489,6 +489,10 @@ public override bool Walk(FunctionDefinition node) {
function.UpdateDefaultParameters(_unit, overload.GetParameters());
}
}

foreach (var @base in bases.OfType<FunctionInfo>()) {
@base.AddDerived(function);
}
}
}

Expand Down
17 changes: 17 additions & 0 deletions src/Analysis/Engine/Impl/Values/FunctionInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
namespace Microsoft.PythonTools.Analysis.Values {
internal class FunctionInfo : AnalysisValue, IFunctionInfo, IHasRichDescription, IHasQualifiedName {
private Dictionary<AnalysisValue, IAnalysisSet> _methods;
private HashSet<FunctionInfo> _derived;
private Dictionary<string, VariableDef> _functionAttrs;
private readonly FunctionAnalysisUnit _analysisUnit;
private ReferenceDict _references;
Expand Down Expand Up @@ -94,6 +95,12 @@ public override IAnalysisSet Call(Node node, AnalysisUnit unit, IAnalysisSet[] a
}

var res = DoCall(node, unit, _analysisUnit, callArgs);
if (_derived != null) {
foreach (FunctionInfo derived in _derived) {
var derivedResult = derived.DoCall(node, unit, derived._analysisUnit, callArgs);
res = res.Union(derivedResult);
}
}

if (_callsWithClosure != null) {
var chain = new CallChain(node, unit, _callDepthLimit);
Expand Down Expand Up @@ -158,6 +165,16 @@ private IVersioned GetAggregate(AnalysisUnit unit) {
return agg;
}

internal bool AddDerived(FunctionInfo derived) {
if (derived == null) throw new ArgumentNullException(nameof(derived));

_derived = _derived ?? new HashSet<FunctionInfo>();

bool @new = _derived.Add(derived);
// TODO: do we need to re-queue this function analysis if a new derived was added?
return @new;
}

internal void AddParameterReference(Node node, AnalysisUnit unit, string name) {
var vd = (_analysisUnit.Scope as FunctionScope)?.GetParameter(name);
vd?.AddReference(node, unit);
Expand Down
26 changes: 26 additions & 0 deletions src/Analysis/Engine/Test/InheritanceTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,5 +64,31 @@ def virt():
analysis.Should().HaveVariable("b").OfTypes(BuiltinTypeId.Int, BuiltinTypeId.Function);
}
}

[TestMethod]
public async Task ParameterTypesPropagateToDerivedFunctions() {
var code = @"
class Baze:
def foo(self, x):
pass

class Derived(Baze):
def foo(self, x):
pass

Baze().foo(42)
";

using (var server = await new Server().InitializeAsync(PythonVersions.Required_Python36X)) {
var analysis = await server.OpenDefaultDocumentAndGetAnalysisAsync(code);

// the class, for which we know parameter type initially
analysis.Should().HaveClass("Baze").WithFunction("foo")
.WithParameter("x").OfType(BuiltinTypeId.Int);
// its derived class
analysis.Should().HaveClass("Derived").WithFunction("foo")
.WithParameter("x").OfType(BuiltinTypeId.Int);
}
}
}
}