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

Commit b25869a

Browse files
committed
Adding diagnostic error if the user calls NewType with the first arg not of string type
1 parent d61f63d commit b25869a

File tree

5 files changed

+177
-16
lines changed

5 files changed

+177
-16
lines changed

src/Analysis/Ast/Impl/Diagnostics/ErrorCodes.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,6 @@ public static class ErrorCodes {
2525
public const string UndefinedVariable = "undefined-variable";
2626
public const string VariableNotDefinedGlobally= "variable-not-defined-globally";
2727
public const string VariableNotDefinedNonLocal = "variable-not-defined-nonlocal";
28+
public const string NewTypeArguments = "newtype-arguments";
2829
}
2930
}

src/Analysis/Ast/Impl/Resources.Designer.cs

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Analysis/Ast/Impl/Resources.resx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,4 +189,7 @@
189189
<data name="UnableToDetermineCachePathException" xml:space="preserve">
190190
<value>Unable to determine analysis cache path. Exception: {0}. Using default '{1}'.</value>
191191
</data>
192+
<data name="NewTypeFirstArgNotString" xml:space="preserve">
193+
<value>The first argument to NewType must be a string but is of type '{0}'.</value>
194+
</data>
192195
</root>

src/Analysis/Ast/Impl/Specializations/Typing/TypingModule.cs

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
using System.Collections.Generic;
1717
using System.Linq;
18+
using Microsoft.Python.Analysis.Diagnostics;
1819
using Microsoft.Python.Analysis.Modules;
1920
using Microsoft.Python.Analysis.Specializations.Typing.Types;
2021
using Microsoft.Python.Analysis.Types;
@@ -61,7 +62,7 @@ private void SpecializeMembers() {
6162
o = new PythonFunctionOverload(fn.Name, location);
6263
// When called, create generic parameter type. For documentation
6364
// use original TypeVar declaration so it appear as a tooltip.
64-
o.SetReturnValueProvider((interpreter, overload, args) => CreateTypeAlias(args.Values<IMember>()));
65+
o.SetReturnValueProvider((interpreter, overload, args) => CreateTypeAlias(args));
6566
fn.AddOverload(o);
6667
_members["NewType"] = fn;
6768

@@ -81,41 +82,41 @@ private void SpecializeMembers() {
8182

8283
_members["Iterable"] = new GenericType("Iterable", typeArgs => CreateListType("Iterable", BuiltinTypeId.List, typeArgs, false), this);
8384
_members["Sequence"] = new GenericType("Sequence", typeArgs => CreateListType("Sequence", BuiltinTypeId.List, typeArgs, false), this);
84-
_members["MutableSequence"] = new GenericType("MutableSequence",
85+
_members["MutableSequence"] = new GenericType("MutableSequence",
8586
typeArgs => CreateListType("MutableSequence", BuiltinTypeId.List, typeArgs, true), this);
86-
_members["List"] = new GenericType("List",
87+
_members["List"] = new GenericType("List",
8788
typeArgs => CreateListType("List", BuiltinTypeId.List, typeArgs, true), this);
8889

89-
_members["MappingView"] = new GenericType("MappingView",
90+
_members["MappingView"] = new GenericType("MappingView",
9091
typeArgs => CreateDictionary("MappingView", typeArgs, false), this);
9192

9293
_members["KeysView"] = new GenericType("KeysView", CreateKeysViewType, this);
9394
_members["ValuesView"] = new GenericType("ValuesView", CreateValuesViewType, this);
9495
_members["ItemsView"] = new GenericType("ItemsView", CreateItemsViewType, this);
9596

96-
_members["Set"] = new GenericType("Set",
97+
_members["Set"] = new GenericType("Set",
9798
typeArgs => CreateListType("Set", BuiltinTypeId.Set, typeArgs, true), this);
98-
_members["MutableSet"] = new GenericType("MutableSet",
99+
_members["MutableSet"] = new GenericType("MutableSet",
99100
typeArgs => CreateListType("MutableSet", BuiltinTypeId.Set, typeArgs, true), this);
100-
_members["FrozenSet"] = new GenericType("FrozenSet",
101+
_members["FrozenSet"] = new GenericType("FrozenSet",
101102
typeArgs => CreateListType("FrozenSet", BuiltinTypeId.Set, typeArgs, false), this);
102103

103104
_members["Tuple"] = new GenericType("Tuple", CreateTupleType, this);
104105

105-
_members["Mapping"] = new GenericType("Mapping",
106+
_members["Mapping"] = new GenericType("Mapping",
106107
typeArgs => CreateDictionary("Mapping", typeArgs, false), this);
107-
_members["MutableMapping"] = new GenericType("MutableMapping",
108+
_members["MutableMapping"] = new GenericType("MutableMapping",
108109
typeArgs => CreateDictionary("MutableMapping", typeArgs, true), this);
109-
_members["Dict"] = new GenericType("Dict",
110+
_members["Dict"] = new GenericType("Dict",
110111
typeArgs => CreateDictionary("Dict", typeArgs, true), this);
111-
_members["OrderedDict"] = new GenericType("OrderedDict",
112+
_members["OrderedDict"] = new GenericType("OrderedDict",
112113
typeArgs => CreateDictionary("OrderedDict", typeArgs, true), this);
113-
_members["DefaultDict"] = new GenericType("DefaultDict",
114+
_members["DefaultDict"] = new GenericType("DefaultDict",
114115
typeArgs => CreateDictionary("DefaultDict", typeArgs, true), this);
115116

116117
_members["Union"] = new GenericType("Union", CreateUnion, this);
117118

118-
_members["Counter"] = Specialized.Function("Counter", this, GetMemberDocumentation("Counter"),
119+
_members["Counter"] = Specialized.Function("Counter", this, GetMemberDocumentation("Counter"),
119120
new PythonInstance(Interpreter.GetBuiltinType(BuiltinTypeId.Int)));
120121

121122
_members["SupportsInt"] = Interpreter.GetBuiltinType(BuiltinTypeId.Int);
@@ -217,13 +218,25 @@ private IPythonType CreateItemsViewType(IReadOnlyList<IPythonType> typeArgs) {
217218
return Interpreter.UnknownType;
218219
}
219220

220-
private IPythonType CreateTypeAlias(IReadOnlyList<IMember> typeArgs) {
221+
private IPythonType CreateTypeAlias(IArgumentSet args) {
222+
var typeArgs = args.Values<IMember>();
221223
if (typeArgs.Count == 2) {
222224
var typeName = (typeArgs[0] as IPythonConstant)?.Value as string;
223225
if (!string.IsNullOrEmpty(typeName)) {
224226
return new TypeAlias(typeName, typeArgs[1].GetPythonType() ?? Interpreter.UnknownType);
225227
}
226-
// TODO: report incorrect first argument to NewVar
228+
229+
var firstArgType = (typeArgs[0] as PythonInstance)?.Type.Name;
230+
var eval = args.Eval;
231+
var expression = args.Expression;
232+
233+
eval.ReportDiagnostics(
234+
eval.Module?.Uri,
235+
new DiagnosticsEntry(Resources.NewTypeFirstArgNotString.FormatInvariant(firstArgType),
236+
expression.GetLocation(eval.Module).Span,
237+
Diagnostics.ErrorCodes.NewTypeArguments,
238+
Severity.Error, DiagnosticSource.Analysis)
239+
);
227240
}
228241
// TODO: report wrong number of arguments
229242
return Interpreter.UnknownType;
@@ -331,7 +344,7 @@ private IPythonType CreateGenericClassParameter(IReadOnlyList<IPythonType> typeA
331344
return Interpreter.UnknownType;
332345
}
333346

334-
private IPythonType ToGenericTemplate(string typeName, IGenericTypeDefinition[] typeArgs, BuiltinTypeId typeId)
347+
private IPythonType ToGenericTemplate(string typeName, IGenericTypeDefinition[] typeArgs, BuiltinTypeId typeId)
335348
=> _members[typeName] is GenericType gt
336349
? new GenericType(CodeFormatter.FormatSequence(typeName, '[', typeArgs), gt.SpecificTypeConstructor, this, typeId, typeArgs)
337350
: Interpreter.UnknownType;
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
// Copyright(c) Microsoft Corporation
2+
// All rights reserved.
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the License); you may not use
5+
// this file except in compliance with the License. You may obtain a copy of the
6+
// License at http://www.apache.org/licenses/LICENSE-2.0
7+
//
8+
// THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS
9+
// OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY
10+
// IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
11+
// MERCHANTABILITY OR NON-INFRINGEMENT.
12+
//
13+
// See the Apache Version 2.0 License for specific language governing
14+
// permissions and limitations under the License.
15+
16+
using System.Collections.Generic;
17+
using System.Linq;
18+
using System.Threading.Tasks;
19+
using FluentAssertions;
20+
using Microsoft.Python.Analysis.Analyzer;
21+
using Microsoft.Python.Analysis.Core.Interpreter;
22+
using Microsoft.Python.Analysis.Diagnostics;
23+
using Microsoft.Python.Analysis.Tests.FluentAssertions;
24+
using Microsoft.Python.Analysis.Types;
25+
using Microsoft.Python.Core;
26+
using Microsoft.Python.Parsing;
27+
using Microsoft.Python.Parsing.Tests;
28+
using Microsoft.VisualStudio.TestTools.UnitTesting;
29+
using TestUtilities;
30+
using ErrorCodes = Microsoft.Python.Analysis.Diagnostics.ErrorCodes;
31+
32+
namespace Microsoft.Python.Analysis.Tests {
33+
[TestClass]
34+
public class LintNewTypeTests : AnalysisTestBase {
35+
public TestContext TestContext { get; set; }
36+
37+
[TestInitialize]
38+
public void TestInitialize()
39+
=> TestEnvironmentImpl.TestInitialize($"{TestContext.FullyQualifiedTestClassName}.{TestContext.TestName}");
40+
41+
[TestCleanup]
42+
public void Cleanup() => TestEnvironmentImpl.TestCleanup();
43+
44+
[TestMethod, Priority(0)]
45+
public async Task NewTypeIntFirstArg() {
46+
const string code = @"
47+
from typing import NewType
48+
49+
T = NewType(5, int)
50+
51+
";
52+
var analysis = await GetAnalysisAsync(code);
53+
analysis.Diagnostics.Should().HaveCount(1);
54+
55+
var diagnostic = analysis.Diagnostics.ElementAt(0);
56+
diagnostic.ErrorCode.Should().Be(Diagnostics.ErrorCodes.NewTypeArguments);
57+
diagnostic.Message.Should().Be(Resources.NewTypeFirstArgNotString.FormatInvariant("int"));
58+
}
59+
60+
[DataRow("float", "float")]
61+
[DataRow("int", "int")]
62+
[DataRow("complex", "str")]
63+
[DataTestMethod, Priority(0)]
64+
public async Task DifferentTypesFirstArg(string nameType, string type) {
65+
string code = $@"
66+
from typing import NewType
67+
68+
T = NewType({nameType}(10), {type})
69+
70+
";
71+
var analysis = await GetAnalysisAsync(code);
72+
analysis.Diagnostics.Should().HaveCount(1);
73+
74+
var diagnostic = analysis.Diagnostics.ElementAt(0);
75+
diagnostic.ErrorCode.Should().Be(Diagnostics.ErrorCodes.NewTypeArguments);
76+
diagnostic.Message.Should().Be(Resources.NewTypeFirstArgNotString.FormatInvariant(nameType));
77+
}
78+
79+
[TestMethod, Priority(0)]
80+
public async Task ObjectFirstArg() {
81+
string code = $@"
82+
from typing import NewType
83+
84+
class X:
85+
def hello():
86+
pass
87+
88+
h = X()
89+
90+
T = NewType(h, int)
91+
";
92+
var analysis = await GetAnalysisAsync(code);
93+
analysis.Diagnostics.Should().HaveCount(1);
94+
95+
var diagnostic = analysis.Diagnostics.ElementAt(0);
96+
diagnostic.ErrorCode.Should().Be(Diagnostics.ErrorCodes.NewTypeArguments);
97+
diagnostic.Message.Should().Be(Resources.NewTypeFirstArgNotString.FormatInvariant("X"));
98+
}
99+
100+
[TestMethod, Priority(0)]
101+
public async Task GenericFirstArg() {
102+
string code = $@"
103+
from typing import NewType, Generic, TypeVar
104+
105+
T = TypeVar('T', str, int)
106+
107+
class X(Generic[T]):
108+
def __init__(self, p: T):
109+
self.x = p
110+
111+
h = X(5)
112+
T = NewType(h, int)
113+
";
114+
var analysis = await GetAnalysisAsync(code);
115+
analysis.Diagnostics.Should().HaveCount(1);
116+
117+
var diagnostic = analysis.Diagnostics.ElementAt(0);
118+
diagnostic.ErrorCode.Should().Be(Diagnostics.ErrorCodes.NewTypeArguments);
119+
diagnostic.Message.Should().Be(Resources.NewTypeFirstArgNotString.FormatInvariant("X[int]"));
120+
}
121+
122+
[DataRow("'test'", "float")]
123+
[DataRow("'testing'", "int")]
124+
[DataTestMethod, Priority(0)]
125+
public async Task NoDiagnosticOnStringFirstArg(string name, string type) {
126+
string code = $@"
127+
from typing import NewType
128+
129+
T = NewType({name}, {type})
130+
";
131+
var analysis = await GetAnalysisAsync(code);
132+
analysis.Diagnostics.Should().HaveCount(0);
133+
}
134+
}
135+
}

0 commit comments

Comments
 (0)