mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
+15
-6
@@ -20,12 +20,16 @@ import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Computable;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.search.searches.ReferencesSearch;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.refactoring.chainCall.ChainCallExtractor;
|
||||
import com.intellij.refactoring.ui.TypeSelectorManagerImpl;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import one.util.streamex.StreamEx;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author Tagir Valeev
|
||||
*/
|
||||
@@ -59,23 +63,28 @@ public class ChainCallInplaceIntroducer extends JavaVariableInplaceIntroducer {
|
||||
return myCall;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
protected PsiVariable introduceVariable() {
|
||||
PsiVariable variable = super.introduceVariable();
|
||||
protected PsiVariable createFieldToStartTemplateOn(String[] names, PsiType psiType) {
|
||||
PsiVariable variable = introduceVariable();
|
||||
if (variable instanceof PsiLocalVariable) {
|
||||
PsiLambdaExpression lambda = ApplicationManager.getApplication().runWriteAction(
|
||||
(Computable<PsiLambdaExpression>)() -> ChainCallExtractor.extractMappingStep(myProject, (PsiLocalVariable)variable));
|
||||
if (lambda != null) {
|
||||
PsiParameter parameter = ArrayUtil.getFirstElement(lambda.getParameterList().getParameters());
|
||||
PsiParameter parameter = Objects.requireNonNull(ArrayUtil.getFirstElement(lambda.getParameterList().getParameters()));
|
||||
myParameter = parameter;
|
||||
myCall = PsiTreeUtil.getParentOfType(lambda, PsiMethodCallExpression.class);
|
||||
myOccurrences = PsiExpression.EMPTY_ARRAY;
|
||||
myOccurrenceMarkers = null;
|
||||
myExprMarker = null;
|
||||
myExpr = null;
|
||||
myOccurrences = StreamEx.of(ReferencesSearch.search(parameter).findAll()).map(PsiReference::getElement).select(PsiExpression.class)
|
||||
.toArray(PsiExpression[]::new);
|
||||
myOccurrenceMarkers = null;
|
||||
final PsiIdentifier identifier = variable.getNameIdentifier();
|
||||
if (identifier != null) {
|
||||
myEditor.getCaretModel().moveToOffset(identifier.getTextOffset());
|
||||
}
|
||||
setAdvertisementText(null);
|
||||
PsiDocumentManager.getInstance(myProject).doPostponedOperationsAndUnblockDocument(myEditor.getDocument());
|
||||
initOccurrencesMarkers();
|
||||
return parameter;
|
||||
}
|
||||
else if (!variable.isValid()) {
|
||||
|
||||
-3
@@ -398,8 +398,6 @@ public class JavaVariableInplaceIntroducer extends AbstractJavaInplaceIntroducer
|
||||
protected PsiVariable createFieldToStartTemplateOn(String[] names, PsiType psiType) {
|
||||
PsiVariable variable = introduceVariable();
|
||||
|
||||
if (variable == null) return null;
|
||||
|
||||
final PsiVariable restoredVar = getVariable();
|
||||
if (restoredVar != null) {
|
||||
variable = restoredVar;
|
||||
@@ -429,7 +427,6 @@ public class JavaVariableInplaceIntroducer extends AbstractJavaInplaceIntroducer
|
||||
return variable;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected PsiVariable introduceVariable() {
|
||||
PsiVariable variable = IntroduceVariableBase
|
||||
.introduce(myProject, myExpr, myEditor, myChosenAnchor.getElement(), getOccurrences(), mySettings);
|
||||
|
||||
@@ -255,16 +255,16 @@ public class LambdaUtil {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static List<HierarchicalMethodSignature> hasSubsignature(List<HierarchicalMethodSignature> signatures) {
|
||||
private static List<HierarchicalMethodSignature> hasSubSignature(List<HierarchicalMethodSignature> signatures) {
|
||||
for (HierarchicalMethodSignature signature : signatures) {
|
||||
boolean subsignature = true;
|
||||
boolean subSignature = true;
|
||||
for (HierarchicalMethodSignature methodSignature : signatures) {
|
||||
if (!signature.equals(methodSignature) && !skipMethod(signature, methodSignature)) {
|
||||
subsignature = false;
|
||||
subSignature = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (subsignature) return Collections.singletonList(signature);
|
||||
if (subSignature) return Collections.singletonList(signature);
|
||||
}
|
||||
return signatures;
|
||||
}
|
||||
@@ -307,7 +307,7 @@ public class LambdaUtil {
|
||||
}
|
||||
}
|
||||
|
||||
return hasSubsignature(methods);
|
||||
return hasSubSignature(methods);
|
||||
}
|
||||
|
||||
|
||||
@@ -554,12 +554,6 @@ public class LambdaUtil {
|
||||
typeByExpression instanceof PsiLambdaParameterType;
|
||||
}
|
||||
|
||||
public static boolean isLambdaReturnExpression(PsiElement element) {
|
||||
final PsiElement parent = element.getParent();
|
||||
return parent instanceof PsiLambdaExpression ||
|
||||
parent instanceof PsiReturnStatement && PsiTreeUtil.getParentOfType(parent, PsiLambdaExpression.class, true, PsiMethod.class) != null;
|
||||
}
|
||||
|
||||
public static PsiReturnStatement[] getReturnStatements(PsiLambdaExpression lambdaExpression) {
|
||||
final PsiElement body = lambdaExpression.getBody();
|
||||
return body instanceof PsiCodeBlock ? PsiUtil.findReturnStatements((PsiCodeBlock)body) : PsiReturnStatement.EMPTY_ARRAY;
|
||||
|
||||
@@ -15,11 +15,11 @@
|
||||
*/
|
||||
class Test {
|
||||
|
||||
private String x;
|
||||
private String s;
|
||||
|
||||
void simpleMethod() {
|
||||
x = "";
|
||||
System.out.println(x);
|
||||
System.out.println(x);
|
||||
s = "";
|
||||
System.out.println(s);
|
||||
System.out.println(s);
|
||||
}
|
||||
}
|
||||
|
||||
+13
-11
@@ -112,7 +112,7 @@ class JsonBySchemaObjectCompletionContributor extends CompletionContributor {
|
||||
@NotNull VirtualFile schemaFile,
|
||||
@NotNull List<JsonSchemaWalker.Step> steps) {
|
||||
if (isName) {
|
||||
final boolean insertComma = myWalker.hasPropertiesBehind(myPosition);
|
||||
final boolean insertComma = myWalker.hasPropertiesBehindAndNoComma(myPosition);
|
||||
final boolean hasValue = myWalker.isPropertyWithValue(myPosition.getParent().getParent());
|
||||
|
||||
final Collection<String> properties = myWalker.getPropertyNamesOfParentObject(myOriginalPosition);
|
||||
@@ -222,7 +222,7 @@ class JsonBySchemaObjectCompletionContributor extends CompletionContributor {
|
||||
|
||||
final JsonSchemaType type = jsonSchemaObject.getType();
|
||||
final List<Object> values = jsonSchemaObject.getEnum();
|
||||
if (type != null || !ContainerUtil.isEmpty(values)) {
|
||||
if (type != null || !ContainerUtil.isEmpty(values) || jsonSchemaObject.getDefault() != null) {
|
||||
builder = builder.withInsertHandler(createPropertyInsertHandler(jsonSchemaObject, hasValue, insertComma));
|
||||
} else if (!hasValue) {
|
||||
builder = builder.withInsertHandler(createDefaultPropertyInsertHandler(hasValue, insertComma));
|
||||
@@ -261,7 +261,8 @@ class JsonBySchemaObjectCompletionContributor extends CompletionContributor {
|
||||
final List<Object> values = jsonSchemaObject.getEnum();
|
||||
if (type == null && values != null && !values.isEmpty()) type = detectType(values);
|
||||
final Object defaultValue = jsonSchemaObject.getDefault();
|
||||
final String defaultValueAsString = defaultValue == null ? null : String.valueOf(defaultValue);
|
||||
final String defaultValueAsString = defaultValue == null ? null : defaultValue instanceof String ? "\"" + defaultValue + "\"" :
|
||||
String.valueOf(defaultValue);
|
||||
JsonSchemaType finalType = type;
|
||||
return new InsertHandler<LookupElement>() {
|
||||
@Override
|
||||
@@ -355,18 +356,19 @@ class JsonBySchemaObjectCompletionContributor extends CompletionContributor {
|
||||
String defaultValue,
|
||||
List<Object> values,
|
||||
JsonSchemaType type, String comma) {
|
||||
final boolean isNumber = JsonSchemaType._integer.equals(type) || JsonSchemaType._number.equals(type);
|
||||
String start = isNumber ? ":" : ":\"";
|
||||
String end = isNumber ? "" : "\"" + comma;
|
||||
final boolean isNumber = type != null && (JsonSchemaType._integer.equals(type) || JsonSchemaType._number.equals(type)) ||
|
||||
type == null && (defaultValue != null &&
|
||||
!StringUtil.isQuotedString(defaultValue) || values != null && ContainerUtil.and(values, v -> !(v instanceof String)));
|
||||
boolean hasValues = !ContainerUtil.isEmpty(values);
|
||||
boolean hasDefaultValue = !StringUtil.isEmpty(defaultValue);
|
||||
String stringToInsert = start + (hasDefaultValue ? defaultValue : "") + end;
|
||||
EditorModificationUtil.insertStringAtCaret(editor, stringToInsert, false, true, start.length() - comma.length());
|
||||
if (hasDefaultValue) {
|
||||
String stringToInsert = ":" + (hasDefaultValue ? defaultValue : (isNumber ? "" : "\"\"")) + comma;
|
||||
EditorModificationUtil.insertStringAtCaret(editor, stringToInsert, false, true, 1);
|
||||
if (!isNumber || hasDefaultValue) {
|
||||
SelectionModel model = editor.getSelectionModel();
|
||||
int caretStart = model.getSelectionStart();
|
||||
int newOffset = caretStart + defaultValue.length();
|
||||
model.setSelection(caretStart, newOffset);
|
||||
int newOffset = caretStart + (hasDefaultValue ? defaultValue.length() : 1);
|
||||
if (hasDefaultValue && !isNumber) newOffset--;
|
||||
model.setSelection(isNumber ? caretStart : (caretStart + 1), newOffset);
|
||||
editor.getCaretModel().moveToOffset(newOffset);
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ public interface JsonLikePsiWalker {
|
||||
PsiElement goUpToCheckable(@NotNull final PsiElement element);
|
||||
List<JsonSchemaWalker.Step> findPosition(@NotNull final PsiElement element, boolean isName, boolean forceLastTransition);
|
||||
boolean isNameQuoted();
|
||||
boolean hasPropertiesBehind(@NotNull PsiElement element);
|
||||
boolean hasPropertiesBehindAndNoComma(@NotNull PsiElement element);
|
||||
Set<String> getPropertyNamesOfParentObject(@NotNull PsiElement element);
|
||||
@Nullable JsonPropertyAdapter getParentPropertyAdapter(@NotNull PsiElement element);
|
||||
boolean isTopJsonElement(@NotNull PsiElement element);
|
||||
|
||||
@@ -15,13 +15,13 @@
|
||||
*/
|
||||
package com.jetbrains.jsonSchema.impl;
|
||||
|
||||
import com.intellij.json.JsonElementTypes;
|
||||
import com.intellij.json.psi.*;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.impl.source.tree.LeafPsiElement;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -122,12 +122,21 @@ public class JsonOriginalPsiWalker implements JsonLikePsiWalker {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPropertiesBehind(@NotNull PsiElement element) {
|
||||
public boolean hasPropertiesBehindAndNoComma(@NotNull PsiElement element) {
|
||||
PsiElement current = element instanceof JsonProperty ? element : PsiTreeUtil.getParentOfType(element, JsonProperty.class);
|
||||
while (current != null && current.getNode().getElementType() != JsonElementTypes.COMMA) {
|
||||
current = current.getNextSibling();
|
||||
}
|
||||
int commaOffset = current == null ? Integer.MAX_VALUE : current.getTextRange().getStartOffset();
|
||||
final int offset = element.getTextRange().getStartOffset();
|
||||
final JsonObject object = PsiTreeUtil.getParentOfType(element, JsonObject.class);
|
||||
if (object != null) {
|
||||
return ContainerUtil.or(object.getPropertyList(), prop -> prop.getTextRange().getStartOffset() >= offset &&
|
||||
!PsiTreeUtil.isAncestor(prop, element, false));
|
||||
for (JsonProperty property : object.getPropertyList()) {
|
||||
final int pOffset = property.getTextRange().getStartOffset();
|
||||
if (pOffset >= offset && !PsiTreeUtil.isAncestor(property, element, false)) {
|
||||
return pOffset < commaOffset;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -179,12 +179,7 @@ public class ReadJsonSchemaFromPsi {
|
||||
if (value instanceof JsonStringLiteral) {
|
||||
objects.add("\"" + StringUtil.unquoteString(((JsonStringLiteral)value).getValue()) + "\"");
|
||||
} else if (value instanceof JsonNumberLiteral) {
|
||||
try {
|
||||
final int i = Integer.parseInt(value.getText());
|
||||
objects.add(i);
|
||||
} catch (NumberFormatException e) {
|
||||
objects.add(((JsonNumberLiteral)value).getValue());
|
||||
}
|
||||
objects.add(getNumber(value));
|
||||
} else if (value instanceof JsonBooleanLiteral) {
|
||||
objects.add(((JsonBooleanLiteral)value).getValue());
|
||||
} else if (value instanceof JsonNullLiteral) {
|
||||
@@ -196,6 +191,17 @@ public class ReadJsonSchemaFromPsi {
|
||||
};
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static Number getNumber(@NotNull JsonValue value) {
|
||||
Number numberValue;
|
||||
try {
|
||||
numberValue = Integer.parseInt(value.getText());
|
||||
} catch (NumberFormatException e) {
|
||||
numberValue = ((JsonNumberLiteral)value).getValue();
|
||||
}
|
||||
return numberValue;
|
||||
}
|
||||
|
||||
private PairConsumer<JsonElement, JsonSchemaObject> createDependencies() {
|
||||
return (element, object) -> {
|
||||
if (element instanceof JsonObject) {
|
||||
@@ -330,17 +336,10 @@ public class ReadJsonSchemaFromPsi {
|
||||
} else if (element instanceof JsonStringLiteral) {
|
||||
object.setDefault(StringUtil.unquoteString(((JsonStringLiteral)element).getValue()));
|
||||
} else if (element instanceof JsonNumberLiteral) {
|
||||
object.setDefault(((JsonNumberLiteral)element).getValue());
|
||||
object.setDefault(getNumber((JsonNumberLiteral) element));
|
||||
} else if (element instanceof JsonBooleanLiteral) {
|
||||
object.setDefault(((JsonBooleanLiteral)element).getValue());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private class MyStringReader implements PairConsumer<JsonElement,JsonSchemaObject> {
|
||||
@Override
|
||||
public void consume(JsonElement element, JsonSchemaObject object) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.jetbrains.jsonSchema.impl;
|
||||
|
||||
import com.intellij.codeInsight.completion.CodeCompletionHandlerBase;
|
||||
import com.intellij.codeInsight.completion.CompletionType;
|
||||
import com.jetbrains.jsonSchema.JsonSchemaHeavyAbstractTest;
|
||||
import com.jetbrains.jsonSchema.JsonSchemaMappingsConfigurationBase;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
/**
|
||||
* @author Irina.Chernushina on 3/4/2017.
|
||||
*/
|
||||
public class JsonBySchemaHeavyCompletionTest extends JsonSchemaHeavyAbstractTest {
|
||||
@Override
|
||||
protected String getBasePath() {
|
||||
return "/tests/testData/jsonSchema/completion";
|
||||
}
|
||||
|
||||
public void testInsertEnumValue() throws Exception {
|
||||
baseInsertTest("insertPropertyName", "testName");
|
||||
}
|
||||
|
||||
public void testInsertNameWithDefaultStringValue() throws Exception {
|
||||
baseInsertTest("insertPropertyName", "testNameWithDefaultStringValue");
|
||||
}
|
||||
|
||||
public void testInsertNameWithDefaultIntegerValue() throws Exception {
|
||||
baseInsertTest("insertPropertyName", "testNameWithDefaultIntegerValue");
|
||||
}
|
||||
|
||||
public void testInsertIntegerType() throws Exception {
|
||||
baseInsertTest("insertPropertyName", "testIntegerType");
|
||||
}
|
||||
|
||||
public void testInsertStringType() throws Exception {
|
||||
baseInsertTest("insertPropertyName", "testStringType");
|
||||
}
|
||||
|
||||
public void testInsertObjectType() throws Exception {
|
||||
baseInsertTest("insertPropertyName", "testObjectType");
|
||||
}
|
||||
|
||||
public void testInsertBooleanType() throws Exception {
|
||||
baseInsertTest("insertPropertyName", "testBooleanType");
|
||||
}
|
||||
|
||||
//no quotes
|
||||
public void testNameWithDefaultStringValueNoQuotes() throws Exception {
|
||||
baseInsertTest("insertPropertyName", "testNameWithDefaultStringValueNoQuotes");
|
||||
}
|
||||
|
||||
public void testNameWithDefaultIntegerValueNoQuotesComma() throws Exception {
|
||||
baseInsertTest("insertPropertyName", "testNameWithDefaultIntegerValueNoQuotesComma");
|
||||
}
|
||||
|
||||
//comma
|
||||
public void testInsertIntegerTypeComma() throws Exception {
|
||||
baseInsertTest("insertPropertyName", "testIntegerTypeComma");
|
||||
}
|
||||
|
||||
public void testInsertBooleanTypeComma() throws Exception {
|
||||
baseInsertTest("insertPropertyName", "testBooleanTypeComma");
|
||||
}
|
||||
|
||||
public void testStringTypeComma() throws Exception {
|
||||
baseInsertTest("insertPropertyName", "testStringTypeComma");
|
||||
}
|
||||
|
||||
public void testNameWithDefaultStringValueComma() throws Exception {
|
||||
baseInsertTest("insertPropertyName", "testNameWithDefaultStringValueComma");
|
||||
}
|
||||
|
||||
private void baseInsertTest(final String folder, final String testFile) throws Exception {
|
||||
skeleton(new Callback() {
|
||||
@Override
|
||||
public void registerSchemes() {
|
||||
final String moduleDir = getModuleDir(getProject());
|
||||
|
||||
final JsonSchemaMappingsConfigurationBase.SchemaInfo base =
|
||||
new JsonSchemaMappingsConfigurationBase.SchemaInfo("base", moduleDir + "/Schema.json", false,
|
||||
Collections
|
||||
.singletonList(new JsonSchemaMappingsConfigurationBase.Item("*.json", true, false)));
|
||||
addSchema(base);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configureFiles() throws Exception {
|
||||
configureByFiles(null, "/" + folder + "/" + testFile + ".json", "/" + folder + "/Schema.json");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doCheck() {
|
||||
final CodeCompletionHandlerBase handlerBase = new CodeCompletionHandlerBase(CompletionType.BASIC);
|
||||
handlerBase.invokeCompletion(getProject(), getEditor());
|
||||
if (myItems != null) {
|
||||
selectItem(myItems[0]);
|
||||
}
|
||||
try {
|
||||
checkResultByFile("/" + folder + "/" + testFile + "_after.json");
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"properties": {
|
||||
"jsx": {
|
||||
"enum": [ "preserve", "react", "react-native" ]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"jsx": <caret>
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"jsx": "<selection>preserve<caret><selection>"
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"properties": {
|
||||
"jsx": {
|
||||
"enum": [ "preserve", "react", "react-native" ]
|
||||
},
|
||||
"withStringDefaultValue": {
|
||||
"default": "stringDefault"
|
||||
},
|
||||
"withIntegerDefaultValue": {
|
||||
"default": 118
|
||||
},
|
||||
"integerType": {
|
||||
"type": "integer"
|
||||
},
|
||||
"stringType": {
|
||||
"type": "string"
|
||||
},
|
||||
"objectType": {
|
||||
"type": "object"
|
||||
},
|
||||
"booleanType": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"booleanTy<caret>"
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"booleanTy<caret>"
|
||||
"someOtherProperty": false
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"booleanType": <selection>false<caret></selection>,
|
||||
"someOtherProperty": false
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"booleanType": <selection>false<caret></selection>
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"integerTy<caret>"
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"integerTy<caret>"
|
||||
"someOtherProperty": 1
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"integerType":<caret>,
|
||||
"someOtherProperty": 1
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"integerType":<caret>
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"js<caret>"
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"withIntegerDefault<caret>"
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
{
|
||||
withIntegerDefault<caret>
|
||||
"someOtherProperty": 112
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"withIntegerDefaultValue": <selection>118<caret></selection>,
|
||||
"someOtherProperty": 112
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"withIntegerDefaultValue": <selection>118<caret></selection>
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"withStringDefault<caret>"
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"withStringDefault<caret>",
|
||||
"some": 2
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"withStringDefaultValue": "<selection>stringDefault<caret></selection>",
|
||||
"some": 2
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
{
|
||||
withStringDefault<caret>
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"withStringDefaultValue": "<selection>stringDefault<caret></selection>"
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"withStringDefaultValue": "<selection>stringDefault<caret></selection>"
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"jsx": "<caret>"
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"objectTy<caret>"
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"objectType": {
|
||||
<caret>
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"stringTy<caret>"
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"stringTy<caret>"
|
||||
"someOtherProperty": "123"
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"stringType": "<caret>",
|
||||
"someOtherProperty": "123"
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"stringType": "<caret>"
|
||||
}
|
||||
+1
@@ -29,6 +29,7 @@ public class HighlightUsagesDescriptionLocation extends ElementDescriptionLocati
|
||||
private HighlightUsagesDescriptionLocation() {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public ElementDescriptionProvider getDefaultProvider() {
|
||||
return new ElementDescriptionProvider() {
|
||||
|
||||
@@ -17,15 +17,15 @@
|
||||
package com.intellij.usageView;
|
||||
|
||||
import com.intellij.ide.TypePresentationService;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.meta.PsiMetaData;
|
||||
import com.intellij.psi.meta.PsiMetaOwner;
|
||||
import com.intellij.psi.meta.PsiPresentableMetaData;
|
||||
import com.intellij.lang.LangBundle;
|
||||
import com.intellij.lang.Language;
|
||||
import com.intellij.lang.findUsages.FindUsagesProvider;
|
||||
import com.intellij.lang.findUsages.LanguageFindUsages;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.meta.PsiMetaData;
|
||||
import com.intellij.psi.meta.PsiMetaOwner;
|
||||
import com.intellij.psi.meta.PsiPresentableMetaData;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
@@ -37,6 +37,7 @@ public class UsageViewTypeLocation extends ElementDescriptionLocation {
|
||||
|
||||
public static final UsageViewTypeLocation INSTANCE = new UsageViewTypeLocation();
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public ElementDescriptionProvider getDefaultProvider() {
|
||||
return DEFAULT_PROVIDER;
|
||||
|
||||
@@ -32,6 +32,7 @@ public class DeleteNameDescriptionLocation extends ElementDescriptionLocation {
|
||||
public static DeleteNameDescriptionLocation INSTANCE = new DeleteNameDescriptionLocation();
|
||||
private static final ElementDescriptionProvider ourDefaultProvider = new DefaultProvider();
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public ElementDescriptionProvider getDefaultProvider() {
|
||||
return ourDefaultProvider;
|
||||
|
||||
@@ -37,6 +37,7 @@ public class DeleteTypeDescriptionLocation extends ElementDescriptionLocation {
|
||||
|
||||
private static final ElementDescriptionProvider ourDefaultProvider = new DefaultProvider();
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public ElementDescriptionProvider getDefaultProvider() {
|
||||
return ourDefaultProvider;
|
||||
|
||||
+2
@@ -18,6 +18,7 @@ package com.intellij.refactoring.util;
|
||||
|
||||
import com.intellij.psi.ElementDescriptionLocation;
|
||||
import com.intellij.psi.ElementDescriptionProvider;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* @author yole
|
||||
@@ -32,6 +33,7 @@ public class NonCodeSearchDescriptionLocation extends ElementDescriptionLocation
|
||||
public static final NonCodeSearchDescriptionLocation NON_JAVA = new NonCodeSearchDescriptionLocation(true);
|
||||
public static final NonCodeSearchDescriptionLocation STRINGS_AND_COMMENTS = new NonCodeSearchDescriptionLocation(false);
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public ElementDescriptionProvider getDefaultProvider() {
|
||||
return DefaultNonCodeSearchElementDescriptionProvider.INSTANCE;
|
||||
|
||||
+2
@@ -18,6 +18,7 @@ package com.intellij.refactoring.util;
|
||||
|
||||
import com.intellij.psi.ElementDescriptionLocation;
|
||||
import com.intellij.psi.ElementDescriptionProvider;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* @author yole
|
||||
@@ -36,6 +37,7 @@ public class RefactoringDescriptionLocation extends ElementDescriptionLocation {
|
||||
return myWithParent;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public ElementDescriptionProvider getDefaultProvider() {
|
||||
return DefaultRefactoringElementDescriptionProvider.INSTANCE;
|
||||
|
||||
@@ -18,8 +18,8 @@ package com.intellij.usageView;
|
||||
|
||||
import com.intellij.psi.ElementDescriptionLocation;
|
||||
import com.intellij.psi.ElementDescriptionProvider;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiDirectory;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.impl.file.PsiDirectoryFactory;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@@ -32,6 +32,7 @@ public class UsageViewLongNameLocation extends ElementDescriptionLocation {
|
||||
|
||||
public static final UsageViewLongNameLocation INSTANCE = new UsageViewLongNameLocation();
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public ElementDescriptionProvider getDefaultProvider() {
|
||||
return DEFAULT_PROVIDER;
|
||||
|
||||
@@ -37,6 +37,7 @@ public class UsageViewNodeTextLocation extends ElementDescriptionLocation {
|
||||
|
||||
public static final UsageViewNodeTextLocation INSTANCE = new UsageViewNodeTextLocation();
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public ElementDescriptionProvider getDefaultProvider() {
|
||||
return DEFAULT_PROVIDER;
|
||||
|
||||
@@ -34,6 +34,7 @@ public class UsageViewShortNameLocation extends ElementDescriptionLocation {
|
||||
|
||||
public static final UsageViewShortNameLocation INSTANCE = new UsageViewShortNameLocation();
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public ElementDescriptionProvider getDefaultProvider() {
|
||||
return DEFAULT_PROVIDER;
|
||||
|
||||
-2
@@ -951,8 +951,6 @@ public class ActionToolbarImpl extends JPanel implements ActionToolbar, QuickAct
|
||||
myPlace, myActionManager, transparentOnly);
|
||||
|
||||
if (forced || !newVisibleActions.equals(myVisibleActions)) {
|
||||
myPresentationFactory.retainAll(newVisibleActions);
|
||||
|
||||
boolean shouldRebuildUI = newVisibleActions.isEmpty() || myVisibleActions.isEmpty();
|
||||
myVisibleActions = newVisibleActions;
|
||||
|
||||
|
||||
-11
@@ -18,11 +18,9 @@ package com.intellij.openapi.actionSystem.impl;
|
||||
import com.intellij.openapi.actionSystem.AnAction;
|
||||
import com.intellij.openapi.actionSystem.Presentation;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.util.containers.HashSet;
|
||||
import com.intellij.util.containers.WeakHashMap;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
public class PresentationFactory {
|
||||
@@ -54,13 +52,4 @@ public class PresentationFactory {
|
||||
ApplicationManager.getApplication().assertIsDispatchThread();
|
||||
myAction2Presentation.clear();
|
||||
}
|
||||
|
||||
public void retainAll(@NotNull Collection<AnAction> actions) {
|
||||
HashSet<AnAction> toRemove = new HashSet<>(myAction2Presentation.keySet());
|
||||
toRemove.removeAll(actions);
|
||||
|
||||
for (AnAction action : toRemove) {
|
||||
myAction2Presentation.remove(action);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,3 +68,4 @@ com.jetbrains.performancePlugin 1.1 1.3 1.3.1 1.3.2
|
||||
org.vuejs 1.1.0 1.0.1 1.0.0
|
||||
Vue.js 1.1.0 1.0.1 1.0.0
|
||||
com.intellij.resharper.unity 1.5.0.0 1.5.0.1
|
||||
com.nmatveev.idea-plugin-protobuf 0.5.9
|
||||
|
||||
+5
@@ -56,6 +56,11 @@ public abstract class AbstractLanguageInjectionSupport extends LanguageInjection
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useDefaultCommentInjector() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public BaseInjection findCommentInjection(@NotNull PsiElement host, @Nullable Ref<PsiElement> commentRef) {
|
||||
|
||||
+2
@@ -63,6 +63,8 @@ public class CommentLanguageInjector implements MultiHostInjector {
|
||||
if (support == myInjectorSupport && applicableFound) continue;
|
||||
applicableFound = true;
|
||||
|
||||
if (!support.useDefaultCommentInjector())
|
||||
continue;
|
||||
BaseInjection injection = support.findCommentInjection(host, null);
|
||||
if (injection == null) continue;
|
||||
if (!InjectorUtils.registerInjectionSimple(host, injection, support, registrar)) continue;
|
||||
|
||||
+2
@@ -57,6 +57,8 @@ public abstract class LanguageInjectionSupport {
|
||||
|
||||
public abstract boolean useDefaultInjector(PsiLanguageInjectionHost host);
|
||||
|
||||
public abstract boolean useDefaultCommentInjector();
|
||||
|
||||
@Nullable
|
||||
public abstract BaseInjection findCommentInjection(@NotNull PsiElement host, @Nullable Ref<PsiElement> commentRef);
|
||||
|
||||
|
||||
+1
-1
@@ -626,7 +626,7 @@ public class GroovyBlockGenerator {
|
||||
|
||||
private static void addChildNodes(PsiElement elem, ArrayList<ASTNode> childNodes, TextRange range, PsiElement root) {
|
||||
ASTNode node = elem.getNode();
|
||||
if (range.contains(elem.getTextRange()) && node != null && elem != root) {
|
||||
if (range.contains(elem.getTextRange()) && node != null && elem != root && !(elem instanceof PsiFile)) {
|
||||
childNodes.add(node);
|
||||
} else {
|
||||
for (PsiElement child : elem.getChildren()) {
|
||||
|
||||
+7
-12
@@ -120,10 +120,10 @@ public class DuplicateStringLiteralInspection extends BaseLocalInspectionTool {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private List<PsiAnchor> findDuplicateLiterals(String stringToFind, Project project) {
|
||||
private List<PsiLiteralExpression> findDuplicateLiterals(String stringToFind, Project project) {
|
||||
Set<PsiFile> resultFiles = getCandidateFiles(stringToFind, project);
|
||||
if (resultFiles.isEmpty()) return Collections.emptyList();
|
||||
List<PsiAnchor> foundExpr = new ArrayList<>();
|
||||
List<PsiLiteralExpression> foundExpr = new ArrayList<>();
|
||||
|
||||
for (final PsiFile file : resultFiles) {
|
||||
ProgressManager.checkCanceled();
|
||||
@@ -138,7 +138,7 @@ public class DuplicateStringLiteralInspection extends BaseLocalInspectionTool {
|
||||
if (element == null || !(element.getParent() instanceof PsiLiteralExpression)) return true;
|
||||
PsiLiteralExpression expression = (PsiLiteralExpression)element.getParent();
|
||||
if (Comparing.equal(stringToFind, expression.getValue()) && shouldCheck(expression)) {
|
||||
foundExpr.add(PsiAnchor.create(expression));
|
||||
foundExpr.add(expression);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
@@ -157,10 +157,8 @@ public class DuplicateStringLiteralInspection extends BaseLocalInspectionTool {
|
||||
List<PsiLiteralExpression> foundExpr = getDuplicateLiterals(stringToFind, originalExpression);
|
||||
if (foundExpr.isEmpty()) return;
|
||||
Set<PsiClass> classes = new THashSet<>();
|
||||
for (PsiLiteralExpression expr : foundExpr) {
|
||||
if (expr == originalExpression) continue;
|
||||
for (PsiElement aClass : foundExpr) {
|
||||
ProgressManager.checkCanceled();
|
||||
PsiElement aClass = expr;
|
||||
do {
|
||||
aClass = PsiTreeUtil.getParentOfType(aClass, PsiClass.class);
|
||||
}
|
||||
@@ -208,15 +206,12 @@ public class DuplicateStringLiteralInspection extends BaseLocalInspectionTool {
|
||||
@NotNull
|
||||
private List<PsiLiteralExpression> getDuplicateLiterals(String stringToFind, PsiLiteralExpression place) {
|
||||
Project project = place.getProject();
|
||||
Map<String, List<PsiAnchor>> map = CachedValuesManager.getManager(project).getCachedValue(project, () -> {
|
||||
Map<String, List<PsiAnchor>> value = ConcurrentFactoryMap.createConcurrentMap(
|
||||
Map<String, List<PsiLiteralExpression>> map = CachedValuesManager.getManager(project).getCachedValue(project, () -> {
|
||||
Map<String, List<PsiLiteralExpression>> value = ConcurrentFactoryMap.createConcurrentMap(
|
||||
s -> Collections.unmodifiableList(findDuplicateLiterals(s, project)));
|
||||
return CachedValueProvider.Result.create(value, PsiModificationTracker.MODIFICATION_COUNT);
|
||||
});
|
||||
return ContainerUtil.mapNotNull(map.get(stringToFind), anchor -> {
|
||||
PsiLiteralExpression literal = (PsiLiteralExpression)anchor.retrieve();
|
||||
return literal == place ? null : literal;
|
||||
});
|
||||
return ContainerUtil.filter(map.get(stringToFind), literal -> literal != place);
|
||||
}
|
||||
|
||||
private boolean shouldCheck(@NotNull PsiLiteralExpression expression) {
|
||||
|
||||
@@ -128,11 +128,18 @@ public class PyJavaClassType implements PyClassLikeType {
|
||||
return myDefinition;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PyClassLikeType toInstance() {
|
||||
return myDefinition ? new PyJavaClassType(myClass, false) : this;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PyClassLikeType toClass() {
|
||||
return myDefinition ? this : new PyJavaClassType(myClass, true);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getClassQName() {
|
||||
|
||||
@@ -624,7 +624,7 @@ public class PyPsiUtils {
|
||||
Preconditions.checkArgument(!module.isDisposed(), String.format("Module %s is disposed", module));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Nullable
|
||||
public static PsiFileSystemItem getFileSystemItem(@NotNull PsiElement element) {
|
||||
if (element instanceof PsiFileSystemItem) {
|
||||
return (PsiFileSystemItem)element;
|
||||
|
||||
@@ -31,10 +31,7 @@ import java.util.Set;
|
||||
/**
|
||||
* @author vlan
|
||||
*/
|
||||
public interface PyClassLikeType extends PyCallableType, PyWithAncestors {
|
||||
boolean isDefinition();
|
||||
|
||||
PyClassLikeType toInstance();
|
||||
public interface PyClassLikeType extends PyCallableType, PyWithAncestors, PyInstantiableType<PyClassLikeType> {
|
||||
|
||||
@Nullable
|
||||
String getClassQName();
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.jetbrains.python.psi.types;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* A type that can describe either particular instantiated instance of a class or a class itself.
|
||||
*
|
||||
* @author Mikhail Golubev
|
||||
*/
|
||||
public interface PyInstantiableType<T extends PyInstantiableType<T>> extends PyType {
|
||||
boolean isDefinition();
|
||||
|
||||
@NotNull
|
||||
T toInstance();
|
||||
|
||||
@NotNull
|
||||
T toClass();
|
||||
}
|
||||
@@ -66,6 +66,7 @@ public interface PyType {
|
||||
Key<Set<String>> CTX_NAMES = new Key<>("Completion variants names");
|
||||
|
||||
/**
|
||||
* TODO rename it to something like getPresentableName(), because it's not clear that these names are actually visible to end-user
|
||||
* @return name of the type
|
||||
*/
|
||||
@Nullable
|
||||
|
||||
@@ -84,14 +84,23 @@ public class PyCustomType implements PyClassLikeType {
|
||||
return !myInstanceType;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public final PyClassLikeType toInstance() {
|
||||
return myInstanceType
|
||||
? this
|
||||
: new PyCustomType(myQualifiedName, myFilter, true, myTypesToMimic.toArray(new PyClassLikeType[myTypesToMimic.size()]));
|
||||
: new PyCustomType(myQualifiedName, myFilter, true, myTypesToMimic.toArray(new PyClassLikeType[0]));
|
||||
}
|
||||
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PyClassLikeType toClass() {
|
||||
return myInstanceType
|
||||
? new PyCustomType(myQualifiedName, myFilter, false, myTypesToMimic.toArray(new PyClassLikeType[0]))
|
||||
: this;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public final String getClassQName() {
|
||||
|
||||
+6
-2
@@ -68,7 +68,11 @@ public class PyTypeAssertionEvaluator extends PyRecursiveElementVisitor {
|
||||
final PyReferenceExpression target = (PyReferenceExpression)args[0];
|
||||
final PyExpression typeElement = args[1];
|
||||
|
||||
pushAssertion(target, myPositive, context -> context.getType(typeElement));
|
||||
// TODO support tuple of types as the second argument of isinstance()
|
||||
pushAssertion(target, myPositive, context -> {
|
||||
final PyType type = context.getType(typeElement);
|
||||
return type instanceof PyClassType ? ((PyClassType)type).toInstance() : type;
|
||||
});
|
||||
}
|
||||
}
|
||||
else if (node.isCalleeText(PyNames.CALLABLE_BUILTIN)) {
|
||||
@@ -183,7 +187,7 @@ public class PyTypeAssertionEvaluator extends PyRecursiveElementVisitor {
|
||||
final PyType initial = context.getType(target);
|
||||
final PyType suggested = suggestedType.apply(context);
|
||||
|
||||
if (!PyUnionType.class.isInstance(initial) &&
|
||||
if (!(initial instanceof PyUnionType) &&
|
||||
!PyTypeChecker.isUnknown(initial) &&
|
||||
PyTypeChecker.match(suggested, initial, context)) {
|
||||
return initial;
|
||||
|
||||
@@ -103,11 +103,18 @@ public class PyNamedTupleType extends PyClassTypeImpl implements PyCallableType
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PyClassType toInstance() {
|
||||
return myDefinitionLevel == 1 ? new PyNamedTupleType(myClass, myDeclaration, myName, myFields, 0) : this;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PyClassLikeType toClass() {
|
||||
return myDefinitionLevel == 0 ? this : new PyNamedTupleType(myClass, myDeclaration, myName, myFields, 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PyNamedTupleType: " + myName;
|
||||
|
||||
@@ -36,6 +36,7 @@ import com.jetbrains.python.codeInsight.functionTypeComments.psi.PyFunctionTypeA
|
||||
import com.jetbrains.python.codeInsight.functionTypeComments.psi.PyFunctionTypeAnnotationFile;
|
||||
import com.jetbrains.python.codeInsight.functionTypeComments.psi.PyParameterTypeList;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.psi.impl.PyBuiltinCache;
|
||||
import com.jetbrains.python.psi.impl.PyExpressionCodeFragmentImpl;
|
||||
import com.jetbrains.python.psi.resolve.PyResolveContext;
|
||||
import com.jetbrains.python.psi.resolve.PyResolveImportUtil;
|
||||
@@ -62,6 +63,8 @@ public class PyTypingTypeProvider extends PyTypeProviderBase {
|
||||
public static final String COROUTINE = "typing.Coroutine";
|
||||
public static final String NAMEDTUPLE = "typing.NamedTuple";
|
||||
public static final String GENERIC = "typing.Generic";
|
||||
public static final String TYPE = "typing.Type";
|
||||
public static final String ANY = "typing.Any";
|
||||
|
||||
public static final Pattern TYPE_COMMENT_PATTERN = Pattern.compile("# *type: *(.*)");
|
||||
|
||||
@@ -470,6 +473,10 @@ public class PyTypingTypeProvider extends PyTypeProviderBase {
|
||||
if (callableType != null) {
|
||||
return Ref.create(callableType);
|
||||
}
|
||||
final Ref<PyType> classObjType = getClassObjectType(resolved, context);
|
||||
if (classObjType != null) {
|
||||
return classObjType;
|
||||
}
|
||||
final PyType parameterizedType = getParameterizedType(resolved, context);
|
||||
if (parameterizedType != null) {
|
||||
return Ref.create(parameterizedType);
|
||||
@@ -502,14 +509,49 @@ public class PyTypingTypeProvider extends PyTypeProviderBase {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static Ref<PyType> getAnyType(@NotNull PsiElement element) {
|
||||
final PyQualifiedNameOwner qualifiedNameOwner = as(element, PyQualifiedNameOwner.class);
|
||||
if (qualifiedNameOwner != null && "typing.Any".equals(qualifiedNameOwner.getQualifiedName())) {
|
||||
return Ref.create();
|
||||
private static Ref<PyType> getClassObjectType(@NotNull PsiElement resolved, @NotNull Context context) {
|
||||
if (resolved instanceof PySubscriptionExpression) {
|
||||
final PySubscriptionExpression subsExpr = (PySubscriptionExpression)resolved;
|
||||
final PyExpression operand = subsExpr.getOperand();
|
||||
final Collection<String> operandNames = resolveToQualifiedNames(operand, context.getTypeContext());
|
||||
if (operandNames.contains(TYPE)) {
|
||||
final PyExpression indexExpr = subsExpr.getIndexExpression();
|
||||
if (indexExpr != null) {
|
||||
if (resolveToQualifiedNames(indexExpr, context.getTypeContext()).contains(ANY)) {
|
||||
return Ref.create(PyBuiltinCache.getInstance(resolved).getTypeType());
|
||||
}
|
||||
final PyType type = Ref.deref(getType(indexExpr, context));
|
||||
final PyClassType classType = as(type, PyClassType.class);
|
||||
if (classType != null && !classType.isDefinition()) {
|
||||
return Ref.create(new PyClassTypeImpl(classType.getPyClass(), true));
|
||||
}
|
||||
final PyGenericType typeVar = as(type, PyGenericType.class);
|
||||
if (typeVar != null && !typeVar.isDefinition()) {
|
||||
return Ref.create(new PyGenericType(typeVar.getName(), typeVar.getBound(), true));
|
||||
}
|
||||
// Represent Type[Union[str, int]] internally as Union[Type[str], Type[int]]
|
||||
final PyUnionType unionType = as(type, PyUnionType.class);
|
||||
if (unionType != null &&
|
||||
unionType.getMembers().stream().allMatch(t -> t instanceof PyClassType && !((PyClassType)t).isDefinition())) {
|
||||
return Ref.create(PyUnionType.union(ContainerUtil.map(unionType.getMembers(), t -> ((PyClassType)t).toClass())));
|
||||
}
|
||||
}
|
||||
// Map Type[Something] with unsupported type parameter to Any, instead of generic type for the class "type"
|
||||
return Ref.create();
|
||||
}
|
||||
}
|
||||
// Replace plain non-parametrized Type with its builtin counterpart
|
||||
else if (TYPE.equals(getQualifiedName(resolved))) {
|
||||
return Ref.create(PyBuiltinCache.getInstance(resolved).getTypeType());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static Ref<PyType> getAnyType(@NotNull PsiElement element) {
|
||||
return ANY.equals(getQualifiedName(element)) ? Ref.create() : null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static Ref<PyType> getClassType(@NotNull PsiElement element, @NotNull TypeEvalContext context) {
|
||||
if (element instanceof PyTypedElement) {
|
||||
|
||||
@@ -17,10 +17,12 @@ package com.jetbrains.python.documentation;
|
||||
|
||||
import com.google.common.collect.Collections2;
|
||||
import com.google.common.collect.Maps;
|
||||
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.codeInsight.typing.PyTypingTypeProvider;
|
||||
import com.jetbrains.python.psi.impl.PyBuiltinCache;
|
||||
import com.jetbrains.python.psi.types.*;
|
||||
import com.jetbrains.python.toolbox.ChainIterable;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -186,6 +188,34 @@ public class PyTypeModelBuilder {
|
||||
visitor.param(this);
|
||||
}
|
||||
}
|
||||
|
||||
static class ClassObjectType extends TypeModel {
|
||||
private final TypeModel classType;
|
||||
|
||||
public ClassObjectType(TypeModel classType) {
|
||||
this.classType = classType;
|
||||
}
|
||||
|
||||
@Override
|
||||
void accept(@NotNull TypeVisitor visitor) {
|
||||
visitor.classObject(this);
|
||||
}
|
||||
}
|
||||
|
||||
static class GenericType extends TypeModel {
|
||||
private final String name;
|
||||
private final List<TypeModel> bounds;
|
||||
|
||||
public GenericType(@Nullable String name, @NotNull List<TypeModel> bounds) {
|
||||
this.name = name;
|
||||
this.bounds = bounds;
|
||||
}
|
||||
|
||||
@Override
|
||||
void accept(@NotNull TypeVisitor visitor) {
|
||||
visitor.genericType(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds tree-like type model for PyType
|
||||
@@ -238,18 +268,49 @@ public class PyTypeModelBuilder {
|
||||
}
|
||||
else if (type instanceof PyUnionType && allowUnions) {
|
||||
final PyUnionType unionType = (PyUnionType)type;
|
||||
if (type instanceof PyDynamicallyEvaluatedType || PyTypeChecker.isUnknown(type)) {
|
||||
final Collection<PyType> unionMembers = unionType.getMembers();
|
||||
final Ref<PyType> optionalType = getOptionalType(unionType);
|
||||
if (optionalType != null) {
|
||||
result = new OptionalType(build(optionalType.get(), true));
|
||||
}
|
||||
else if (type instanceof PyDynamicallyEvaluatedType || PyTypeChecker.isUnknown(type, false)) {
|
||||
result = new UnknownType(build(unionType.excludeNull(myContext), true));
|
||||
}
|
||||
else if (unionMembers.stream().allMatch(t -> t instanceof PyClassType && ((PyClassType)t).isDefinition())) {
|
||||
final List<TypeModel> instanceTypes = ContainerUtil.map(unionMembers, t -> build(((PyClassType)t).toInstance(), allowUnions));
|
||||
result = new ClassObjectType(new OneOf(instanceTypes));
|
||||
}
|
||||
else {
|
||||
result = Optional
|
||||
.ofNullable(getOptionalType(unionType))
|
||||
.<PyTypeModelBuilder.TypeModel>map(optionalType -> new OptionalType(build(optionalType, true)))
|
||||
.orElseGet(() -> new OneOf(Collections2.transform(unionType.getMembers(), t -> build(t, false))));
|
||||
result = new OneOf(Collections2.transform(unionMembers, t -> build(t, false)));
|
||||
}
|
||||
}
|
||||
else if (type instanceof PyCallableType && !(type instanceof PyClassLikeType)) {
|
||||
result = build((PyCallableType)type);
|
||||
result = buildCallable((PyCallableType)type);
|
||||
}
|
||||
else if (type instanceof PyInstantiableType && ((PyInstantiableType)type).isDefinition()) {
|
||||
final PyInstantiableType instanceType = ((PyInstantiableType)type).toInstance();
|
||||
// Special case: render Type[type] as just type
|
||||
if (type instanceof PyClassType && instanceType.equals(PyBuiltinCache.getInstance(((PyClassType)type).getPyClass()).getTypeType())) {
|
||||
result = NamedType.nameOrAny(type);
|
||||
}
|
||||
else {
|
||||
result = new ClassObjectType(build(instanceType, allowUnions));
|
||||
}
|
||||
}
|
||||
else if (type instanceof PyGenericType) {
|
||||
//assert !((PyGenericType)type).isDefinition()
|
||||
final PyType bound = ((PyGenericType)type).getBound();
|
||||
final List<TypeModel> boundNames;
|
||||
if (bound instanceof PyUnionType) {
|
||||
boundNames = ContainerUtil.map(((PyUnionType)bound).getMembers(), t -> build(t, allowUnions));
|
||||
}
|
||||
else if (bound != null) {
|
||||
boundNames = Collections.singletonList(build(bound, allowUnions));
|
||||
}
|
||||
else {
|
||||
boundNames = Collections.emptyList();
|
||||
}
|
||||
result = new GenericType(type.getName(), boundNames);
|
||||
}
|
||||
if (result == null) {
|
||||
result = NamedType.nameOrAny(type);
|
||||
@@ -259,7 +320,7 @@ public class PyTypeModelBuilder {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PyType getOptionalType(@NotNull PyUnionType type) {
|
||||
private static Ref<PyType> getOptionalType(@NotNull PyUnionType type) {
|
||||
final Collection<PyType> members = type.getMembers();
|
||||
if (members.size() == 2) {
|
||||
boolean foundNone = false;
|
||||
@@ -273,13 +334,13 @@ public class PyTypeModelBuilder {
|
||||
}
|
||||
}
|
||||
if (foundNone) {
|
||||
return optional;
|
||||
return Ref.create(optional);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private TypeModel build(@NotNull PyCallableType type) {
|
||||
private TypeModel buildCallable(@NotNull PyCallableType type) {
|
||||
List<TypeModel> parameterModels = null;
|
||||
final List<PyCallableParameter> parameters = type.getParameters(myContext);
|
||||
if (parameters != null) {
|
||||
@@ -309,6 +370,10 @@ public class PyTypeModelBuilder {
|
||||
void optional(OptionalType type);
|
||||
|
||||
void tuple(TupleType type);
|
||||
|
||||
void classObject(ClassObjectType type);
|
||||
|
||||
void genericType(GenericType type);
|
||||
}
|
||||
|
||||
private static class TypeToStringVisitor extends TypeNameVisitor {
|
||||
@@ -503,5 +568,31 @@ public class PyTypeModelBuilder {
|
||||
}
|
||||
add("]");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void classObject(ClassObjectType type) {
|
||||
add("Type[");
|
||||
type.classType.accept(this);
|
||||
add("]");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void genericType(GenericType type) {
|
||||
add("TypeVar('");
|
||||
add(type.name);
|
||||
add("'");
|
||||
if (!type.bounds.isEmpty()) {
|
||||
add(", ");
|
||||
boolean first = true;
|
||||
for (TypeModel bound : type.bounds) {
|
||||
if (!first) {
|
||||
add(", ");
|
||||
}
|
||||
bound.accept(this);
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
add(")");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,18 +78,23 @@ public class SetupTaskChooserAction extends AnAction {
|
||||
}
|
||||
|
||||
public static void runSetupTask(String taskName, Module module) {
|
||||
final PyFile setupPy = PyPackageUtil.findSetupPy(module);
|
||||
try {
|
||||
final List<SetupTask.Option> options = SetupTaskIntrospector.getSetupTaskOptions(module, taskName);
|
||||
List<String> parameters = new ArrayList<>();
|
||||
parameters.add(taskName);
|
||||
if (options != null) {
|
||||
SetupTaskDialog dialog = new SetupTaskDialog(module.getProject(), taskName, options);
|
||||
if (!dialog.showAndGet()) {
|
||||
return;
|
||||
}
|
||||
parameters.addAll(dialog.getCommandLine());
|
||||
final List<SetupTask.Option> options = SetupTaskIntrospector.getSetupTaskOptions(module, taskName);
|
||||
List<String> parameters = new ArrayList<>();
|
||||
parameters.add(taskName);
|
||||
if (options != null) {
|
||||
SetupTaskDialog dialog = new SetupTaskDialog(module.getProject(), taskName, options);
|
||||
if (!dialog.showAndGet()) {
|
||||
return;
|
||||
}
|
||||
parameters.addAll(dialog.getCommandLine());
|
||||
}
|
||||
runSetupTask(taskName, module, parameters);
|
||||
}
|
||||
|
||||
public static void runSetupTask(String taskName, Module module, List<String> parameters) {
|
||||
try {
|
||||
final PyFile setupPy = PyPackageUtil.findSetupPy(module);
|
||||
if (setupPy == null) return;
|
||||
final PythonTask task = new PythonTask(module, taskName);
|
||||
final VirtualFile virtualFile = setupPy.getVirtualFile();
|
||||
task.setRunnerScript(virtualFile.getPath());
|
||||
|
||||
@@ -846,20 +846,13 @@ public class PyUtil {
|
||||
});
|
||||
}
|
||||
|
||||
public static <T, P> T getParameterizedCachedValue(@NotNull PsiElement element, @NotNull P param, @NotNull NotNullFunction<P, T> f) {
|
||||
final Map<P, T> cache = CachedValuesManager.getCachedValue(element, new CachedValueProvider<Map<P, T>>() {
|
||||
@Nullable
|
||||
@Override
|
||||
public Result<Map<P, T>> compute() {
|
||||
return Result.create(Maps.newHashMap(), PsiModificationTracker.MODIFICATION_COUNT);
|
||||
}
|
||||
public static <T, P> T getParameterizedCachedValue(@NotNull PsiElement element, @Nullable P param, @NotNull NullableFunction<P, T> f) {
|
||||
final CachedValuesManager manager = CachedValuesManager.getManager(element.getProject());
|
||||
final Map<Optional<P>, Optional<T>> cache = CachedValuesManager.getCachedValue(element, manager.getKeyForClass(f.getClass()), () -> {
|
||||
// concurrent hash map is a null-hostile collection
|
||||
return CachedValueProvider.Result.create(Maps.newConcurrentMap(), PsiModificationTracker.MODIFICATION_COUNT);
|
||||
});
|
||||
T result = cache.get(param);
|
||||
if (result == null) {
|
||||
result = f.fun(param);
|
||||
cache.put(param, result);
|
||||
}
|
||||
return result;
|
||||
return cache.computeIfAbsent(Optional.ofNullable(param), p -> Optional.ofNullable(f.fun(param))).orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -29,6 +29,7 @@ import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.vfs.VfsUtilCore;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.scope.DelegatingScopeProcessor;
|
||||
import com.intellij.psi.scope.PsiScopeProcessor;
|
||||
import com.intellij.psi.stubs.StubElement;
|
||||
import com.intellij.psi.util.PsiModificationTracker;
|
||||
@@ -298,25 +299,15 @@ public class PyFileImpl extends PsiFileBase implements PyFile, PyExpression {
|
||||
@NotNull PsiElement place) {
|
||||
final List<String> dunderAll = getDunderAll();
|
||||
final List<String> remainingDunderAll = dunderAll == null ? null : new ArrayList<>(dunderAll);
|
||||
PsiScopeProcessor wrapper = new PsiScopeProcessor() {
|
||||
PsiScopeProcessor wrapper = new DelegatingScopeProcessor(processor) {
|
||||
@Override
|
||||
public boolean execute(@NotNull PsiElement element, @NotNull ResolveState state) {
|
||||
if (!processor.execute(element, state)) return false;
|
||||
if (!super.execute(element, state)) return false;
|
||||
if (remainingDunderAll != null && element instanceof PyElement) {
|
||||
remainingDunderAll.remove(((PyElement)element).getName());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T getHint(@NotNull Key<T> hintKey) {
|
||||
return processor.getHint(hintKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleEvent(@NotNull Event event, @Nullable Object associated) {
|
||||
processor.handleEvent(event, associated);
|
||||
}
|
||||
};
|
||||
|
||||
Set<PyFile> pyFiles = resolveState.get(PROCESSED_FILES);
|
||||
|
||||
@@ -17,11 +17,12 @@ package com.jetbrains.python.psi.impl;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.scope.DelegatingScopeProcessor;
|
||||
import com.intellij.psi.scope.PsiScopeProcessor;
|
||||
import com.intellij.psi.stubs.IStubElementType;
|
||||
import com.intellij.psi.util.QualifiedName;
|
||||
import com.intellij.util.ArrayFactory;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.jetbrains.python.PyElementTypes;
|
||||
@@ -161,7 +162,17 @@ public class PyFromImportStatementImpl extends PyBaseElementImpl<PyFromImportSta
|
||||
for (PsiElement target : targets) {
|
||||
final PsiElement importedFile = PyUtil.turnDirIntoInit(target);
|
||||
if (importedFile != null) {
|
||||
if (!importedFile.processDeclarations(processor, state, null, place)) {
|
||||
PsiScopeProcessor starImportableNamesProcessor = new DelegatingScopeProcessor(processor) {
|
||||
@Override
|
||||
public boolean execute(@NotNull PsiElement element, @NotNull ResolveState state) {
|
||||
if (element instanceof PyElement && importedFile instanceof PyFile &&
|
||||
!PyUtil.isStarImportableFrom(StringUtil.notNullize(((PyElement)element).getName()), (PyFile)importedFile)) {
|
||||
return true;
|
||||
}
|
||||
return super.execute(element, state);
|
||||
}
|
||||
};
|
||||
if (!importedFile.processDeclarations(starImportableNamesProcessor, state, null, place)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,6 +37,8 @@ import javax.swing.*;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static com.jetbrains.python.psi.PyUtil.as;
|
||||
|
||||
/**
|
||||
* @author dcheryasov
|
||||
*/
|
||||
@@ -93,21 +95,13 @@ public class PyStarImportElementImpl extends PyBaseElementImpl<PyStarImportEleme
|
||||
PyFromImportStatement fromImportStatement = (PyFromImportStatement)parent;
|
||||
final List<PsiElement> importedFiles = fromImportStatement.resolveImportSourceCandidates();
|
||||
for (PsiElement importedFile : new HashSet<>(importedFiles)) { // resolver gives lots of duplicates
|
||||
final PsiElement source = PyUtil.turnDirIntoInit(importedFile);
|
||||
if (source instanceof PyFile) {
|
||||
PyFile sourceFile = (PyFile)source;
|
||||
final PyFile sourceFile = as(PyUtil.turnDirIntoInit(importedFile), PyFile.class);
|
||||
if (sourceFile != null && PyUtil.isStarImportableFrom(name, sourceFile)) {
|
||||
final PyModuleType moduleType = new PyModuleType(sourceFile);
|
||||
final List<? extends RatedResolveResult> results = moduleType.resolveMember(name, null, AccessDirection.READ,
|
||||
PyResolveContext.defaultContext());
|
||||
if (results != null && !results.isEmpty() && PyUtil.isStarImportableFrom(name, sourceFile)) {
|
||||
if (results.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
final List<RatedResolveResult> res = Lists.newArrayList();
|
||||
for (RatedResolveResult result : results) {
|
||||
res.add(result);
|
||||
}
|
||||
return res;
|
||||
if (results != null && !results.isEmpty()) {
|
||||
return Lists.newArrayList(results);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.jetbrains.python.psi.resolve;
|
||||
import com.intellij.openapi.extensions.Extensions;
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.projectRoots.Sdk;
|
||||
import com.intellij.openapi.util.Couple;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.VfsUtilCore;
|
||||
@@ -27,11 +28,9 @@ import com.intellij.psi.util.QualifiedName;
|
||||
import com.jetbrains.python.PyNames;
|
||||
import com.jetbrains.python.codeInsight.controlflow.ScopeOwner;
|
||||
import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil;
|
||||
import com.jetbrains.python.psi.PyClass;
|
||||
import com.jetbrains.python.psi.PyElement;
|
||||
import com.jetbrains.python.psi.PyFile;
|
||||
import com.jetbrains.python.psi.PyFunction;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.psi.impl.PyBuiltinCache;
|
||||
import com.jetbrains.python.psi.impl.PyPsiUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -114,7 +113,15 @@ public class QualifiedNameFinder {
|
||||
*/
|
||||
@Nullable
|
||||
public static QualifiedName findCanonicalImportPath(@NotNull PsiElement symbol, @Nullable PsiElement foothold) {
|
||||
PsiFileSystemItem srcfile = symbol instanceof PsiFileSystemItem ? (PsiFileSystemItem)symbol : symbol.getContainingFile();
|
||||
return PyUtil.getParameterizedCachedValue(symbol, Couple.of(symbol, foothold), QualifiedNameFinder::doFindCanonicalImportPath);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static QualifiedName doFindCanonicalImportPath(@NotNull Couple<PsiElement> param) {
|
||||
final PsiElement symbol = param.getFirst();
|
||||
final PsiElement foothold = param.getSecond();
|
||||
|
||||
final PsiFileSystemItem srcfile = PyPsiUtils.getFileSystemItem(symbol);
|
||||
if (srcfile == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -107,11 +107,18 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType {
|
||||
return myIsDefinition;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PyClassType toInstance() {
|
||||
return myIsDefinition ? withUserDataCopy(new PyClassTypeImpl(myClass, false)) : this;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PyClassLikeType toClass() {
|
||||
return myIsDefinition ? this : new PyClassTypeImpl(myClass, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap new instance to copy user data to it
|
||||
*/
|
||||
|
||||
@@ -70,11 +70,18 @@ public class PyCollectionTypeImpl extends PyClassTypeImpl implements PyCollectio
|
||||
return new PyCollectionTypeImpl(pyClass, isDefinition, elementTypes);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PyClassType toInstance() {
|
||||
return myIsDefinition ? withUserDataCopy(new PyCollectionTypeImpl(myClass, false, myElementTypes)) : this;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PyClassLikeType toClass() {
|
||||
return myIsDefinition ? this : withUserDataCopy(new PyCollectionTypeImpl(myClass, true, myElementTypes));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
|
||||
@@ -15,11 +15,9 @@
|
||||
*/
|
||||
package com.jetbrains.python.psi.types;
|
||||
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.util.Function;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.ProcessingContext;
|
||||
import com.jetbrains.python.PyNames;
|
||||
import com.jetbrains.python.psi.AccessDirection;
|
||||
import com.jetbrains.python.psi.PyExpression;
|
||||
import com.jetbrains.python.psi.resolve.PyResolveContext;
|
||||
@@ -32,13 +30,19 @@ import java.util.List;
|
||||
/**
|
||||
* @author vlan
|
||||
*/
|
||||
public class PyGenericType implements PyType {
|
||||
public class PyGenericType implements PyType, PyInstantiableType<PyGenericType> {
|
||||
@NotNull private final String myName;
|
||||
@Nullable private PyType myBound;
|
||||
@Nullable private final PyType myBound;
|
||||
private boolean myIsDefinition = false;
|
||||
|
||||
public PyGenericType(@NotNull String name, @Nullable PyType bound) {
|
||||
this(name, bound, false);
|
||||
}
|
||||
|
||||
public PyGenericType(@NotNull String name, @Nullable PyType bound, boolean isDefinition) {
|
||||
myName = name;
|
||||
myBound = bound;
|
||||
myIsDefinition = isDefinition;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -52,20 +56,13 @@ public class PyGenericType implements PyType {
|
||||
|
||||
@Override
|
||||
public Object[] getCompletionVariants(String completionPrefix, PsiElement location, ProcessingContext context) {
|
||||
return new Object[0];
|
||||
return ArrayUtil.EMPTY_OBJECT_ARRAY;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getName() {
|
||||
if (myBound instanceof PyUnionType) {
|
||||
final PyUnionType bounds = (PyUnionType)myBound;
|
||||
final String boundsString = StringUtil.join(bounds.getMembers(), type -> type != null ? type.getName() : PyNames.UNKNOWN_TYPE, ", ");
|
||||
return "TypeVar('" + myName + "', " + boundsString + ")";
|
||||
}
|
||||
else {
|
||||
return "TypeVar('" + myName + "')";
|
||||
}
|
||||
return myName;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -86,7 +83,7 @@ public class PyGenericType implements PyType {
|
||||
return false;
|
||||
}
|
||||
final PyGenericType type = (PyGenericType)o;
|
||||
return myName.equals(type.myName);
|
||||
return myName.equals(type.myName) && myIsDefinition == type.isDefinition();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -104,4 +101,21 @@ public class PyGenericType implements PyType {
|
||||
public PyType getBound() {
|
||||
return myBound;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDefinition() {
|
||||
return myIsDefinition;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PyGenericType toInstance() {
|
||||
return myIsDefinition ? new PyGenericType(myName, myBound, false) : this;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PyGenericType toClass() {
|
||||
return myIsDefinition ? this : new PyGenericType(myName, myBound, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,18 +65,38 @@ public class PyTypeChecker {
|
||||
private static boolean match(@Nullable PyType expected, @Nullable PyType actual, @NotNull TypeEvalContext context,
|
||||
@Nullable Map<PyGenericType, PyType> substitutions, boolean recursive) {
|
||||
// TODO: subscriptable types?, module types?, etc.
|
||||
if (actual instanceof PyClassType) {
|
||||
final PyClassType classType = (PyClassType)actual;
|
||||
final PyBuiltinCache builtinCache = PyBuiltinCache.getInstance(classType.getPyClass());
|
||||
|
||||
if (actual == builtinCache.getObjectType(PyNames.BASESTRING)) {
|
||||
final PyClassType expectedClassType = as(expected, PyClassType.class);
|
||||
final PyClassType actualClassType = as(actual, PyClassType.class);
|
||||
|
||||
// Special cases: object and type
|
||||
if (expectedClassType != null && ArrayUtil.contains(expectedClassType.getName(), PyNames.OBJECT, PyNames.TYPE)) {
|
||||
final PyBuiltinCache builtinCache = PyBuiltinCache.getInstance(expectedClassType.getPyClass());
|
||||
if (expectedClassType.equals(builtinCache.getObjectType())) {
|
||||
return true;
|
||||
}
|
||||
if (expectedClassType.equals(builtinCache.getTypeType()) &&
|
||||
actual instanceof PyInstantiableType && ((PyInstantiableType)actual).isDefinition()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (expected instanceof PyInstantiableType && actual instanceof PyInstantiableType
|
||||
&& ((PyInstantiableType)expected).isDefinition() ^ ((PyInstantiableType)actual).isDefinition()) {
|
||||
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);
|
||||
final PyType bound = generic.getBound();
|
||||
PyType bound = generic.getBound();
|
||||
// Promote int in Type[TypeVar('T', int)] to Type[int] before checking that bounds match
|
||||
if (generic.isDefinition() && bound instanceof PyInstantiableType) {
|
||||
bound = ((PyInstantiableType)bound).toClass();
|
||||
}
|
||||
if (!match(bound, actual, context, substitutions, recursive)) {
|
||||
return false;
|
||||
}
|
||||
@@ -102,12 +122,6 @@ public class PyTypeChecker {
|
||||
if (expected == null || actual == null) {
|
||||
return true;
|
||||
}
|
||||
if (expected instanceof PyClassType) {
|
||||
final PyClass c = ((PyClassType)expected).getPyClass();
|
||||
if ("object".equals(c.getName())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (isUnknown(actual)) {
|
||||
return true;
|
||||
}
|
||||
@@ -142,9 +156,9 @@ public class PyTypeChecker {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (expected instanceof PyClassType && actual instanceof PyClassType) {
|
||||
final PyClass superClass = ((PyClassType)expected).getPyClass();
|
||||
final PyClass subClass = ((PyClassType)actual).getPyClass();
|
||||
if (expectedClassType != null && actualClassType != null) {
|
||||
final PyClass superClass = expectedClassType.getPyClass();
|
||||
final PyClass subClass = actualClassType.getPyClass();
|
||||
if (expected instanceof PyTupleType && actual instanceof PyTupleType) {
|
||||
final PyTupleType superTupleType = (PyTupleType)expected;
|
||||
final PyTupleType subTupleType = (PyTupleType)actual;
|
||||
@@ -213,15 +227,15 @@ public class PyTypeChecker {
|
||||
else if (matchClasses(superClass, subClass, context)) {
|
||||
return true;
|
||||
}
|
||||
else if (((PyClassType)actual).isDefinition() && PyNames.CALLABLE.equals(expected.getName())) {
|
||||
else if (actualClassType.isDefinition() && PyNames.CALLABLE.equals(expected.getName())) {
|
||||
return true;
|
||||
}
|
||||
if (expected.equals(actual)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (actual instanceof PyFunctionTypeImpl && expected instanceof PyClassType) {
|
||||
final PyClass superClass = ((PyClassType)expected).getPyClass();
|
||||
if (actual instanceof PyFunctionTypeImpl && expectedClassType != null) {
|
||||
final PyClass superClass = expectedClassType.getPyClass();
|
||||
if (PyNames.CALLABLE.equals(superClass.getName())) {
|
||||
return true;
|
||||
}
|
||||
@@ -237,16 +251,15 @@ public class PyTypeChecker {
|
||||
}
|
||||
return expectedStructural.getAttributeNames().containsAll(actualStructural.getAttributeNames());
|
||||
}
|
||||
if (expected instanceof PyStructuralType && actual instanceof PyClassType) {
|
||||
final PyClassType actualClassType = (PyClassType)actual;
|
||||
if (expected instanceof PyStructuralType && actualClassType != null) {
|
||||
if (overridesGetAttr(actualClassType.getPyClass(), context)) {
|
||||
return true;
|
||||
}
|
||||
final Set<String> actualAttributes = actualClassType.getMemberNames(true, context);
|
||||
return actualAttributes.containsAll(((PyStructuralType)expected).getAttributeNames());
|
||||
}
|
||||
if (actual instanceof PyStructuralType && expected instanceof PyClassType) {
|
||||
final Set<String> expectedAttributes = ((PyClassType)expected).getMemberNames(true, context);
|
||||
if (actual instanceof PyStructuralType && expectedClassType != null) {
|
||||
final Set<String> expectedAttributes = expectedClassType.getMemberNames(true, context);
|
||||
return expectedAttributes.containsAll(((PyStructuralType)actual).getAttributeNames());
|
||||
}
|
||||
if (actual instanceof PyCallableType && expected instanceof PyCallableType) {
|
||||
@@ -338,13 +351,17 @@ public class PyTypeChecker {
|
||||
}
|
||||
|
||||
public static boolean isUnknown(@Nullable PyType type) {
|
||||
if (type == null || type instanceof PyGenericType) {
|
||||
return isUnknown(type, true);
|
||||
}
|
||||
|
||||
public static boolean isUnknown(@Nullable PyType type, boolean genericsAreUnknown) {
|
||||
if (type == null || (genericsAreUnknown && type instanceof PyGenericType)) {
|
||||
return true;
|
||||
}
|
||||
if (type instanceof PyUnionType) {
|
||||
final PyUnionType union = (PyUnionType)type;
|
||||
for (PyType t : union.getMembers()) {
|
||||
if (isUnknown(t)) {
|
||||
if (isUnknown(t, genericsAreUnknown)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -416,7 +433,22 @@ public class PyTypeChecker {
|
||||
@NotNull TypeEvalContext context) {
|
||||
if (hasGenerics(type, context)) {
|
||||
if (type instanceof PyGenericType) {
|
||||
final PyType substitution = substitutions.get((PyGenericType)type);
|
||||
final PyGenericType typeVar = (PyGenericType)type;
|
||||
PyType substitution = substitutions.get(typeVar);
|
||||
if (substitution == null) {
|
||||
if (!typeVar.isDefinition()) {
|
||||
final PyInstantiableType<?> classType = as(substitutions.get(typeVar.toClass()), PyInstantiableType.class);
|
||||
if (classType != null) {
|
||||
substitution = classType.toInstance();
|
||||
}
|
||||
}
|
||||
else {
|
||||
final PyInstantiableType<?> instanceType = as(substitutions.get(typeVar.toInstance()), PyInstantiableType.class);
|
||||
if (instanceType != null) {
|
||||
substitution = instanceType.toClass();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (substitution instanceof PyGenericType && substitution != type) {
|
||||
final PyType recursive = substitute(substitution, substitutions, context);
|
||||
if (recursive != null) {
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
from pkg import <caret>
|
||||
@@ -0,0 +1,2 @@
|
||||
from .mod1 import *
|
||||
from .mod2 import *
|
||||
@@ -0,0 +1,2 @@
|
||||
foo = 1
|
||||
_foo = 2
|
||||
@@ -0,0 +1,4 @@
|
||||
__all__ = ['_bar']
|
||||
|
||||
bar = 1
|
||||
_bar = 2
|
||||
@@ -0,0 +1,36 @@
|
||||
from typing import Type, TypeVar, Optional
|
||||
|
||||
class MyClass:
|
||||
pass
|
||||
|
||||
def expects_myclass(x: Type[MyClass]):
|
||||
pass
|
||||
|
||||
expects_myclass(<warning descr="Expected type 'Type[MyClass]', got 'MyClass' instead">MyClass()</warning>)
|
||||
expects_class(MyClass)
|
||||
|
||||
T1 = TypeVar('T1')
|
||||
def expects_any_class(x: Type[T1]):
|
||||
pass
|
||||
|
||||
expects_any_class(MyClass)
|
||||
expects_any_class(<warning descr="Expected type 'Type[TypeVar('T1')]', got 'MyClass' instead">MyClass()</warning>)
|
||||
expects_any_class(object)
|
||||
expects_any_class(<warning descr="Expected type 'Type[TypeVar('T1')]', got 'object' instead">object()</warning>)
|
||||
|
||||
T2 = TypeVar('T2', MyClass)
|
||||
def expects_myclass_descendant(x: Type[T2]):
|
||||
pass
|
||||
|
||||
expects_myclass_descendant(MyClass)
|
||||
expects_myclass_descendant(<warning descr="Expected type 'Type[TypeVar('T2', MyClass)]', got 'MyClass' instead">MyClass()</warning>)
|
||||
expects_myclass_descendant(<warning descr="Expected type 'Type[TypeVar('T2', MyClass)]', got 'Type[object]' instead">object</warning>)
|
||||
expects_myclass_descendant(<warning descr="Expected type 'Type[TypeVar('T2', MyClass)]', got 'object' instead">object()</warning>)
|
||||
|
||||
def expects_myclass_descendant_or_none(x: Optional[Type[T2]]):
|
||||
pass
|
||||
|
||||
expects_myclass_descendant_or_none(MyClass)
|
||||
expects_myclass_descendant_or_none(<weak_warning descr="Expected type 'Optional[Any]' (matched generic type 'Optional[Type[TypeVar('T2', MyClass)]]'), got 'MyClass' instead">MyClass()</weak_warning>)
|
||||
expects_myclass_descendant_or_none(<weak_warning descr="Expected type 'Optional[Any]' (matched generic type 'Optional[Type[TypeVar('T2', MyClass)]]'), got 'Type[object]' instead">object</weak_warning>)
|
||||
expects_myclass_descendant_or_none(<weak_warning descr="Expected type 'Optional[Any]' (matched generic type 'Optional[Type[TypeVar('T2', MyClass)]]'), got 'object' instead">object()</weak_warning>)
|
||||
@@ -0,0 +1,21 @@
|
||||
from typing import Type, Union
|
||||
|
||||
class MyClass:
|
||||
pass
|
||||
|
||||
def expects_myclass_or_str1(x: Type[Union[MyClass, str]]):
|
||||
pass
|
||||
|
||||
expects_myclass_or_str1(MyClass)
|
||||
expects_myclass_or_str1(str)
|
||||
expects_myclass_or_str1(<warning descr="Expected type 'Type[Union[MyClass, str]]', got 'Type[int]' instead">int</warning>)
|
||||
expects_myclass_or_str1(<warning descr="Expected type 'Type[Union[MyClass, str]]', got 'int' instead">42</warning>)
|
||||
|
||||
|
||||
def expects_myclass_or_str2(x: Union[Type[MyClass], Type[str]]):
|
||||
pass
|
||||
|
||||
expects_myclass_or_str2(MyClass)
|
||||
expects_myclass_or_str2(str)
|
||||
expects_myclass_or_str2(<warning descr="Expected type 'Type[Union[MyClass, str]]', got 'Type[int]' instead">int</warning>)
|
||||
expects_myclass_or_str2(<warning descr="Expected type 'Type[Union[MyClass, str]]', got 'int' instead">42</warning>)
|
||||
@@ -15,7 +15,7 @@ def test():
|
||||
for item in func(<warning descr="Expected type '(Any) -> str', got '(x: Any) -> int' instead">f</warning>, []):
|
||||
pass
|
||||
|
||||
for item in func(<warning descr="Expected type '(Any) -> str', got 'int' instead">int</warning>, []):
|
||||
for item in func(<warning descr="Expected type '(Any) -> str', got 'Type[int]' instead">int</warning>, []):
|
||||
pass
|
||||
|
||||
for item in func(<warning descr="Expected type '(Any) -> str', got '(x: Any) -> int' instead">lambda x: int(x)</warning>, []):
|
||||
|
||||
@@ -23,7 +23,7 @@ for x in C1:
|
||||
pass
|
||||
|
||||
|
||||
for y in <warning descr="Expected 'collections.Iterable', got 'C2' instead">C2</warning>:
|
||||
for y in <warning descr="Expected 'collections.Iterable', got 'Type[C2]' instead">C2</warning>:
|
||||
pass
|
||||
|
||||
|
||||
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
from typing import Optional, TypeVar
|
||||
|
||||
|
||||
T = TypeVar('T', int)
|
||||
|
||||
|
||||
def expects_int_subclass_or_none(x: Optional[T]):
|
||||
pass
|
||||
|
||||
|
||||
expects_int_subclass_or_none(<weak_warning descr="Expected type 'Optional[Any]' (matched generic type 'Optional[TypeVar('T', int)]'), got 'str' instead">'foo'</weak_warning>)
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
from typing import TypeVar, Type
|
||||
|
||||
T = TypeVar('T')
|
||||
S = TypeVar('T', str)
|
||||
|
||||
|
||||
def expects_type(x: type):
|
||||
pass
|
||||
|
||||
|
||||
def expects_typing_type(x: Type):
|
||||
expects_type(x)
|
||||
|
||||
|
||||
def expects_typing_type_any(x: Type[Any]):
|
||||
expects_type(x)
|
||||
|
||||
|
||||
def expects_any_type_via_type_var(x: Type[T]):
|
||||
expects_type(x)
|
||||
|
||||
|
||||
def expects_str_class(x: Type[str]):
|
||||
expects_type(x)
|
||||
|
||||
|
||||
def expects_str_subclass(x: Type[S]):
|
||||
expects_type(x)
|
||||
|
||||
|
||||
def expects_object(x: object):
|
||||
expects_type(<warning descr="Expected type 'type', got 'object' instead">x</warning>)
|
||||
|
||||
|
||||
expects_type(type)
|
||||
expects_type(object)
|
||||
expects_typing_type(type)
|
||||
expects_typing_type_any(type)
|
||||
expects_typing_type(object)
|
||||
expects_str_class(<warning descr="Expected type 'Type[str]', got 'type' instead">type</warning>)
|
||||
expects_any_type_via_type_var(type)
|
||||
expects_str_subclass(<warning descr="Expected type 'Type[TypeVar('T', str)]', got 'type' instead">type</warning>)
|
||||
expects_object(type)
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<html><body><code>def <b>f</b>(x1: Optional[T], x2: Union[T, Tuple[Any, Any]])<br>Inferred type: (x1: Optional[TypeVar('T', <a href="psi_element://#typename#int">int</a>)], x2: Union[TypeVar('T', <a href="psi_element://#typename#int">int</a>), Tuple[Any, Any]]) -> None<br></code></body></html>
|
||||
@@ -0,0 +1,11 @@
|
||||
from typing import TypeVar, Optional, Union, Tuple, Any
|
||||
|
||||
|
||||
T = TypeVar('T', int)
|
||||
|
||||
|
||||
def f(x1: Optional[T], x2: Union[T, Tuple[Any, Any]]):
|
||||
print(x1, x2)
|
||||
|
||||
|
||||
<the_ref>f
|
||||
@@ -0,0 +1 @@
|
||||
<html><body><code>def <b>f</b>(p1: T1, p2: T2, p3: T3)<br>Inferred type: (p1: TypeVar('T1', <a href="psi_element://#typename#int">int</a>), p2: TypeVar('T2', <a href="psi_element://#typename#int">int</a>, <a href="psi_element://#typename#str">str</a>), p3: TypeVar('T3', List[<a href="psi_element://#typename#bool">bool</a>])) -> None<br></code></body></html>
|
||||
@@ -0,0 +1,10 @@
|
||||
from typing import TypeVar, List
|
||||
|
||||
T1 = TypeVar('T1', int)
|
||||
T2 = TypeVar('T2', int, str)
|
||||
T3 = TypeVar('T3', List[bool])
|
||||
|
||||
def f(p1: T1, p2: T2, p3: T3):
|
||||
pass
|
||||
|
||||
<the_ref>f()
|
||||
@@ -450,7 +450,7 @@ public class Py3TypeTest extends PyTestCase {
|
||||
|
||||
// PY-20757
|
||||
public void testMinElseNone() {
|
||||
doTest("Union[None, Any]",
|
||||
doTest("Optional[Any]",
|
||||
"def get_value(v):\n" +
|
||||
" if v:\n" +
|
||||
" return min(v)\n" +
|
||||
|
||||
@@ -277,4 +277,15 @@ public class PyQuickDocTest extends LightMarkedTestCase {
|
||||
public void testUnknownTuple() {
|
||||
runWithLanguageLevel(LanguageLevel.PYTHON35, this::checkHTMLOnly);
|
||||
}
|
||||
|
||||
public void testTypeVars() {
|
||||
myFixture.copyDirectoryToProject("typing", "");
|
||||
runWithLanguageLevel(LanguageLevel.PYTHON35, this::checkHTMLOnly);
|
||||
}
|
||||
|
||||
// PY-22730
|
||||
public void testOptionalAndUnionTypesContainingTypeVars() {
|
||||
myFixture.copyDirectoryToProject("typing", "");
|
||||
runWithLanguageLevel(LanguageLevel.PYTHON36, this::checkHTMLOnly);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,8 +24,6 @@ import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import static com.jetbrains.python.psi.PyUtil.as;
|
||||
|
||||
/**
|
||||
* @author yole
|
||||
*/
|
||||
@@ -139,7 +137,7 @@ public class PyTypeParserTest extends PyTestCase {
|
||||
final PyType type = PyTypeParser.getTypeByName(myFixture.getFile(), "T");
|
||||
assertNotNull(type);
|
||||
assertInstanceOf(type, PyGenericType.class);
|
||||
assertEquals("TypeVar('T')", type.getName());
|
||||
assertEquals("T", type.getName());
|
||||
}
|
||||
|
||||
// PY-4223
|
||||
@@ -251,7 +249,7 @@ public class PyTypeParserTest extends PyTestCase {
|
||||
assertEquals("int", type0.getName());
|
||||
final PyType type1 = parameterTypes.get(1).getType(context);
|
||||
assertNotNull(type1);
|
||||
assertEquals("TypeVar('T')", type1.getName());
|
||||
assertEquals("T", type1.getName());
|
||||
}
|
||||
|
||||
public void testCallableWithoutArgs() {
|
||||
|
||||
@@ -1496,14 +1496,14 @@ public class PyTypeTest extends PyTestCase {
|
||||
|
||||
// PY-20409
|
||||
public void testGetFromDictWithDefaultNoneValue() {
|
||||
doTest("Union[None, Any]",
|
||||
doTest("Optional[Any]",
|
||||
"d = {}\n" +
|
||||
"expr = d.get(\"abc\", None)");
|
||||
}
|
||||
|
||||
// PY-20757
|
||||
public void testMinOrNone() {
|
||||
doTest("Union[None, Any]",
|
||||
doTest("Optional[Any]",
|
||||
"def get_value(v):\n" +
|
||||
" if v:\n" +
|
||||
" return min(v)\n" +
|
||||
|
||||
@@ -783,6 +783,91 @@ public class PyTypingTest extends PyTestCase {
|
||||
"expr = C(0).get()\n");
|
||||
}
|
||||
|
||||
// PY-20057
|
||||
public void testClassObjectType() {
|
||||
doTest("Type[MyClass]",
|
||||
"from typing import Type\n" +
|
||||
"\n" +
|
||||
"class MyClass:\n" +
|
||||
" pass\n" +
|
||||
"\n" +
|
||||
"def f(x: Type[MyClass]): \n" +
|
||||
" expr = x");
|
||||
}
|
||||
|
||||
// PY-20057
|
||||
public void testConstrainedClassObjectTypeOfParam() {
|
||||
doTest("Type[TypeVar('T', int)]",
|
||||
"from typing import Type, TypeVar\n" +
|
||||
"\n" +
|
||||
"T = TypeVar('T', bound=int)\n" +
|
||||
"\n" +
|
||||
"def f(x: Type[T]):\n" +
|
||||
" expr = x");
|
||||
}
|
||||
|
||||
// PY-20057
|
||||
public void testFunctionCreatesInstanceFromType() {
|
||||
doTest("int",
|
||||
"from typing import Type, TypeVar\n" +
|
||||
"\n" +
|
||||
"T = TypeVar('T')\n" +
|
||||
"\n" +
|
||||
"def f(x: Type[T]) -> T:\n" +
|
||||
" return x()\n" +
|
||||
"\n" +
|
||||
"expr = f(int)");
|
||||
}
|
||||
|
||||
// PY-20057
|
||||
public void testFunctionReturnsTypeOfInstance() {
|
||||
doTest("Type[int]",
|
||||
"from typing import Type, TypeVar\n" +
|
||||
"\n" +
|
||||
"T = TypeVar('T')\n" +
|
||||
"\n" +
|
||||
"def f(x: T) -> Type[T]:\n" +
|
||||
" return type(T)\n" +
|
||||
" \n" +
|
||||
"expr = f(42)");
|
||||
}
|
||||
|
||||
// PY-20057
|
||||
public void testNonParametrizedTypingTypeMapsToBuiltinType() {
|
||||
doTest("type",
|
||||
"from typing import Type\n" +
|
||||
"\n" +
|
||||
"def f(x: Type):\n" +
|
||||
" expr = x");
|
||||
}
|
||||
|
||||
// PY-20057
|
||||
public void testTypingTypeOfAnyMapsToBuiltinType() {
|
||||
doTest("type",
|
||||
"from typing import Type, Any\n" +
|
||||
"\n" +
|
||||
"def f(x: Type[Any]):\n" +
|
||||
" expr = x");
|
||||
}
|
||||
|
||||
// PY-20057
|
||||
public void testIllegalTypingTypeFormat() {
|
||||
doTest("Tuple[Any, Any, Any]",
|
||||
"from typing import Type, Tuple\n" +
|
||||
"\n" +
|
||||
"def f(x: Tuple[Type[42], Type[], Type[unresolved]]):\n" +
|
||||
" expr = x");
|
||||
}
|
||||
|
||||
// PY-20057
|
||||
public void testUnionOfClassObjectTypes() {
|
||||
doTest("Type[Union[int, str]]",
|
||||
"from typing import Type, Union\n" +
|
||||
"\n" +
|
||||
"def f(x: Type[Union[int, str]]):\n" +
|
||||
" expr = x");
|
||||
}
|
||||
|
||||
private void doTestNoInjectedText(@NotNull String text) {
|
||||
myFixture.configureByText(PythonFileType.INSTANCE, text);
|
||||
final InjectedLanguageManager languageManager = InjectedLanguageManager.getInstance(myFixture.getProject());
|
||||
|
||||
@@ -1105,6 +1105,15 @@ public class PythonCompletionTest extends PyTestCase {
|
||||
assertContainsElements(suggested, "baz");
|
||||
}
|
||||
|
||||
// PY-22570
|
||||
public void testNamesReexportedViaStarImport() {
|
||||
myFixture.copyDirectoryToProject(getTestName(true), "");
|
||||
myFixture.configureByFile("a.py");
|
||||
myFixture.completeBasic();
|
||||
final List<String> variants = myFixture.getLookupElementStrings();
|
||||
assertSameElements(variants, "mod1", "mod2", "foo", "_bar");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getTestDataPath() {
|
||||
return super.getTestDataPath() + "/completion";
|
||||
|
||||
@@ -198,4 +198,24 @@ public class Py3TypeCheckerInspectionTest extends PyTestCase {
|
||||
public void testBuiltinInputPy3() {
|
||||
doTest();
|
||||
}
|
||||
|
||||
// PY-200057
|
||||
public void testClassObjectType() {
|
||||
doTest();
|
||||
}
|
||||
|
||||
// PY-20057
|
||||
public void testTypeAndClassObjectTypesCompatibility() {
|
||||
doTest();
|
||||
}
|
||||
|
||||
// PY-20057
|
||||
public void testClassObjectTypeWithUnion() {
|
||||
doTest();
|
||||
}
|
||||
|
||||
// PY-22730
|
||||
public void testOptionalOfBoundTypeVarInWarnings() {
|
||||
doTest();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user