From 5def6b554f95e5843d479ca79ef8178b61a5573d Mon Sep 17 00:00:00 2001 From: Semyon Proshev Date: Mon, 31 Jul 2017 19:24:24 +0300 Subject: [PATCH] PY-16066 Fixed: basestring not treated as a `type` by PyTypeChecker inspection Remove replacing basestring with union of str and unicode from PyTypeChecker. Force this replacement while inferring type. Update PyTypeAssertionEvaluator to correctly transform union types. --- .../controlflow/PyTypeAssertionEvaluator.java | 6 ++++++ .../stdlib/PyStdlibTypeProvider.java | 2 +- .../python/psi/impl/PyBuiltinCache.java | 20 +++++++++++++++++-- .../python/psi/types/PyTypeChecker.java | 6 ------ .../python/psi/types/PyTypeParser.java | 4 ++++ .../BasestringMatchesType.py | 8 ++++++++ .../PyTypeCheckerInspectionTest.java | 5 +++++ 7 files changed, 42 insertions(+), 9 deletions(-) create mode 100644 python/testData/inspections/PyTypeCheckerInspection/BasestringMatchesType.py diff --git a/python/src/com/jetbrains/python/codeInsight/controlflow/PyTypeAssertionEvaluator.java b/python/src/com/jetbrains/python/codeInsight/controlflow/PyTypeAssertionEvaluator.java index 064a55dcfaac..b36c65c115be 100644 --- a/python/src/com/jetbrains/python/codeInsight/controlflow/PyTypeAssertionEvaluator.java +++ b/python/src/com/jetbrains/python/codeInsight/controlflow/PyTypeAssertionEvaluator.java @@ -17,6 +17,7 @@ package com.jetbrains.python.codeInsight.controlflow; import com.intellij.openapi.util.Ref; import com.intellij.psi.PsiElement; +import com.intellij.util.containers.ContainerUtil; import com.jetbrains.python.PyNames; import com.jetbrains.python.PyTokenTypes; import com.jetbrains.python.psi.*; @@ -26,6 +27,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.Stack; import java.util.function.Function; @@ -184,6 +186,10 @@ public class PyTypeAssertionEvaluator extends PyRecursiveElementVisitor { } return PyUnionType.union(members); } + else if (type instanceof PyUnionType) { + final Collection members = ((PyUnionType)type).getMembers(); + return PyUnionType.union(ContainerUtil.map(members, member -> transformTypeFromAssertion(member, transformToDefinition))); + } else if (type instanceof PyInstantiableType) { final PyInstantiableType instantiableType = (PyInstantiableType)type; return transformToDefinition ? instantiableType.toClass() : instantiableType.toInstance(); diff --git a/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibTypeProvider.java b/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibTypeProvider.java index 0030bf4bf71a..ea88437ca555 100644 --- a/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibTypeProvider.java +++ b/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibTypeProvider.java @@ -118,7 +118,7 @@ public class PyStdlibTypeProvider extends PyTypeProviderBase { final PyBuiltinCache builtinCache = PyBuiltinCache.getInstance(referenceTarget); if (referenceTarget instanceof PyElement && builtinCache.isBuiltin(referenceTarget) && PyNames.BASESTRING.equals(((PyElement)referenceTarget).getName())) { - return builtinCache.getStrOrUnicodeType(); + return builtinCache.getStrOrUnicodeType(true); } return null; } diff --git a/python/src/com/jetbrains/python/psi/impl/PyBuiltinCache.java b/python/src/com/jetbrains/python/psi/impl/PyBuiltinCache.java index 8e02c32cb1b8..fe569b3dba3e 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyBuiltinCache.java +++ b/python/src/com/jetbrains/python/psi/impl/PyBuiltinCache.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -393,7 +393,23 @@ public class PyBuiltinCache { @Nullable public PyType getStrOrUnicodeType() { - return PyUnionType.union(getObjectType("str"), getObjectType("unicode")); + return getStrOrUnicodeType(false); + } + + @Nullable + public PyType getStrOrUnicodeType(boolean definition) { + PyClassLikeType str = getObjectType("str"); + PyClassLikeType unicode = getObjectType("unicode"); + + if (str != null && str.isDefinition() ^ definition) { + str = definition ? str.toClass() : str.toInstance(); + } + + if (unicode != null && unicode.isDefinition() ^ definition) { + unicode = definition ? unicode.toClass() : unicode.toInstance(); + } + + return PyUnionType.union(str, unicode); } @Nullable diff --git a/python/src/com/jetbrains/python/psi/types/PyTypeChecker.java b/python/src/com/jetbrains/python/psi/types/PyTypeChecker.java index 71597c0fecd6..6a46b74354fc 100644 --- a/python/src/com/jetbrains/python/psi/types/PyTypeChecker.java +++ b/python/src/com/jetbrains/python/psi/types/PyTypeChecker.java @@ -90,12 +90,6 @@ public class PyTypeChecker { } return false; } - if (actualClassType != null && PyNames.BASESTRING.equals(actualClassType.getName())) { - final PyBuiltinCache builtinCache = PyBuiltinCache.getInstance(actualClassType.getPyClass()); - if (actualClassType.equals(builtinCache.getObjectType(PyNames.BASESTRING))) { - return match(expected, builtinCache.getStrOrUnicodeType(), context, substitutions, recursive); - } - } if (expected instanceof PyGenericType && substitutions != null) { final PyGenericType generic = (PyGenericType)expected; final PyType subst = substitutions.get(generic); diff --git a/python/src/com/jetbrains/python/psi/types/PyTypeParser.java b/python/src/com/jetbrains/python/psi/types/PyTypeParser.java index 3b104aaeb372..2224ccfa1054 100644 --- a/python/src/com/jetbrains/python/psi/types/PyTypeParser.java +++ b/python/src/com/jetbrains/python/psi/types/PyTypeParser.java @@ -528,6 +528,10 @@ public class PyTypeParser { final PyClassType type = builtinCache.getDictType(); return type != null ? new ParseResult(type, range) : EMPTY_RESULT; } + else if (PyNames.BASESTRING.equals(name)) { + final PyType type = builtinCache.getStrOrUnicodeType(); + return type != null ? new ParseResult(type, range) : EMPTY_RESULT; + } final PyType builtinType = builtinCache.getObjectType(name); if (builtinType != null) { diff --git a/python/testData/inspections/PyTypeCheckerInspection/BasestringMatchesType.py b/python/testData/inspections/PyTypeCheckerInspection/BasestringMatchesType.py new file mode 100644 index 000000000000..0f029c71a864 --- /dev/null +++ b/python/testData/inspections/PyTypeCheckerInspection/BasestringMatchesType.py @@ -0,0 +1,8 @@ +class Filter(object): + def __init__(self, allowed_types): + """ + :type allowed_types: type + """ + pass + +Filter(basestring) \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/inspections/PyTypeCheckerInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/PyTypeCheckerInspectionTest.java index 9dfd6e55ee64..44f6f553c665 100644 --- a/python/testSrc/com/jetbrains/python/inspections/PyTypeCheckerInspectionTest.java +++ b/python/testSrc/com/jetbrains/python/inspections/PyTypeCheckerInspectionTest.java @@ -442,6 +442,11 @@ public class PyTypeCheckerInspectionTest extends PyTestCase { doTest(); } + // PY-16066 + public void testBasestringMatchesType() { + doTest(); + } + // PY-23864 public void testClassObjectAndMetaclassCompatibility() { doTest();