diff --git a/java/java-impl/src/com/intellij/refactoring/introduceVariable/ChainCallInplaceIntroducer.java b/java/java-impl/src/com/intellij/refactoring/introduceVariable/ChainCallInplaceIntroducer.java index de01d378a48f..97a6523b8ee1 100644 --- a/java/java-impl/src/com/intellij/refactoring/introduceVariable/ChainCallInplaceIntroducer.java +++ b/java/java-impl/src/com/intellij/refactoring/introduceVariable/ChainCallInplaceIntroducer.java @@ -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)() -> 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()) { diff --git a/java/java-impl/src/com/intellij/refactoring/introduceVariable/JavaVariableInplaceIntroducer.java b/java/java-impl/src/com/intellij/refactoring/introduceVariable/JavaVariableInplaceIntroducer.java index 270cc341ea30..d5b68adc402b 100644 --- a/java/java-impl/src/com/intellij/refactoring/introduceVariable/JavaVariableInplaceIntroducer.java +++ b/java/java-impl/src/com/intellij/refactoring/introduceVariable/JavaVariableInplaceIntroducer.java @@ -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); diff --git a/java/java-psi-api/src/com/intellij/psi/LambdaUtil.java b/java/java-psi-api/src/com/intellij/psi/LambdaUtil.java index 9fe15b18af5c..95833732fa89 100644 --- a/java/java-psi-api/src/com/intellij/psi/LambdaUtil.java +++ b/java/java-psi-api/src/com/intellij/psi/LambdaUtil.java @@ -255,16 +255,16 @@ public class LambdaUtil { } @NotNull - private static List hasSubsignature(List signatures) { + private static List hasSubSignature(List 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; diff --git a/java/java-tests/testData/refactoring/inplaceIntroduceField/replaceAll_after.java b/java/java-tests/testData/refactoring/inplaceIntroduceField/replaceAll_after.java index a818fa631b43..15eddb42a09a 100644 --- a/java/java-tests/testData/refactoring/inplaceIntroduceField/replaceAll_after.java +++ b/java/java-tests/testData/refactoring/inplaceIntroduceField/replaceAll_after.java @@ -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); } } diff --git a/json/src/com/jetbrains/jsonSchema/impl/JsonBySchemaObjectCompletionContributor.java b/json/src/com/jetbrains/jsonSchema/impl/JsonBySchemaObjectCompletionContributor.java index eace684d7c39..09f4820cb17f 100644 --- a/json/src/com/jetbrains/jsonSchema/impl/JsonBySchemaObjectCompletionContributor.java +++ b/json/src/com/jetbrains/jsonSchema/impl/JsonBySchemaObjectCompletionContributor.java @@ -112,7 +112,7 @@ class JsonBySchemaObjectCompletionContributor extends CompletionContributor { @NotNull VirtualFile schemaFile, @NotNull List 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 properties = myWalker.getPropertyNamesOfParentObject(myOriginalPosition); @@ -222,7 +222,7 @@ class JsonBySchemaObjectCompletionContributor extends CompletionContributor { final JsonSchemaType type = jsonSchemaObject.getType(); final List 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 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() { @Override @@ -355,18 +356,19 @@ class JsonBySchemaObjectCompletionContributor extends CompletionContributor { String defaultValue, List 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); } diff --git a/json/src/com/jetbrains/jsonSchema/impl/JsonLikePsiWalker.java b/json/src/com/jetbrains/jsonSchema/impl/JsonLikePsiWalker.java index 287cbe92a072..a51a839d31fe 100644 --- a/json/src/com/jetbrains/jsonSchema/impl/JsonLikePsiWalker.java +++ b/json/src/com/jetbrains/jsonSchema/impl/JsonLikePsiWalker.java @@ -34,7 +34,7 @@ public interface JsonLikePsiWalker { PsiElement goUpToCheckable(@NotNull final PsiElement element); List findPosition(@NotNull final PsiElement element, boolean isName, boolean forceLastTransition); boolean isNameQuoted(); - boolean hasPropertiesBehind(@NotNull PsiElement element); + boolean hasPropertiesBehindAndNoComma(@NotNull PsiElement element); Set getPropertyNamesOfParentObject(@NotNull PsiElement element); @Nullable JsonPropertyAdapter getParentPropertyAdapter(@NotNull PsiElement element); boolean isTopJsonElement(@NotNull PsiElement element); diff --git a/json/src/com/jetbrains/jsonSchema/impl/JsonOriginalPsiWalker.java b/json/src/com/jetbrains/jsonSchema/impl/JsonOriginalPsiWalker.java index ece32b536416..66c12fe4d211 100644 --- a/json/src/com/jetbrains/jsonSchema/impl/JsonOriginalPsiWalker.java +++ b/json/src/com/jetbrains/jsonSchema/impl/JsonOriginalPsiWalker.java @@ -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; } diff --git a/json/src/com/jetbrains/jsonSchema/impl/ReadJsonSchemaFromPsi.java b/json/src/com/jetbrains/jsonSchema/impl/ReadJsonSchemaFromPsi.java index 06c9526b3d46..96df01450bca 100644 --- a/json/src/com/jetbrains/jsonSchema/impl/ReadJsonSchemaFromPsi.java +++ b/json/src/com/jetbrains/jsonSchema/impl/ReadJsonSchemaFromPsi.java @@ -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 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 { - @Override - public void consume(JsonElement element, JsonSchemaObject object) { - - } - } } diff --git a/json/tests/test/com/jetbrains/jsonSchema/impl/JsonBySchemaHeavyCompletionTest.java b/json/tests/test/com/jetbrains/jsonSchema/impl/JsonBySchemaHeavyCompletionTest.java new file mode 100644 index 000000000000..9531abab953f --- /dev/null +++ b/json/tests/test/com/jetbrains/jsonSchema/impl/JsonBySchemaHeavyCompletionTest.java @@ -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); + } + } + }); + } +} diff --git a/json/tests/testData/jsonSchema/completion/insertEnumValue/Schema.json b/json/tests/testData/jsonSchema/completion/insertEnumValue/Schema.json new file mode 100644 index 000000000000..7b4f071c0294 --- /dev/null +++ b/json/tests/testData/jsonSchema/completion/insertEnumValue/Schema.json @@ -0,0 +1,7 @@ +{ + "properties": { + "jsx": { + "enum": [ "preserve", "react", "react-native" ] + } + } +} \ No newline at end of file diff --git a/json/tests/testData/jsonSchema/completion/insertEnumValue/testValue.json b/json/tests/testData/jsonSchema/completion/insertEnumValue/testValue.json new file mode 100644 index 000000000000..661c1ad7dc7a --- /dev/null +++ b/json/tests/testData/jsonSchema/completion/insertEnumValue/testValue.json @@ -0,0 +1,3 @@ +{ + "jsx": +} \ No newline at end of file diff --git a/json/tests/testData/jsonSchema/completion/insertEnumValue/testValue_after.json b/json/tests/testData/jsonSchema/completion/insertEnumValue/testValue_after.json new file mode 100644 index 000000000000..9357f4a220b8 --- /dev/null +++ b/json/tests/testData/jsonSchema/completion/insertEnumValue/testValue_after.json @@ -0,0 +1,3 @@ +{ + "jsx": "preserve" +} \ No newline at end of file diff --git a/json/tests/testData/jsonSchema/completion/insertPropertyName/Schema.json b/json/tests/testData/jsonSchema/completion/insertPropertyName/Schema.json new file mode 100644 index 000000000000..51a473a1d7b8 --- /dev/null +++ b/json/tests/testData/jsonSchema/completion/insertPropertyName/Schema.json @@ -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" + } + } +} \ No newline at end of file diff --git a/json/tests/testData/jsonSchema/completion/insertPropertyName/testBooleanType.json b/json/tests/testData/jsonSchema/completion/insertPropertyName/testBooleanType.json new file mode 100644 index 000000000000..fc719517cf97 --- /dev/null +++ b/json/tests/testData/jsonSchema/completion/insertPropertyName/testBooleanType.json @@ -0,0 +1,3 @@ +{ + "booleanTy" +} \ No newline at end of file diff --git a/json/tests/testData/jsonSchema/completion/insertPropertyName/testBooleanTypeComma.json b/json/tests/testData/jsonSchema/completion/insertPropertyName/testBooleanTypeComma.json new file mode 100644 index 000000000000..e065e2af3963 --- /dev/null +++ b/json/tests/testData/jsonSchema/completion/insertPropertyName/testBooleanTypeComma.json @@ -0,0 +1,4 @@ +{ + "booleanTy" + "someOtherProperty": false +} \ No newline at end of file diff --git a/json/tests/testData/jsonSchema/completion/insertPropertyName/testBooleanTypeComma_after.json b/json/tests/testData/jsonSchema/completion/insertPropertyName/testBooleanTypeComma_after.json new file mode 100644 index 000000000000..619a737e0fdb --- /dev/null +++ b/json/tests/testData/jsonSchema/completion/insertPropertyName/testBooleanTypeComma_after.json @@ -0,0 +1,4 @@ +{ + "booleanType": false, + "someOtherProperty": false +} \ No newline at end of file diff --git a/json/tests/testData/jsonSchema/completion/insertPropertyName/testBooleanType_after.json b/json/tests/testData/jsonSchema/completion/insertPropertyName/testBooleanType_after.json new file mode 100644 index 000000000000..fa272c99a836 --- /dev/null +++ b/json/tests/testData/jsonSchema/completion/insertPropertyName/testBooleanType_after.json @@ -0,0 +1,3 @@ +{ + "booleanType": false +} \ No newline at end of file diff --git a/json/tests/testData/jsonSchema/completion/insertPropertyName/testIntegerType.json b/json/tests/testData/jsonSchema/completion/insertPropertyName/testIntegerType.json new file mode 100644 index 000000000000..c71f54604d5e --- /dev/null +++ b/json/tests/testData/jsonSchema/completion/insertPropertyName/testIntegerType.json @@ -0,0 +1,3 @@ +{ + "integerTy" +} \ No newline at end of file diff --git a/json/tests/testData/jsonSchema/completion/insertPropertyName/testIntegerTypeComma.json b/json/tests/testData/jsonSchema/completion/insertPropertyName/testIntegerTypeComma.json new file mode 100644 index 000000000000..40369c25f74f --- /dev/null +++ b/json/tests/testData/jsonSchema/completion/insertPropertyName/testIntegerTypeComma.json @@ -0,0 +1,4 @@ +{ + "integerTy" + "someOtherProperty": 1 +} \ No newline at end of file diff --git a/json/tests/testData/jsonSchema/completion/insertPropertyName/testIntegerTypeComma_after.json b/json/tests/testData/jsonSchema/completion/insertPropertyName/testIntegerTypeComma_after.json new file mode 100644 index 000000000000..500d7a2d8e4c --- /dev/null +++ b/json/tests/testData/jsonSchema/completion/insertPropertyName/testIntegerTypeComma_after.json @@ -0,0 +1,4 @@ +{ + "integerType":, + "someOtherProperty": 1 +} \ No newline at end of file diff --git a/json/tests/testData/jsonSchema/completion/insertPropertyName/testIntegerType_after.json b/json/tests/testData/jsonSchema/completion/insertPropertyName/testIntegerType_after.json new file mode 100644 index 000000000000..fc659a697430 --- /dev/null +++ b/json/tests/testData/jsonSchema/completion/insertPropertyName/testIntegerType_after.json @@ -0,0 +1,3 @@ +{ + "integerType": +} \ No newline at end of file diff --git a/json/tests/testData/jsonSchema/completion/insertPropertyName/testName.json b/json/tests/testData/jsonSchema/completion/insertPropertyName/testName.json new file mode 100644 index 000000000000..a3830a723dc7 --- /dev/null +++ b/json/tests/testData/jsonSchema/completion/insertPropertyName/testName.json @@ -0,0 +1,3 @@ +{ + "js" +} \ No newline at end of file diff --git a/json/tests/testData/jsonSchema/completion/insertPropertyName/testNameWithDefaultIntegerValue.json b/json/tests/testData/jsonSchema/completion/insertPropertyName/testNameWithDefaultIntegerValue.json new file mode 100644 index 000000000000..89f350fc70ef --- /dev/null +++ b/json/tests/testData/jsonSchema/completion/insertPropertyName/testNameWithDefaultIntegerValue.json @@ -0,0 +1,3 @@ +{ + "withIntegerDefault" +} \ No newline at end of file diff --git a/json/tests/testData/jsonSchema/completion/insertPropertyName/testNameWithDefaultIntegerValueNoQuotesComma.json b/json/tests/testData/jsonSchema/completion/insertPropertyName/testNameWithDefaultIntegerValueNoQuotesComma.json new file mode 100644 index 000000000000..bab9defb389e --- /dev/null +++ b/json/tests/testData/jsonSchema/completion/insertPropertyName/testNameWithDefaultIntegerValueNoQuotesComma.json @@ -0,0 +1,4 @@ +{ + withIntegerDefault + "someOtherProperty": 112 +} \ No newline at end of file diff --git a/json/tests/testData/jsonSchema/completion/insertPropertyName/testNameWithDefaultIntegerValueNoQuotesComma_after.json b/json/tests/testData/jsonSchema/completion/insertPropertyName/testNameWithDefaultIntegerValueNoQuotesComma_after.json new file mode 100644 index 000000000000..d4b99358c14f --- /dev/null +++ b/json/tests/testData/jsonSchema/completion/insertPropertyName/testNameWithDefaultIntegerValueNoQuotesComma_after.json @@ -0,0 +1,4 @@ +{ + "withIntegerDefaultValue": 118, + "someOtherProperty": 112 +} \ No newline at end of file diff --git a/json/tests/testData/jsonSchema/completion/insertPropertyName/testNameWithDefaultIntegerValue_after.json b/json/tests/testData/jsonSchema/completion/insertPropertyName/testNameWithDefaultIntegerValue_after.json new file mode 100644 index 000000000000..0e276905bb16 --- /dev/null +++ b/json/tests/testData/jsonSchema/completion/insertPropertyName/testNameWithDefaultIntegerValue_after.json @@ -0,0 +1,3 @@ +{ + "withIntegerDefaultValue": 118 +} \ No newline at end of file diff --git a/json/tests/testData/jsonSchema/completion/insertPropertyName/testNameWithDefaultStringValue.json b/json/tests/testData/jsonSchema/completion/insertPropertyName/testNameWithDefaultStringValue.json new file mode 100644 index 000000000000..cf78bbbd228b --- /dev/null +++ b/json/tests/testData/jsonSchema/completion/insertPropertyName/testNameWithDefaultStringValue.json @@ -0,0 +1,3 @@ +{ + "withStringDefault" +} \ No newline at end of file diff --git a/json/tests/testData/jsonSchema/completion/insertPropertyName/testNameWithDefaultStringValueComma.json b/json/tests/testData/jsonSchema/completion/insertPropertyName/testNameWithDefaultStringValueComma.json new file mode 100644 index 000000000000..e52a254b2a12 --- /dev/null +++ b/json/tests/testData/jsonSchema/completion/insertPropertyName/testNameWithDefaultStringValueComma.json @@ -0,0 +1,4 @@ +{ + "withStringDefault", + "some": 2 +} \ No newline at end of file diff --git a/json/tests/testData/jsonSchema/completion/insertPropertyName/testNameWithDefaultStringValueComma_after.json b/json/tests/testData/jsonSchema/completion/insertPropertyName/testNameWithDefaultStringValueComma_after.json new file mode 100644 index 000000000000..b76abc10ba9f --- /dev/null +++ b/json/tests/testData/jsonSchema/completion/insertPropertyName/testNameWithDefaultStringValueComma_after.json @@ -0,0 +1,4 @@ +{ + "withStringDefaultValue": "stringDefault", + "some": 2 +} \ No newline at end of file diff --git a/json/tests/testData/jsonSchema/completion/insertPropertyName/testNameWithDefaultStringValueNoQuotes.json b/json/tests/testData/jsonSchema/completion/insertPropertyName/testNameWithDefaultStringValueNoQuotes.json new file mode 100644 index 000000000000..02cf872a64ca --- /dev/null +++ b/json/tests/testData/jsonSchema/completion/insertPropertyName/testNameWithDefaultStringValueNoQuotes.json @@ -0,0 +1,3 @@ +{ + withStringDefault +} \ No newline at end of file diff --git a/json/tests/testData/jsonSchema/completion/insertPropertyName/testNameWithDefaultStringValueNoQuotes_after.json b/json/tests/testData/jsonSchema/completion/insertPropertyName/testNameWithDefaultStringValueNoQuotes_after.json new file mode 100644 index 000000000000..3432fd7449dc --- /dev/null +++ b/json/tests/testData/jsonSchema/completion/insertPropertyName/testNameWithDefaultStringValueNoQuotes_after.json @@ -0,0 +1,3 @@ +{ + "withStringDefaultValue": "stringDefault" +} \ No newline at end of file diff --git a/json/tests/testData/jsonSchema/completion/insertPropertyName/testNameWithDefaultStringValue_after.json b/json/tests/testData/jsonSchema/completion/insertPropertyName/testNameWithDefaultStringValue_after.json new file mode 100644 index 000000000000..3432fd7449dc --- /dev/null +++ b/json/tests/testData/jsonSchema/completion/insertPropertyName/testNameWithDefaultStringValue_after.json @@ -0,0 +1,3 @@ +{ + "withStringDefaultValue": "stringDefault" +} \ No newline at end of file diff --git a/json/tests/testData/jsonSchema/completion/insertPropertyName/testName_after.json b/json/tests/testData/jsonSchema/completion/insertPropertyName/testName_after.json new file mode 100644 index 000000000000..5a5b64cf526b --- /dev/null +++ b/json/tests/testData/jsonSchema/completion/insertPropertyName/testName_after.json @@ -0,0 +1,3 @@ +{ + "jsx": "" +} \ No newline at end of file diff --git a/json/tests/testData/jsonSchema/completion/insertPropertyName/testObjectType.json b/json/tests/testData/jsonSchema/completion/insertPropertyName/testObjectType.json new file mode 100644 index 000000000000..edc306cc2428 --- /dev/null +++ b/json/tests/testData/jsonSchema/completion/insertPropertyName/testObjectType.json @@ -0,0 +1,3 @@ +{ + "objectTy" +} \ No newline at end of file diff --git a/json/tests/testData/jsonSchema/completion/insertPropertyName/testObjectType_after.json b/json/tests/testData/jsonSchema/completion/insertPropertyName/testObjectType_after.json new file mode 100644 index 000000000000..22356e03f956 --- /dev/null +++ b/json/tests/testData/jsonSchema/completion/insertPropertyName/testObjectType_after.json @@ -0,0 +1,5 @@ +{ + "objectType": { + + } +} \ No newline at end of file diff --git a/json/tests/testData/jsonSchema/completion/insertPropertyName/testStringType.json b/json/tests/testData/jsonSchema/completion/insertPropertyName/testStringType.json new file mode 100644 index 000000000000..9dd1cf02af96 --- /dev/null +++ b/json/tests/testData/jsonSchema/completion/insertPropertyName/testStringType.json @@ -0,0 +1,3 @@ +{ + "stringTy" +} \ No newline at end of file diff --git a/json/tests/testData/jsonSchema/completion/insertPropertyName/testStringTypeComma.json b/json/tests/testData/jsonSchema/completion/insertPropertyName/testStringTypeComma.json new file mode 100644 index 000000000000..b7d59797825b --- /dev/null +++ b/json/tests/testData/jsonSchema/completion/insertPropertyName/testStringTypeComma.json @@ -0,0 +1,4 @@ +{ + "stringTy" + "someOtherProperty": "123" +} \ No newline at end of file diff --git a/json/tests/testData/jsonSchema/completion/insertPropertyName/testStringTypeComma_after.json b/json/tests/testData/jsonSchema/completion/insertPropertyName/testStringTypeComma_after.json new file mode 100644 index 000000000000..f1d2dcb290de --- /dev/null +++ b/json/tests/testData/jsonSchema/completion/insertPropertyName/testStringTypeComma_after.json @@ -0,0 +1,4 @@ +{ + "stringType": "", + "someOtherProperty": "123" +} \ No newline at end of file diff --git a/json/tests/testData/jsonSchema/completion/insertPropertyName/testStringType_after.json b/json/tests/testData/jsonSchema/completion/insertPropertyName/testStringType_after.json new file mode 100644 index 000000000000..4941857678e8 --- /dev/null +++ b/json/tests/testData/jsonSchema/completion/insertPropertyName/testStringType_after.json @@ -0,0 +1,3 @@ +{ + "stringType": "" +} \ No newline at end of file diff --git a/platform/core-impl/src/com/intellij/codeInsight/highlighting/HighlightUsagesDescriptionLocation.java b/platform/core-impl/src/com/intellij/codeInsight/highlighting/HighlightUsagesDescriptionLocation.java index 10b41243d3cd..a9671defef2b 100644 --- a/platform/core-impl/src/com/intellij/codeInsight/highlighting/HighlightUsagesDescriptionLocation.java +++ b/platform/core-impl/src/com/intellij/codeInsight/highlighting/HighlightUsagesDescriptionLocation.java @@ -29,6 +29,7 @@ public class HighlightUsagesDescriptionLocation extends ElementDescriptionLocati private HighlightUsagesDescriptionLocation() { } + @NotNull @Override public ElementDescriptionProvider getDefaultProvider() { return new ElementDescriptionProvider() { diff --git a/platform/indexing-impl/src/com/intellij/usageView/UsageViewTypeLocation.java b/platform/indexing-impl/src/com/intellij/usageView/UsageViewTypeLocation.java index 8ded650b95e1..e1829d750380 100644 --- a/platform/indexing-impl/src/com/intellij/usageView/UsageViewTypeLocation.java +++ b/platform/indexing-impl/src/com/intellij/usageView/UsageViewTypeLocation.java @@ -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; diff --git a/platform/lang-impl/src/com/intellij/ide/util/DeleteNameDescriptionLocation.java b/platform/lang-impl/src/com/intellij/ide/util/DeleteNameDescriptionLocation.java index 16e584b4820e..cd06ba0f465f 100644 --- a/platform/lang-impl/src/com/intellij/ide/util/DeleteNameDescriptionLocation.java +++ b/platform/lang-impl/src/com/intellij/ide/util/DeleteNameDescriptionLocation.java @@ -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; diff --git a/platform/lang-impl/src/com/intellij/ide/util/DeleteTypeDescriptionLocation.java b/platform/lang-impl/src/com/intellij/ide/util/DeleteTypeDescriptionLocation.java index 82c7354622ba..679bbb70e85a 100644 --- a/platform/lang-impl/src/com/intellij/ide/util/DeleteTypeDescriptionLocation.java +++ b/platform/lang-impl/src/com/intellij/ide/util/DeleteTypeDescriptionLocation.java @@ -37,6 +37,7 @@ public class DeleteTypeDescriptionLocation extends ElementDescriptionLocation { private static final ElementDescriptionProvider ourDefaultProvider = new DefaultProvider(); + @NotNull @Override public ElementDescriptionProvider getDefaultProvider() { return ourDefaultProvider; diff --git a/platform/lang-impl/src/com/intellij/refactoring/util/NonCodeSearchDescriptionLocation.java b/platform/lang-impl/src/com/intellij/refactoring/util/NonCodeSearchDescriptionLocation.java index f3238e4b67b7..9d9ea7c219e8 100644 --- a/platform/lang-impl/src/com/intellij/refactoring/util/NonCodeSearchDescriptionLocation.java +++ b/platform/lang-impl/src/com/intellij/refactoring/util/NonCodeSearchDescriptionLocation.java @@ -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; diff --git a/platform/lang-impl/src/com/intellij/refactoring/util/RefactoringDescriptionLocation.java b/platform/lang-impl/src/com/intellij/refactoring/util/RefactoringDescriptionLocation.java index ed2ae876c262..71bc763ce30f 100644 --- a/platform/lang-impl/src/com/intellij/refactoring/util/RefactoringDescriptionLocation.java +++ b/platform/lang-impl/src/com/intellij/refactoring/util/RefactoringDescriptionLocation.java @@ -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; diff --git a/platform/lang-impl/src/com/intellij/usageView/UsageViewLongNameLocation.java b/platform/lang-impl/src/com/intellij/usageView/UsageViewLongNameLocation.java index aa699ae29c9a..5d53dab6c3b8 100644 --- a/platform/lang-impl/src/com/intellij/usageView/UsageViewLongNameLocation.java +++ b/platform/lang-impl/src/com/intellij/usageView/UsageViewLongNameLocation.java @@ -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; diff --git a/platform/lang-impl/src/com/intellij/usageView/UsageViewNodeTextLocation.java b/platform/lang-impl/src/com/intellij/usageView/UsageViewNodeTextLocation.java index 94692f9c2e27..a98a4efac78e 100644 --- a/platform/lang-impl/src/com/intellij/usageView/UsageViewNodeTextLocation.java +++ b/platform/lang-impl/src/com/intellij/usageView/UsageViewNodeTextLocation.java @@ -37,6 +37,7 @@ public class UsageViewNodeTextLocation extends ElementDescriptionLocation { public static final UsageViewNodeTextLocation INSTANCE = new UsageViewNodeTextLocation(); + @NotNull @Override public ElementDescriptionProvider getDefaultProvider() { return DEFAULT_PROVIDER; diff --git a/platform/lang-impl/src/com/intellij/usageView/UsageViewShortNameLocation.java b/platform/lang-impl/src/com/intellij/usageView/UsageViewShortNameLocation.java index ad1e3edd2d18..2f635fd2db8c 100644 --- a/platform/lang-impl/src/com/intellij/usageView/UsageViewShortNameLocation.java +++ b/platform/lang-impl/src/com/intellij/usageView/UsageViewShortNameLocation.java @@ -34,6 +34,7 @@ public class UsageViewShortNameLocation extends ElementDescriptionLocation { public static final UsageViewShortNameLocation INSTANCE = new UsageViewShortNameLocation(); + @NotNull @Override public ElementDescriptionProvider getDefaultProvider() { return DEFAULT_PROVIDER; diff --git a/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ActionToolbarImpl.java b/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ActionToolbarImpl.java index 01742027e476..b2103ce43c42 100644 --- a/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ActionToolbarImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ActionToolbarImpl.java @@ -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; diff --git a/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/PresentationFactory.java b/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/PresentationFactory.java index 6f1aa461341b..6c4df4a8abb2 100644 --- a/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/PresentationFactory.java +++ b/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/PresentationFactory.java @@ -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 actions) { - HashSet toRemove = new HashSet<>(myAction2Presentation.keySet()); - toRemove.removeAll(actions); - - for (AnAction action : toRemove) { - myAction2Presentation.remove(action); - } - } } diff --git a/platform/platform-resources/src/brokenPlugins.txt b/platform/platform-resources/src/brokenPlugins.txt index d075f05404aa..c2a003f5f079 100644 --- a/platform/platform-resources/src/brokenPlugins.txt +++ b/platform/platform-resources/src/brokenPlugins.txt @@ -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 diff --git a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/AbstractLanguageInjectionSupport.java b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/AbstractLanguageInjectionSupport.java index 44fab8be2d3d..b5f783937aa6 100644 --- a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/AbstractLanguageInjectionSupport.java +++ b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/AbstractLanguageInjectionSupport.java @@ -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 commentRef) { diff --git a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/CommentLanguageInjector.java b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/CommentLanguageInjector.java index de479811a62e..696bbf5f9436 100644 --- a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/CommentLanguageInjector.java +++ b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/CommentLanguageInjector.java @@ -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; diff --git a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/LanguageInjectionSupport.java b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/LanguageInjectionSupport.java index 167ddb4db25a..23230a249c8c 100644 --- a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/LanguageInjectionSupport.java +++ b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/LanguageInjectionSupport.java @@ -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 commentRef); diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/formatter/blocks/GroovyBlockGenerator.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/formatter/blocks/GroovyBlockGenerator.java index d54618609dbe..3e5c6b84f59a 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/formatter/blocks/GroovyBlockGenerator.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/formatter/blocks/GroovyBlockGenerator.java @@ -626,7 +626,7 @@ public class GroovyBlockGenerator { private static void addChildNodes(PsiElement elem, ArrayList 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()) { diff --git a/plugins/java-i18n/src/com/intellij/codeInspection/duplicateStringLiteral/DuplicateStringLiteralInspection.java b/plugins/java-i18n/src/com/intellij/codeInspection/duplicateStringLiteral/DuplicateStringLiteralInspection.java index b113fef17bf6..6282312da9b9 100644 --- a/plugins/java-i18n/src/com/intellij/codeInspection/duplicateStringLiteral/DuplicateStringLiteralInspection.java +++ b/plugins/java-i18n/src/com/intellij/codeInspection/duplicateStringLiteral/DuplicateStringLiteralInspection.java @@ -120,10 +120,10 @@ public class DuplicateStringLiteralInspection extends BaseLocalInspectionTool { } @NotNull - private List findDuplicateLiterals(String stringToFind, Project project) { + private List findDuplicateLiterals(String stringToFind, Project project) { Set resultFiles = getCandidateFiles(stringToFind, project); if (resultFiles.isEmpty()) return Collections.emptyList(); - List foundExpr = new ArrayList<>(); + List 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 foundExpr = getDuplicateLiterals(stringToFind, originalExpression); if (foundExpr.isEmpty()) return; Set 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 getDuplicateLiterals(String stringToFind, PsiLiteralExpression place) { Project project = place.getProject(); - Map> map = CachedValuesManager.getManager(project).getCachedValue(project, () -> { - Map> value = ConcurrentFactoryMap.createConcurrentMap( + Map> map = CachedValuesManager.getManager(project).getCachedValue(project, () -> { + Map> 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) { diff --git a/python/pluginJava/com/jetbrains/python/psi/impl/PyJavaClassType.java b/python/pluginJava/com/jetbrains/python/psi/impl/PyJavaClassType.java index a06208a41e4d..ae1e30b7ba3e 100644 --- a/python/pluginJava/com/jetbrains/python/psi/impl/PyJavaClassType.java +++ b/python/pluginJava/com/jetbrains/python/psi/impl/PyJavaClassType.java @@ -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() { diff --git a/python/psi-api/src/com/jetbrains/python/psi/impl/PyPsiUtils.java b/python/psi-api/src/com/jetbrains/python/psi/impl/PyPsiUtils.java index 8811c5000d75..cc867d2a69e8 100644 --- a/python/psi-api/src/com/jetbrains/python/psi/impl/PyPsiUtils.java +++ b/python/psi-api/src/com/jetbrains/python/psi/impl/PyPsiUtils.java @@ -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; diff --git a/python/psi-api/src/com/jetbrains/python/psi/types/PyClassLikeType.java b/python/psi-api/src/com/jetbrains/python/psi/types/PyClassLikeType.java index f7d40633fd48..f962f3cd6548 100644 --- a/python/psi-api/src/com/jetbrains/python/psi/types/PyClassLikeType.java +++ b/python/psi-api/src/com/jetbrains/python/psi/types/PyClassLikeType.java @@ -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 { @Nullable String getClassQName(); diff --git a/python/psi-api/src/com/jetbrains/python/psi/types/PyInstantiableType.java b/python/psi-api/src/com/jetbrains/python/psi/types/PyInstantiableType.java new file mode 100644 index 000000000000..9bb670c7c606 --- /dev/null +++ b/python/psi-api/src/com/jetbrains/python/psi/types/PyInstantiableType.java @@ -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> extends PyType { + boolean isDefinition(); + + @NotNull + T toInstance(); + + @NotNull + T toClass(); +} diff --git a/python/psi-api/src/com/jetbrains/python/psi/types/PyType.java b/python/psi-api/src/com/jetbrains/python/psi/types/PyType.java index a458f9488951..4a668e23759c 100644 --- a/python/psi-api/src/com/jetbrains/python/psi/types/PyType.java +++ b/python/psi-api/src/com/jetbrains/python/psi/types/PyType.java @@ -66,6 +66,7 @@ public interface PyType { Key> 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 diff --git a/python/src/com/jetbrains/python/PyCustomType.java b/python/src/com/jetbrains/python/PyCustomType.java index 46acab051843..d6eddc458670 100644 --- a/python/src/com/jetbrains/python/PyCustomType.java +++ b/python/src/com/jetbrains/python/PyCustomType.java @@ -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() { diff --git a/python/src/com/jetbrains/python/codeInsight/controlflow/PyTypeAssertionEvaluator.java b/python/src/com/jetbrains/python/codeInsight/controlflow/PyTypeAssertionEvaluator.java index a6c983c6b5ab..377d5b4520b3 100644 --- a/python/src/com/jetbrains/python/codeInsight/controlflow/PyTypeAssertionEvaluator.java +++ b/python/src/com/jetbrains/python/codeInsight/controlflow/PyTypeAssertionEvaluator.java @@ -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; diff --git a/python/src/com/jetbrains/python/codeInsight/stdlib/PyNamedTupleType.java b/python/src/com/jetbrains/python/codeInsight/stdlib/PyNamedTupleType.java index c209106f909b..2835e373a58c 100644 --- a/python/src/com/jetbrains/python/codeInsight/stdlib/PyNamedTupleType.java +++ b/python/src/com/jetbrains/python/codeInsight/stdlib/PyNamedTupleType.java @@ -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; diff --git a/python/src/com/jetbrains/python/codeInsight/typing/PyTypingTypeProvider.java b/python/src/com/jetbrains/python/codeInsight/typing/PyTypingTypeProvider.java index 6ee353183b47..6ab2f86239f7 100644 --- a/python/src/com/jetbrains/python/codeInsight/typing/PyTypingTypeProvider.java +++ b/python/src/com/jetbrains/python/codeInsight/typing/PyTypingTypeProvider.java @@ -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 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 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 getClassObjectType(@NotNull PsiElement resolved, @NotNull Context context) { + if (resolved instanceof PySubscriptionExpression) { + final PySubscriptionExpression subsExpr = (PySubscriptionExpression)resolved; + final PyExpression operand = subsExpr.getOperand(); + final Collection 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 getAnyType(@NotNull PsiElement element) { + return ANY.equals(getQualifiedName(element)) ? Ref.create() : null; + } + @Nullable private static Ref getClassType(@NotNull PsiElement element, @NotNull TypeEvalContext context) { if (element instanceof PyTypedElement) { diff --git a/python/src/com/jetbrains/python/documentation/PyTypeModelBuilder.java b/python/src/com/jetbrains/python/documentation/PyTypeModelBuilder.java index 7fc8cf0a6a6b..88240e22865e 100644 --- a/python/src/com/jetbrains/python/documentation/PyTypeModelBuilder.java +++ b/python/src/com/jetbrains/python/documentation/PyTypeModelBuilder.java @@ -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 bounds; + + public GenericType(@Nullable String name, @NotNull List 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 unionMembers = unionType.getMembers(); + final Ref 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 instanceTypes = ContainerUtil.map(unionMembers, t -> build(((PyClassType)t).toInstance(), allowUnions)); + result = new ClassObjectType(new OneOf(instanceTypes)); + } else { - result = Optional - .ofNullable(getOptionalType(unionType)) - .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 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 getOptionalType(@NotNull PyUnionType type) { final Collection 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 parameterModels = null; final List 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(")"); + } } } diff --git a/python/src/com/jetbrains/python/packaging/setupPy/SetupTaskChooserAction.java b/python/src/com/jetbrains/python/packaging/setupPy/SetupTaskChooserAction.java index 74bb08d30cc3..8c0743100cce 100644 --- a/python/src/com/jetbrains/python/packaging/setupPy/SetupTaskChooserAction.java +++ b/python/src/com/jetbrains/python/packaging/setupPy/SetupTaskChooserAction.java @@ -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 options = SetupTaskIntrospector.getSetupTaskOptions(module, taskName); - List 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 options = SetupTaskIntrospector.getSetupTaskOptions(module, taskName); + List 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 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()); diff --git a/python/src/com/jetbrains/python/psi/PyUtil.java b/python/src/com/jetbrains/python/psi/PyUtil.java index 3c5c1b1ee3ce..7e4c74feea09 100644 --- a/python/src/com/jetbrains/python/psi/PyUtil.java +++ b/python/src/com/jetbrains/python/psi/PyUtil.java @@ -846,20 +846,13 @@ public class PyUtil { }); } - public static T getParameterizedCachedValue(@NotNull PsiElement element, @NotNull P param, @NotNull NotNullFunction f) { - final Map cache = CachedValuesManager.getCachedValue(element, new CachedValueProvider>() { - @Nullable - @Override - public Result> compute() { - return Result.create(Maps.newHashMap(), PsiModificationTracker.MODIFICATION_COUNT); - } + public static T getParameterizedCachedValue(@NotNull PsiElement element, @Nullable P param, @NotNull NullableFunction f) { + final CachedValuesManager manager = CachedValuesManager.getManager(element.getProject()); + final Map, Optional> 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); } /** diff --git a/python/src/com/jetbrains/python/psi/impl/PyFileImpl.java b/python/src/com/jetbrains/python/psi/impl/PyFileImpl.java index 105346707114..b950c0e12759 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyFileImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyFileImpl.java @@ -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 dunderAll = getDunderAll(); final List 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 getHint(@NotNull Key hintKey) { - return processor.getHint(hintKey); - } - - @Override - public void handleEvent(@NotNull Event event, @Nullable Object associated) { - processor.handleEvent(event, associated); - } }; Set pyFiles = resolveState.get(PROCESSED_FILES); diff --git a/python/src/com/jetbrains/python/psi/impl/PyFromImportStatementImpl.java b/python/src/com/jetbrains/python/psi/impl/PyFromImportStatementImpl.java index 0a8a015b62f1..9ac5bae4811d 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyFromImportStatementImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyFromImportStatementImpl.java @@ -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 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 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 res = Lists.newArrayList(); - for (RatedResolveResult result : results) { - res.add(result); - } - return res; + if (results != null && !results.isEmpty()) { + return Lists.newArrayList(results); } } } diff --git a/python/src/com/jetbrains/python/psi/resolve/QualifiedNameFinder.java b/python/src/com/jetbrains/python/psi/resolve/QualifiedNameFinder.java index bab8681fbbd6..24c4213715ef 100644 --- a/python/src/com/jetbrains/python/psi/resolve/QualifiedNameFinder.java +++ b/python/src/com/jetbrains/python/psi/resolve/QualifiedNameFinder.java @@ -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 param) { + final PsiElement symbol = param.getFirst(); + final PsiElement foothold = param.getSecond(); + + final PsiFileSystemItem srcfile = PyPsiUtils.getFileSystemItem(symbol); if (srcfile == null) { return null; } diff --git a/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java b/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java index 71de5731379c..32d8aec23cb0 100644 --- a/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java +++ b/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java @@ -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 */ diff --git a/python/src/com/jetbrains/python/psi/types/PyCollectionTypeImpl.java b/python/src/com/jetbrains/python/psi/types/PyCollectionTypeImpl.java index 92bb5a904d1f..ea48feab5568 100644 --- a/python/src/com/jetbrains/python/psi/types/PyCollectionTypeImpl.java +++ b/python/src/com/jetbrains/python/psi/types/PyCollectionTypeImpl.java @@ -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; diff --git a/python/src/com/jetbrains/python/psi/types/PyGenericType.java b/python/src/com/jetbrains/python/psi/types/PyGenericType.java index b3f47fb9a4ae..bcfd524dc416 100644 --- a/python/src/com/jetbrains/python/psi/types/PyGenericType.java +++ b/python/src/com/jetbrains/python/psi/types/PyGenericType.java @@ -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 { @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); + } } diff --git a/python/src/com/jetbrains/python/psi/types/PyTypeChecker.java b/python/src/com/jetbrains/python/psi/types/PyTypeChecker.java index 5d46f1aa18e5..0ca884be7b24 100644 --- a/python/src/com/jetbrains/python/psi/types/PyTypeChecker.java +++ b/python/src/com/jetbrains/python/psi/types/PyTypeChecker.java @@ -65,18 +65,38 @@ public class PyTypeChecker { private static boolean match(@Nullable PyType expected, @Nullable PyType actual, @NotNull TypeEvalContext context, @Nullable Map 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 actualAttributes = actualClassType.getMemberNames(true, context); return actualAttributes.containsAll(((PyStructuralType)expected).getAttributeNames()); } - if (actual instanceof PyStructuralType && expected instanceof PyClassType) { - final Set expectedAttributes = ((PyClassType)expected).getMemberNames(true, context); + if (actual instanceof PyStructuralType && expectedClassType != null) { + final Set 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) { diff --git a/python/testData/completion/namesReexportedViaStarImport/a.py b/python/testData/completion/namesReexportedViaStarImport/a.py new file mode 100644 index 000000000000..284501bd6125 --- /dev/null +++ b/python/testData/completion/namesReexportedViaStarImport/a.py @@ -0,0 +1 @@ +from pkg import \ No newline at end of file diff --git a/python/testData/completion/namesReexportedViaStarImport/pkg/__init__.py b/python/testData/completion/namesReexportedViaStarImport/pkg/__init__.py new file mode 100644 index 000000000000..0ef7b811ce65 --- /dev/null +++ b/python/testData/completion/namesReexportedViaStarImport/pkg/__init__.py @@ -0,0 +1,2 @@ +from .mod1 import * +from .mod2 import * diff --git a/python/testData/completion/namesReexportedViaStarImport/pkg/mod1.py b/python/testData/completion/namesReexportedViaStarImport/pkg/mod1.py new file mode 100644 index 000000000000..c56290a94e2e --- /dev/null +++ b/python/testData/completion/namesReexportedViaStarImport/pkg/mod1.py @@ -0,0 +1,2 @@ +foo = 1 +_foo = 2 \ No newline at end of file diff --git a/python/testData/completion/namesReexportedViaStarImport/pkg/mod2.py b/python/testData/completion/namesReexportedViaStarImport/pkg/mod2.py new file mode 100644 index 000000000000..eed13f89da1e --- /dev/null +++ b/python/testData/completion/namesReexportedViaStarImport/pkg/mod2.py @@ -0,0 +1,4 @@ +__all__ = ['_bar'] + +bar = 1 +_bar = 2 diff --git a/python/testData/inspections/PyTypeCheckerInspection/ClassObjectType.py b/python/testData/inspections/PyTypeCheckerInspection/ClassObjectType.py new file mode 100644 index 000000000000..9a829ed33c98 --- /dev/null +++ b/python/testData/inspections/PyTypeCheckerInspection/ClassObjectType.py @@ -0,0 +1,36 @@ +from typing import Type, TypeVar, Optional + +class MyClass: + pass + +def expects_myclass(x: Type[MyClass]): + pass + +expects_myclass(MyClass()) +expects_class(MyClass) + +T1 = TypeVar('T1') +def expects_any_class(x: Type[T1]): + pass + +expects_any_class(MyClass) +expects_any_class(MyClass()) +expects_any_class(object) +expects_any_class(object()) + +T2 = TypeVar('T2', MyClass) +def expects_myclass_descendant(x: Type[T2]): + pass + +expects_myclass_descendant(MyClass) +expects_myclass_descendant(MyClass()) +expects_myclass_descendant(object) +expects_myclass_descendant(object()) + +def expects_myclass_descendant_or_none(x: Optional[Type[T2]]): + pass + +expects_myclass_descendant_or_none(MyClass) +expects_myclass_descendant_or_none(MyClass()) +expects_myclass_descendant_or_none(object) +expects_myclass_descendant_or_none(object()) \ No newline at end of file diff --git a/python/testData/inspections/PyTypeCheckerInspection/ClassObjectTypeWithUnion.py b/python/testData/inspections/PyTypeCheckerInspection/ClassObjectTypeWithUnion.py new file mode 100644 index 000000000000..4e22f3045773 --- /dev/null +++ b/python/testData/inspections/PyTypeCheckerInspection/ClassObjectTypeWithUnion.py @@ -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(int) +expects_myclass_or_str1(42) + + +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(int) +expects_myclass_or_str2(42) diff --git a/python/testData/inspections/PyTypeCheckerInspection/FunctionParameterReturnType.py b/python/testData/inspections/PyTypeCheckerInspection/FunctionParameterReturnType.py index efdfc5722cde..3a4d0a8c1273 100644 --- a/python/testData/inspections/PyTypeCheckerInspection/FunctionParameterReturnType.py +++ b/python/testData/inspections/PyTypeCheckerInspection/FunctionParameterReturnType.py @@ -15,7 +15,7 @@ def test(): for item in func(f, []): pass - for item in func(int, []): + for item in func(int, []): pass for item in func(lambda x: int(x), []): diff --git a/python/testData/inspections/PyTypeCheckerInspection/MetaClassIteration.py b/python/testData/inspections/PyTypeCheckerInspection/MetaClassIteration.py index 0ec69266af7c..daaefdd4cc32 100644 --- a/python/testData/inspections/PyTypeCheckerInspection/MetaClassIteration.py +++ b/python/testData/inspections/PyTypeCheckerInspection/MetaClassIteration.py @@ -23,7 +23,7 @@ for x in C1: pass -for y in C2: +for y in C2: pass diff --git a/python/testData/inspections/PyTypeCheckerInspection/OptionalOfBoundTypeVarInWarnings.py b/python/testData/inspections/PyTypeCheckerInspection/OptionalOfBoundTypeVarInWarnings.py new file mode 100644 index 000000000000..64cee3d5ba3b --- /dev/null +++ b/python/testData/inspections/PyTypeCheckerInspection/OptionalOfBoundTypeVarInWarnings.py @@ -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('foo') \ No newline at end of file diff --git a/python/testData/inspections/PyTypeCheckerInspection/TypeAndClassObjectTypesCompatibility.py b/python/testData/inspections/PyTypeCheckerInspection/TypeAndClassObjectTypesCompatibility.py new file mode 100644 index 000000000000..cc0be1130066 --- /dev/null +++ b/python/testData/inspections/PyTypeCheckerInspection/TypeAndClassObjectTypesCompatibility.py @@ -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(x) + + +expects_type(type) +expects_type(object) +expects_typing_type(type) +expects_typing_type_any(type) +expects_typing_type(object) +expects_str_class(type) +expects_any_type_via_type_var(type) +expects_str_subclass(type) +expects_object(type) + + + + diff --git a/python/testData/quickdoc/OptionalAndUnionTypesContainingTypeVars.html b/python/testData/quickdoc/OptionalAndUnionTypesContainingTypeVars.html new file mode 100644 index 000000000000..38ed0a947505 --- /dev/null +++ b/python/testData/quickdoc/OptionalAndUnionTypesContainingTypeVars.html @@ -0,0 +1 @@ +def f(x1: Optional[T], x2: Union[T, Tuple[Any, Any]])
Inferred type: (x1: Optional[TypeVar('T', int)], x2: Union[TypeVar('T', int), Tuple[Any, Any]]) -> None
\ No newline at end of file diff --git a/python/testData/quickdoc/OptionalAndUnionTypesContainingTypeVars.py b/python/testData/quickdoc/OptionalAndUnionTypesContainingTypeVars.py new file mode 100644 index 000000000000..61f83aa38952 --- /dev/null +++ b/python/testData/quickdoc/OptionalAndUnionTypesContainingTypeVars.py @@ -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) + + +f diff --git a/python/testData/quickdoc/TypeVars.html b/python/testData/quickdoc/TypeVars.html new file mode 100644 index 000000000000..344bc1b3b4a7 --- /dev/null +++ b/python/testData/quickdoc/TypeVars.html @@ -0,0 +1 @@ +def f(p1: T1, p2: T2, p3: T3)
Inferred type: (p1: TypeVar('T1', int), p2: TypeVar('T2', int, str), p3: TypeVar('T3', List[bool])) -> None
\ No newline at end of file diff --git a/python/testData/quickdoc/TypeVars.py b/python/testData/quickdoc/TypeVars.py new file mode 100644 index 000000000000..5bbb833557a2 --- /dev/null +++ b/python/testData/quickdoc/TypeVars.py @@ -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 + +f() \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/Py3TypeTest.java b/python/testSrc/com/jetbrains/python/Py3TypeTest.java index f948e98cd602..ff5dd8a79fe0 100644 --- a/python/testSrc/com/jetbrains/python/Py3TypeTest.java +++ b/python/testSrc/com/jetbrains/python/Py3TypeTest.java @@ -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" + diff --git a/python/testSrc/com/jetbrains/python/PyQuickDocTest.java b/python/testSrc/com/jetbrains/python/PyQuickDocTest.java index 7746b33e004b..d4f4a6041c9f 100644 --- a/python/testSrc/com/jetbrains/python/PyQuickDocTest.java +++ b/python/testSrc/com/jetbrains/python/PyQuickDocTest.java @@ -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); + } } diff --git a/python/testSrc/com/jetbrains/python/PyTypeParserTest.java b/python/testSrc/com/jetbrains/python/PyTypeParserTest.java index 79e1a3e7c958..af5b38ff7c8c 100644 --- a/python/testSrc/com/jetbrains/python/PyTypeParserTest.java +++ b/python/testSrc/com/jetbrains/python/PyTypeParserTest.java @@ -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() { diff --git a/python/testSrc/com/jetbrains/python/PyTypeTest.java b/python/testSrc/com/jetbrains/python/PyTypeTest.java index c57ba0b361e1..f75e723a94d9 100644 --- a/python/testSrc/com/jetbrains/python/PyTypeTest.java +++ b/python/testSrc/com/jetbrains/python/PyTypeTest.java @@ -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" + diff --git a/python/testSrc/com/jetbrains/python/PyTypingTest.java b/python/testSrc/com/jetbrains/python/PyTypingTest.java index 8a7e76ad6ffc..9342dd00adfe 100644 --- a/python/testSrc/com/jetbrains/python/PyTypingTest.java +++ b/python/testSrc/com/jetbrains/python/PyTypingTest.java @@ -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()); diff --git a/python/testSrc/com/jetbrains/python/PythonCompletionTest.java b/python/testSrc/com/jetbrains/python/PythonCompletionTest.java index 4e105d631e98..6e66e1a7d0d8 100644 --- a/python/testSrc/com/jetbrains/python/PythonCompletionTest.java +++ b/python/testSrc/com/jetbrains/python/PythonCompletionTest.java @@ -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 variants = myFixture.getLookupElementStrings(); + assertSameElements(variants, "mod1", "mod2", "foo", "_bar"); + } + @Override protected String getTestDataPath() { return super.getTestDataPath() + "/completion"; diff --git a/python/testSrc/com/jetbrains/python/inspections/Py3TypeCheckerInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/Py3TypeCheckerInspectionTest.java index 62775974104c..9fbc9fe97bc7 100644 --- a/python/testSrc/com/jetbrains/python/inspections/Py3TypeCheckerInspectionTest.java +++ b/python/testSrc/com/jetbrains/python/inspections/Py3TypeCheckerInspectionTest.java @@ -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(); + } }