|
17 | 17 | using System.Collections.Generic;
|
18 | 18 | using System.Linq;
|
19 | 19 | using Microsoft.Python.Analysis.Analyzer.Evaluation;
|
| 20 | +using Microsoft.Python.Analysis.Diagnostics; |
20 | 21 | using Microsoft.Python.Analysis.Types;
|
21 | 22 | using Microsoft.Python.Analysis.Values;
|
22 | 23 | using Microsoft.Python.Core;
|
@@ -98,19 +99,29 @@ private PythonClassType CreateClass(ClassDefinition cd) {
|
98 | 99 |
|
99 | 100 | private void AddFunctionOrProperty(FunctionDefinition fd) {
|
100 | 101 | var declaringType = fd.Parent != null && _typeMap.TryGetValue(fd.Parent, out var t) ? t : null;
|
101 |
| - if (!TryAddProperty(fd, declaringType)) { |
102 |
| - AddFunction(fd, declaringType); |
| 102 | + var existing = _eval.LookupNameInScopes(fd.Name, LookupOptions.Local); |
| 103 | + |
| 104 | + switch (existing?.MemberType) { |
| 105 | + case PythonMemberType.Method: |
| 106 | + case PythonMemberType.Function: |
| 107 | + case PythonMemberType.Property: |
| 108 | + ReportRedefinedFunction(fd, existing as PythonType); |
| 109 | + break; |
| 110 | + } |
| 111 | + |
| 112 | + if (!TryAddProperty(fd, existing, declaringType)) { |
| 113 | + AddFunction(fd, existing, declaringType); |
103 | 114 | }
|
104 | 115 | }
|
105 | 116 |
|
106 |
| - private void AddFunction(FunctionDefinition fd, IPythonType declaringType) { |
107 |
| - if (!(_eval.LookupNameInScopes(fd.Name, LookupOptions.Local) is PythonFunctionType existing)) { |
108 |
| - existing = new PythonFunctionType(fd, declaringType, _eval.GetLocationOfName(fd)); |
| 117 | + private void AddFunction(FunctionDefinition fd, IMember existing, IPythonType declaringType) { |
| 118 | + if (!(existing is PythonFunctionType f)) { |
| 119 | + f = new PythonFunctionType(fd, declaringType, _eval.GetLocationOfName(fd)); |
109 | 120 | // The variable is transient (non-user declared) hence it does not have location.
|
110 | 121 | // Function type is tracking locations for references and renaming.
|
111 |
| - _eval.DeclareVariable(fd.Name, existing, VariableSource.Declaration); |
| 122 | + _eval.DeclareVariable(fd.Name, f, VariableSource.Declaration); |
112 | 123 | }
|
113 |
| - AddOverload(fd, existing, o => existing.AddOverload(o)); |
| 124 | + AddOverload(fd, f, o => f.AddOverload(o)); |
114 | 125 | }
|
115 | 126 |
|
116 | 127 | private void AddOverload(FunctionDefinition fd, IPythonClassMember function, Action<IPythonFunctionOverload> addOverload) {
|
@@ -148,31 +159,31 @@ private PythonFunctionOverload GetOverloadFromStub(FunctionDefinition node) {
|
148 | 159 | return null;
|
149 | 160 | }
|
150 | 161 |
|
151 |
| - private bool TryAddProperty(FunctionDefinition node, IPythonType declaringType) { |
| 162 | + private bool TryAddProperty(FunctionDefinition node, IMember existing, IPythonType declaringType) { |
152 | 163 | var dec = node.Decorators?.Decorators;
|
153 | 164 | var decorators = dec != null ? dec.ExcludeDefault().ToArray() : Array.Empty<Expression>();
|
154 | 165 |
|
155 | 166 | foreach (var d in decorators.OfType<NameExpression>()) {
|
156 | 167 | switch (d.Name) {
|
157 | 168 | case @"property":
|
158 |
| - AddProperty(node, declaringType, false); |
| 169 | + AddProperty(node, existing, declaringType, false); |
159 | 170 | return true;
|
160 | 171 | case @"abstractproperty":
|
161 |
| - AddProperty(node, declaringType, true); |
| 172 | + AddProperty(node, existing, declaringType, true); |
162 | 173 | return true;
|
163 | 174 | }
|
164 | 175 | }
|
165 | 176 | return false;
|
166 | 177 | }
|
167 | 178 |
|
168 |
| - private void AddProperty(FunctionDefinition fd, IPythonType declaringType, bool isAbstract) { |
169 |
| - if (!(_eval.LookupNameInScopes(fd.Name, LookupOptions.Local) is PythonPropertyType existing)) { |
170 |
| - existing = new PythonPropertyType(fd, _eval.GetLocationOfName(fd), declaringType, isAbstract); |
| 179 | + private void AddProperty(FunctionDefinition fd, IMember existing, IPythonType declaringType, bool isAbstract) { |
| 180 | + if (!(existing is PythonPropertyType p)) { |
| 181 | + p = new PythonPropertyType(fd, _eval.GetLocationOfName(fd), declaringType, isAbstract); |
171 | 182 | // The variable is transient (non-user declared) hence it does not have location.
|
172 | 183 | // Property type is tracking locations for references and renaming.
|
173 |
| - _eval.DeclareVariable(fd.Name, existing, VariableSource.Declaration); |
| 184 | + _eval.DeclareVariable(fd.Name, p, VariableSource.Declaration); |
174 | 185 | }
|
175 |
| - AddOverload(fd, existing, o => existing.AddOverload(o)); |
| 186 | + AddOverload(fd, p, o => p.AddOverload(o)); |
176 | 187 | }
|
177 | 188 |
|
178 | 189 | private IMember GetMemberFromStub(string name) {
|
@@ -202,6 +213,22 @@ private IMember GetMemberFromStub(string name) {
|
202 | 213 | return member;
|
203 | 214 | }
|
204 | 215 |
|
| 216 | + private void ReportRedefinedFunction(FunctionDefinition redefined, ILocatedMember existing) { |
| 217 | + // get line number of existing function for diagnostic message |
| 218 | + var existingLoc = existing.Definition; |
| 219 | + var existingLine = existingLoc.Span.Start.Line; |
| 220 | + |
| 221 | + _eval.ReportDiagnostics( |
| 222 | + _eval.Module.Uri, |
| 223 | + new DiagnosticsEntry( |
| 224 | + Resources.FunctionRedefined.FormatInvariant(existingLine), |
| 225 | + // only highlight the redefined name |
| 226 | + _eval.GetLocationInfo(redefined.NameExpression).Span, |
| 227 | + ErrorCodes.FunctionRedefined, |
| 228 | + Parsing.Severity.Error, |
| 229 | + DiagnosticSource.Analysis)); |
| 230 | + } |
| 231 | + |
205 | 232 | private static bool IsDeprecated(ClassDefinition cd)
|
206 | 233 | => cd.Decorators?.Decorators != null && IsDeprecated(cd.Decorators.Decorators);
|
207 | 234 |
|
|
0 commit comments