From 36dac48969404757250a9fd030b47818c5b78868 Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Fri, 4 Oct 2019 10:26:03 -0700 Subject: [PATCH] Treat None as its own type and instance --- .../Ast/Impl/Analyzer/PythonInterpreter.cs | 3 +- src/Analysis/Ast/Impl/Values/PythonNone.cs | 33 ++++++++++++++++++ .../Ast/Test/LintInheritNonClassTests.cs | 34 +++++++++++++++++++ src/Analysis/Ast/Test/TypingTests.cs | 13 +++++++ 4 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 src/Analysis/Ast/Impl/Values/PythonNone.cs diff --git a/src/Analysis/Ast/Impl/Analyzer/PythonInterpreter.cs b/src/Analysis/Ast/Impl/Analyzer/PythonInterpreter.cs index 556c7665c..14511bd0d 100644 --- a/src/Analysis/Ast/Impl/Analyzer/PythonInterpreter.cs +++ b/src/Analysis/Ast/Impl/Analyzer/PythonInterpreter.cs @@ -22,6 +22,7 @@ using Microsoft.Python.Analysis.Modules.Resolution; using Microsoft.Python.Analysis.Specializations.Typing; using Microsoft.Python.Analysis.Types; +using Microsoft.Python.Analysis.Values; using Microsoft.Python.Core; using Microsoft.Python.Core.Collections; using Microsoft.Python.Core.Services; @@ -138,7 +139,7 @@ CancellationToken cancellationToken } if (id == BuiltinTypeId.NoneType) { - type = new PythonType("NoneType", new Location(_moduleResolution.BuiltinsModule), string.Empty, BuiltinTypeId.NoneType); + type = new PythonNone(_moduleResolution.BuiltinsModule); } else { var bm = _moduleResolution.BuiltinsModule; var typeName = id.GetTypeName(LanguageVersion); diff --git a/src/Analysis/Ast/Impl/Values/PythonNone.cs b/src/Analysis/Ast/Impl/Values/PythonNone.cs new file mode 100644 index 000000000..6cbafcee4 --- /dev/null +++ b/src/Analysis/Ast/Impl/Values/PythonNone.cs @@ -0,0 +1,33 @@ +// 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 Microsoft.Python.Analysis.Types; +using Microsoft.Python.Analysis.Values.Collections; + +namespace Microsoft.Python.Analysis.Values { + internal sealed class PythonNone : PythonType, IPythonInstance { + public PythonNone(IBuiltinsPythonModule builtins) : base("None", new Location(builtins), string.Empty, BuiltinTypeId.NoneType) { } + + public override IPythonInstance CreateInstance(IArgumentSet args) => this; + + public IPythonType Type => this; + + public IMember Call(string memberName, IArgumentSet args) => DeclaringModule.Interpreter.UnknownType; + + public IPythonIterator GetIterator() => new EmptyIterator(DeclaringModule.Interpreter.UnknownType); + + public IMember Index(IArgumentSet args) => DeclaringModule.Interpreter.UnknownType; + } +} diff --git a/src/Analysis/Ast/Test/LintInheritNonClassTests.cs b/src/Analysis/Ast/Test/LintInheritNonClassTests.cs index 812e8d6be..91fc86c5f 100644 --- a/src/Analysis/Ast/Test/LintInheritNonClassTests.cs +++ b/src/Analysis/Ast/Test/LintInheritNonClassTests.cs @@ -489,5 +489,39 @@ class C(Enum): ... var analysis = await GetAnalysisAsync(code); analysis.Diagnostics.Should().BeEmpty(); } + + [TestMethod, Priority(0)] + public async Task InheritFromNone() { + const string code = @" +class C(None): + def method(self): + return 'test' +"; + var analysis = await GetAnalysisAsync(code); + analysis.Diagnostics.Should().HaveCount(1); + + var diagnostic = analysis.Diagnostics.ElementAt(0); + diagnostic.SourceSpan.Should().Be(2, 9, 2, 13); + diagnostic.Message.Should().Be(Resources.InheritNonClass.FormatInvariant("None")); + diagnostic.ErrorCode.Should().Be(ErrorCodes.InheritNonClass); + } + + [TestMethod, Priority(0)] + public async Task InheritFromNoneVar() { + const string code = @" +x = None + +class C(x): + def method(self): + return 'test' +"; + var analysis = await GetAnalysisAsync(code); + analysis.Diagnostics.Should().HaveCount(1); + + var diagnostic = analysis.Diagnostics.ElementAt(0); + diagnostic.SourceSpan.Should().Be(4, 9, 4, 10); + diagnostic.Message.Should().Be(Resources.InheritNonClass.FormatInvariant("x")); + diagnostic.ErrorCode.Should().Be(ErrorCodes.InheritNonClass); + } } } diff --git a/src/Analysis/Ast/Test/TypingTests.cs b/src/Analysis/Ast/Test/TypingTests.cs index 31d9fd1de..cd5b6074e 100644 --- a/src/Analysis/Ast/Test/TypingTests.cs +++ b/src/Analysis/Ast/Test/TypingTests.cs @@ -329,6 +329,19 @@ class A: .Which.Should().HaveType(BuiltinTypeId.Str); } + [TestMethod, Priority(0)] + public async Task TupleOfNones() { + const string code = @" +from typing import Tuple + +x: Tuple[None, None, None] +"; + var analysis = await GetAnalysisAsync(code, PythonVersions.LatestAvailable3X); + + analysis.Should().HaveVariable("x") + .Which.Should().HaveType("Tuple[None, None, None]"); + } + [TestMethod, Priority(0)] public void AnnotationParsing() { AssertTransform("List", "NameOp:List");