-
Notifications
You must be signed in to change notification settings - Fork 133
Give a diagnostic when creating a function in a class and not specifying the first argument as self #1279
Give a diagnostic when creating a function in a class and not specifying the first argument as self #1279
Changes from all commits
6354e21
6f334f8
2410f5f
9bbec89
18779ee
38c347a
524d2e8
ae00ad5
d2abaab
6e3a97e
a046e66
f6e9c92
60ce7c9
53962c0
1aacae6
48f1ff0
1725532
49f983c
23ff7c0
f09539a
86ea843
e0b12a3
db76f2e
e9fb7b5
029c12b
65d4110
8f5d5ba
bac3937
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
using Microsoft.Python.Analysis.Types; | ||
|
||
namespace Microsoft.Python.Analysis { | ||
public static class ClassMemberExtensions { | ||
public static bool IsDunderInit(this IPythonClassMember member) { | ||
return member.Name == "__init__" && member.DeclaringType?.MemberType == PythonMemberType.Class; | ||
} | ||
|
||
public static bool IsDunderNew(this IPythonClassMember member) { | ||
return member.Name == "__new__" && member.DeclaringType?.MemberType == PythonMemberType.Class; | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,16 +14,20 @@ | |
// permissions and limitations under the License. | ||
|
||
using System.Linq; | ||
using Microsoft.Python.Analysis.Analyzer; | ||
using Microsoft.Python.Analysis.Diagnostics; | ||
using Microsoft.Python.Analysis.Types; | ||
using Microsoft.Python.Analysis.Values; | ||
using Microsoft.Python.Core; | ||
using Microsoft.Python.Parsing; | ||
using Microsoft.Python.Parsing.Ast; | ||
|
||
namespace Microsoft.Python.Analysis.Extensions { | ||
public static class PythonFunctionExtensions { | ||
public static bool IsUnbound(this IPythonFunctionType f) | ||
public static bool IsUnbound(this IPythonFunctionType f) | ||
=> f.DeclaringType != null && f.MemberType == PythonMemberType.Function; | ||
|
||
public static bool IsBound(this IPythonFunctionType f) | ||
public static bool IsBound(this IPythonFunctionType f) | ||
=> f.DeclaringType != null && f.MemberType == PythonMemberType.Method; | ||
|
||
public static bool HasClassFirstArgument(this IPythonClassMember m) | ||
|
@@ -34,5 +38,44 @@ public static IScope GetScope(this IPythonFunctionType f) { | |
IScope gs = f.DeclaringModule.GlobalScope; | ||
return gs?.TraverseBreadthFirst(s => s.Children).FirstOrDefault(s => s.Node == f.FunctionDefinition); | ||
} | ||
|
||
/// <summary> | ||
/// Reports any decorator errors on function. | ||
/// Returns true if the decorator combinations for the property are valid | ||
/// </summary> | ||
public static bool HasValidDecorators(this IPythonFunctionType f, IExpressionEvaluator eval) { | ||
bool valid = true; | ||
// If function is abstract, allow all decorators because it will be overridden | ||
if (f.IsAbstract) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I looked at this for the abstract check |
||
return valid; | ||
} | ||
|
||
foreach (var dec in (f.FunctionDefinition?.Decorators?.Decorators).MaybeEnumerate().OfType<NameExpression>()) { | ||
switch (dec.Name) { | ||
case @"staticmethod": | ||
if (f.IsClassMethod) { | ||
ReportInvalidDecorator(dec, Resources.InvalidDecoratorForFunction.FormatInvariant("Staticmethod", "class"), eval); | ||
valid = false; | ||
} | ||
break; | ||
case @"classmethod": | ||
if (f.IsStatic) { | ||
ReportInvalidDecorator(dec, Resources.InvalidDecoratorForFunction.FormatInvariant("Classmethod", "static"), eval); | ||
valid = false; | ||
} | ||
break; | ||
} | ||
} | ||
return valid; | ||
} | ||
|
||
private static void ReportInvalidDecorator(NameExpression name, string errorMsg, IExpressionEvaluator eval) { | ||
eval.ReportDiagnostics(eval.Module.Uri, | ||
new DiagnosticsEntry( | ||
errorMsg, eval.GetLocation(name).Span, | ||
Diagnostics.ErrorCodes.InvalidDecoratorCombination, | ||
Severity.Warning, DiagnosticSource.Analysis | ||
)); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
// Copyright(c) Microsoft Corporation | ||
// All rights reserved. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the License); you may not use | ||
// this file except in compliance with the License. You may obtain a copy of the | ||
// License at http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS | ||
// OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY | ||
// IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, | ||
// MERCHANTABILITY OR NON-INFRINGEMENT. | ||
// | ||
// See the Apache Version 2.0 License for specific language governing | ||
// permissions and limitations under the License. | ||
|
||
using System.Linq; | ||
using Microsoft.Python.Analysis.Analyzer; | ||
using Microsoft.Python.Analysis.Diagnostics; | ||
using Microsoft.Python.Analysis.Types; | ||
using Microsoft.Python.Core; | ||
using Microsoft.Python.Parsing; | ||
using Microsoft.Python.Parsing.Ast; | ||
|
||
namespace Microsoft.Python.Analysis.Extensions { | ||
public static class PythonPropertyExtensions { | ||
/// <summary> | ||
/// Returns true if the decorator combiantions for the property are valid | ||
/// </summary> | ||
public static bool HasValidDecorators(this IPythonPropertyType p, IExpressionEvaluator eval) { | ||
bool valid = true; | ||
// If property is abstract, allow all decorators because it will be overridden | ||
if(p.IsAbstract) { | ||
return valid; | ||
} | ||
|
||
foreach (var dec in (p.FunctionDefinition?.Decorators?.Decorators).MaybeEnumerate().OfType<NameExpression>()) { | ||
CTrando marked this conversation as resolved.
Show resolved
Hide resolved
|
||
switch (dec.Name) { | ||
case @"staticmethod": | ||
ReportInvalidDecorator(dec, Resources.InvalidDecoratorForProperty.FormatInvariant("Staticmethods"), eval); | ||
valid = false; | ||
break; | ||
case @"classmethod": | ||
ReportInvalidDecorator(dec, Resources.InvalidDecoratorForProperty.FormatInvariant("Classmethods"), eval); | ||
valid = false; | ||
break; | ||
} | ||
} | ||
return valid; | ||
} | ||
|
||
private static void ReportInvalidDecorator(NameExpression name, string errorMsg, IExpressionEvaluator eval) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Very similar to the one in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Instead of passing type, pass actual flags. I.e. |
||
eval.ReportDiagnostics(eval.Module.Uri, | ||
new DiagnosticsEntry( | ||
errorMsg, eval.GetLocation(name).Span, | ||
Diagnostics.ErrorCodes.InvalidDecoratorCombination, | ||
Severity.Warning, DiagnosticSource.Analysis | ||
)); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -37,8 +37,5 @@ public static void TransferDocumentationAndLocation(this IPythonType s, IPythonT | |
dst.Location = src.Location; | ||
} | ||
} | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Removing because was unused and does not check for declaration type and null check for name. |
||
public static bool IsConstructor(this IPythonClassMember m) | ||
=> m.Name.EqualsOrdinal("__init__") || m.Name.EqualsOrdinal("__new__"); | ||
} | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Need to check for Type in the Mro, don't know an easy way besides this