diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/JavaGenericsUtil.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/JavaGenericsUtil.java index 26169b7bc063..e01eb710d73d 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/JavaGenericsUtil.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/JavaGenericsUtil.java @@ -101,18 +101,8 @@ public class JavaGenericsUtil { final PsiExpression[] args = argumentList.getExpressions(); if (args.length == parametersCount) { final PsiExpression lastArg = args[args.length - 1]; - if (lastArg instanceof PsiReferenceExpression) { - final PsiElement lastArgsResolve = ((PsiReferenceExpression)lastArg).resolve(); - if (lastArgsResolve instanceof PsiParameter) { - if (((PsiParameter)lastArgsResolve).getType() instanceof PsiArrayType) { - return false; - } - } - } - else if (lastArg instanceof PsiMethodCallExpression) { - if (lastArg.getType() instanceof PsiArrayType) { - return false; - } + if (lastArg.getType() instanceof PsiArrayType) { + return false; } } for (int i = parametersCount - 1; i < args.length; i++) { diff --git a/java/java-impl/src/com/intellij/refactoring/changeSignature/ChangeSignatureProcessor.java b/java/java-impl/src/com/intellij/refactoring/changeSignature/ChangeSignatureProcessor.java index efa82dfbc230..77fce0eb1366 100644 --- a/java/java-impl/src/com/intellij/refactoring/changeSignature/ChangeSignatureProcessor.java +++ b/java/java-impl/src/com/intellij/refactoring/changeSignature/ChangeSignatureProcessor.java @@ -37,7 +37,10 @@ import com.intellij.util.containers.MultiMap; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Set; import static com.intellij.util.ObjectUtils.assertNotNull; @@ -140,15 +143,7 @@ public class ChangeSignatureProcessor extends ChangeSignatureProcessorBase { if (!processor.setupDefaultValues(myChangeInfo, refUsages, myProject)) return false; } MultiMap conflictDescriptions = new MultiMap(); - for (ChangeSignatureUsageProcessor usageProcessor : ChangeSignatureUsageProcessor.EP_NAME.getExtensions()) { - final MultiMap conflicts = usageProcessor.findConflicts(myChangeInfo, refUsages); - for (PsiElement key : conflicts.keySet()) { - Collection collection = conflictDescriptions.get(key); - if (collection.size() == 0) collection = new HashSet(); - collection.addAll(conflicts.get(key)); - conflictDescriptions.put(key, collection); - } - } + collectConflictsFromExtensions(refUsages, conflictDescriptions, myChangeInfo); final UsageInfo[] usagesIn = refUsages.get(); RenameUtil.addConflictDescriptions(usagesIn, conflictDescriptions); diff --git a/java/java-impl/src/com/intellij/refactoring/introduceparameterobject/IntroduceParameterObjectProcessor.java b/java/java-impl/src/com/intellij/refactoring/introduceparameterobject/IntroduceParameterObjectProcessor.java index 21bad491f704..2cd79bd89714 100644 --- a/java/java-impl/src/com/intellij/refactoring/introduceparameterobject/IntroduceParameterObjectProcessor.java +++ b/java/java-impl/src/com/intellij/refactoring/introduceparameterobject/IntroduceParameterObjectProcessor.java @@ -38,6 +38,8 @@ import com.intellij.psi.util.PsiUtil; import com.intellij.psi.util.TypeConversionUtil; import com.intellij.refactoring.MoveDestination; import com.intellij.refactoring.RefactorJBundle; +import com.intellij.refactoring.changeSignature.ChangeInfo; +import com.intellij.refactoring.changeSignature.ChangeSignatureProcessorBase; import com.intellij.refactoring.introduceparameterobject.usageInfo.*; import com.intellij.refactoring.util.FixableUsageInfo; import com.intellij.refactoring.util.FixableUsagesRefactoringProcessor; @@ -77,6 +79,7 @@ public class IntroduceParameterObjectProcessor extends FixableUsagesRefactoringP private final Set paramsNeedingGetters = new HashSet(); private final PsiClass existingClass; private PsiMethod myExistingClassCompatibleConstructor; + private ChangeInfo myChangeInfo; public IntroduceParameterObjectProcessor(String className, String packageName, @@ -164,6 +167,7 @@ public class IntroduceParameterObjectProcessor extends FixableUsagesRefactoringP } } } + List changeSignatureUsages = new ArrayList<>(); for (UsageInfo usageInfo : refUsages.get()) { if (usageInfo instanceof FixableUsageInfo) { final String conflictMessage = ((FixableUsageInfo)usageInfo).getConflictMessage(); @@ -171,7 +175,13 @@ public class IntroduceParameterObjectProcessor extends FixableUsagesRefactoringP conflicts.putValue(usageInfo.getElement(), conflictMessage); } } + else { + changeSignatureUsages.add(usageInfo); + } } + + ChangeSignatureProcessorBase.collectConflictsFromExtensions(new Ref<>(changeSignatureUsages.toArray(new UsageInfo[changeSignatureUsages.size()])), conflicts, myChangeInfo); + return showConflicts(conflicts, refUsages.get()); } @@ -179,7 +189,24 @@ public class IntroduceParameterObjectProcessor extends FixableUsagesRefactoringP if (myUseExistingClass && existingClass != null) { myExistingClassCompatibleConstructor = existingClassIsCompatible(existingClass, parameters); } - findUsagesForMethod(method, usages, true); + + final PsiCodeBlock body = method.getBody(); + final String baseParameterName = StringUtil.decapitalize(className); + + final String fixedParamName = + body != null + ? JavaCodeStyleManager.getInstance(myProject).suggestUniqueVariableName(baseParameterName, body.getLBrace(), true) + : JavaCodeStyleManager.getInstance(myProject).propertyNameToVariableName(baseParameterName, VariableKind.PARAMETER); + + myChangeInfo = + new MergeMethodArguments(method, className, packageName, fixedParamName, paramsToMerge, typeParams, keepMethodAsDelegate, + myCreateInnerClass ? method.getContainingClass() : null).createChangeInfo(); + + for (UsageInfo info : ChangeSignatureProcessorBase.findUsages(myChangeInfo)) { + usages.add(new ChangeSignatureUsageWrapper(info)); + } + + findUsagesForMethod(method, usages, fixedParamName); if (myUseExistingClass && existingClass != null && !(paramsNeedingGetters.isEmpty() && paramsNeedingSetters.isEmpty())) { usages.add(new AppendAccessorsUsageInfo(existingClass, myGenerateAccessors, paramsNeedingGetters, paramsNeedingSetters, parameters)); @@ -187,7 +214,7 @@ public class IntroduceParameterObjectProcessor extends FixableUsagesRefactoringP final PsiMethod[] overridingMethods = OverridingMethodsSearch.search(method, true).toArray(PsiMethod.EMPTY_ARRAY); for (PsiMethod siblingMethod : overridingMethods) { - findUsagesForMethod(siblingMethod, usages, false); + findUsagesForMethod(siblingMethod, usages, fixedParamName); } if (myNewVisibility != null) { @@ -195,16 +222,7 @@ public class IntroduceParameterObjectProcessor extends FixableUsagesRefactoringP } } - private void findUsagesForMethod(PsiMethod overridingMethod, List usages, boolean changeSignature) { - final PsiCodeBlock body = overridingMethod.getBody(); - final String baseParameterName = StringUtil.decapitalize(className); - final String fixedParamName = - body != null - ? JavaCodeStyleManager.getInstance(myProject).suggestUniqueVariableName(baseParameterName, body.getLBrace(), true) - : JavaCodeStyleManager.getInstance(myProject).propertyNameToVariableName(baseParameterName, VariableKind.PARAMETER); - - usages.add(new MergeMethodArguments(overridingMethod, className, packageName, fixedParamName, paramsToMerge, typeParams, keepMethodAsDelegate, myCreateInnerClass ? method.getContainingClass() : null, changeSignature)); - + private void findUsagesForMethod(PsiMethod overridingMethod, List usages, String fixedParamName) { final ParamUsageVisitor visitor = new ParamUsageVisitor(overridingMethod, paramsToMerge); overridingMethod.accept(visitor); final Set values = visitor.getParameterUsages(); @@ -261,6 +279,13 @@ public class IntroduceParameterObjectProcessor extends FixableUsagesRefactoringP } } } + List changeSignatureUsages = new ArrayList<>(); + for (UsageInfo info : usageInfos) { + if (info instanceof ChangeSignatureUsageWrapper) { + changeSignatureUsages.add(((ChangeSignatureUsageWrapper)info).getInfo()); + } + } + ChangeSignatureProcessorBase.doChangeSignature(myChangeInfo, changeSignatureUsages.toArray(new UsageInfo[changeSignatureUsages.size()])); } } @@ -537,4 +562,20 @@ public class IntroduceParameterObjectProcessor extends FixableUsagesRefactoringP } } + + private static class ChangeSignatureUsageWrapper extends FixableUsageInfo { + private final UsageInfo myInfo; + + public ChangeSignatureUsageWrapper(UsageInfo info) { + super(info.getElement()); + myInfo = info; + } + + public UsageInfo getInfo() { + return myInfo; + } + + @Override + public void fixUsage() throws IncorrectOperationException {} + } } diff --git a/java/java-impl/src/com/intellij/refactoring/introduceparameterobject/usageInfo/MergeMethodArguments.java b/java/java-impl/src/com/intellij/refactoring/introduceparameterobject/MergeMethodArguments.java similarity index 63% rename from java/java-impl/src/com/intellij/refactoring/introduceparameterobject/usageInfo/MergeMethodArguments.java rename to java/java-impl/src/com/intellij/refactoring/introduceparameterobject/MergeMethodArguments.java index 0aecd58be007..33da7f6253c9 100644 --- a/java/java-impl/src/com/intellij/refactoring/introduceparameterobject/usageInfo/MergeMethodArguments.java +++ b/java/java-impl/src/com/intellij/refactoring/introduceparameterobject/MergeMethodArguments.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2011 JetBrains s.r.o. + * Copyright 2000-2016 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. @@ -13,32 +13,29 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.intellij.refactoring.introduceparameterobject.usageInfo; +package com.intellij.refactoring.introduceparameterobject; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.*; import com.intellij.psi.codeStyle.JavaCodeStyleManager; -import com.intellij.psi.impl.source.PsiImmediateClassType; -import com.intellij.psi.search.GlobalSearchScope; -import com.intellij.psi.util.TypeConversionUtil; -import com.intellij.refactoring.changeSignature.ChangeSignatureProcessor; +import com.intellij.refactoring.changeSignature.ChangeInfo; +import com.intellij.refactoring.changeSignature.JavaChangeInfoImpl; import com.intellij.refactoring.changeSignature.ParameterInfoImpl; -import com.intellij.refactoring.util.FixableUsageInfo; +import com.intellij.refactoring.util.CanonicalTypes; import com.intellij.util.Function; import com.intellij.util.IncorrectOperationException; +import com.intellij.util.VisibilityUtil; import org.jetbrains.annotations.Nullable; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; -@SuppressWarnings({"MethodWithTooManyParameters"}) -public class MergeMethodArguments extends FixableUsageInfo { +public class MergeMethodArguments { private final PsiMethod method; private final PsiClass myContainingClass; - private final boolean myChangeSignature; private final boolean myKeepMethodAsDelegate; private final List typeParams; private final String className; @@ -53,50 +50,45 @@ public class MergeMethodArguments extends FixableUsageInfo { String parameterName, int[] paramsToMerge, List typeParams, - final boolean keepMethodAsDelegate, final PsiClass containingClass, boolean changeSignature) { - super(method); + final boolean keepMethodAsDelegate, + final PsiClass containingClass) { this.paramsToMerge = paramsToMerge; this.packageName = packageName; this.className = className; this.parameterName = parameterName; this.method = method; myContainingClass = containingClass; - myChangeSignature = changeSignature; lastParamIsVararg = method.isVarArgs() && isParameterToMerge(method.getParameterList().getParametersCount() - 1); myKeepMethodAsDelegate = keepMethodAsDelegate; this.typeParams = new ArrayList(typeParams); } - public void fixUsage() throws IncorrectOperationException { + public ChangeInfo createChangeInfo() { final Project project = method.getProject(); final JavaPsiFacade psiFacade = JavaPsiFacade.getInstance(project); - final PsiMethod deepestSuperMethod = method.findDeepestSuperMethod(); - final PsiClass psiClass; + PsiElementFactory factory = JavaPsiFacade.getElementFactory(project); + String packageName; if (myContainingClass != null) { - psiClass = myContainingClass.findInnerClassByName(className, false); - } - else { - psiClass = psiFacade.findClass(StringUtil.getQualifiedName(packageName, className), GlobalSearchScope.allScope(project)); - } - assert psiClass != null; - PsiSubstitutor subst = PsiSubstitutor.EMPTY; - if (deepestSuperMethod != null) { - final PsiClass parentClass = deepestSuperMethod.getContainingClass(); - final PsiSubstitutor parentSubstitutor = - TypeConversionUtil.getSuperClassSubstitutor(parentClass, method.getContainingClass(), PsiSubstitutor.EMPTY); - for (int i1 = 0; i1 < psiClass.getTypeParameters().length; i1++) { - final PsiTypeParameter typeParameter = psiClass.getTypeParameters()[i1]; - for (PsiTypeParameter parameter : parentClass.getTypeParameters()) { - if (Comparing.strEqual(typeParameter.getName(), parameter.getName())) { - subst = subst.put(typeParameter, parentSubstitutor.substitute( - new PsiImmediateClassType(parameter, PsiSubstitutor.EMPTY))); - break; - } - } + packageName = myContainingClass.getQualifiedName(); + if (packageName == null) { + packageName = myContainingClass.getName(); } } + else { + packageName = this.packageName; + } + + String text = StringUtil.getQualifiedName(packageName, className); + if (!typeParams.isEmpty()) { + text += "<" + StringUtil.join(typeParams, new Function() { + @Override + public String fun(PsiTypeParameter parameter) { + return parameter.getName(); + } + }, ", ") + ">"; + } + final PsiType classType = factory.createTypeFromText(text, method); final List parametersInfo = new ArrayList(); - final PsiClassType classType = JavaPsiFacade.getElementFactory(project).createType(psiClass, subst); final ParameterInfoImpl mergedParamInfo = new ParameterInfoImpl(-1, parameterName, classType, null) { @Override @@ -117,33 +109,16 @@ public class MergeMethodArguments extends FixableUsageInfo { } parametersInfo.add(firstIncludedIdx == -1 ? 0 : firstIncludedIdx, mergedParamInfo); - final SmartPsiElementPointer meth = SmartPointerManager.getInstance(project).createSmartPsiElementPointer(method); - - final Runnable performChangeSignatureRunnable = new Runnable() { - @Override - public void run() { - final PsiMethod psiMethod = meth.getElement(); - if (psiMethod == null) return; - if (myChangeSignature) { - final ChangeSignatureProcessor changeSignatureProcessor = - new ChangeSignatureProcessor(psiMethod.getProject(), psiMethod, - myKeepMethodAsDelegate, null, psiMethod.getName(), - psiMethod.getReturnType(), - parametersInfo.toArray(new ParameterInfoImpl[parametersInfo.size()])); - changeSignatureProcessor.run(); - } - } - }; - if (ApplicationManager.getApplication().isUnitTestMode()) { - performChangeSignatureRunnable.run(); - } else { - ApplicationManager.getApplication().invokeLater(new Runnable() { - @Override - public void run() { - CommandProcessor.getInstance().runUndoTransparentAction(performChangeSignatureRunnable); - } - }); - } + PsiType returnType = method.getReturnType(); + return new JavaChangeInfoImpl(VisibilityUtil.getVisibilityModifier(method.getModifierList()), + method, + method.getName(), + returnType != null ? CanonicalTypes.createTypeWrapper(returnType) : null, + parametersInfo.toArray(new ParameterInfoImpl[parametersInfo.size()]), + null, + myKeepMethodAsDelegate, + Collections.emptySet(), + Collections.emptySet()); } private boolean isParameterToMerge(int index) { diff --git a/java/java-psi-api/src/com/intellij/psi/GenericsUtil.java b/java/java-psi-api/src/com/intellij/psi/GenericsUtil.java index 314580fc7d80..aaf7141512ab 100644 --- a/java/java-psi-api/src/com/intellij/psi/GenericsUtil.java +++ b/java/java-psi-api/src/com/intellij/psi/GenericsUtil.java @@ -307,7 +307,10 @@ public class GenericsUtil { PsiType componentType = arrayType.getComponentType(); PsiType type = componentType.accept(this); if (type == componentType) return arrayType; - return type.createArrayType(); + if (type instanceof PsiWildcardType) { + type = ((PsiWildcardType)type).getBound(); + } + return type != null ? type.createArrayType() : arrayType; } @Override diff --git a/java/java-psi-api/src/com/intellij/psi/util/PsiUtil.java b/java/java-psi-api/src/com/intellij/psi/util/PsiUtil.java index 98df1492904c..b8b9f39e2f8d 100644 --- a/java/java-psi-api/src/com/intellij/psi/util/PsiUtil.java +++ b/java/java-psi-api/src/com/intellij/psi/util/PsiUtil.java @@ -764,6 +764,9 @@ public final class PsiUtil extends PsiUtilCore { return null; } + /** + * Applies capture conversion to the type in context + */ @NotNull public static PsiType captureToplevelWildcards(@NotNull final PsiType type, @NotNull final PsiElement context) { if (type instanceof PsiClassType) { @@ -811,6 +814,38 @@ public final class PsiUtil extends PsiUtilCore { return type; } + /** + * Opens top level captured wildcards and remap them according to the context. + * The only valid purpose: allow to speculate on non-physical expressions about types, e.g. to detect redundant casts with 'wildcards' + */ + public static PsiType recaptureWildcards(PsiType type, PsiElement context) { + if (type instanceof PsiClassType) { + final PsiClassType.ClassResolveResult resolveResult = ((PsiClassType)type).resolveGenerics(); + final PsiClass aClass = resolveResult.getElement(); + if (aClass != null) { + final PsiSubstitutor substitutor = resolveResult.getSubstitutor(); + + PsiSubstitutor resultSubstitution = null; + for (PsiTypeParameter parameter : substitutor.getSubstitutionMap().keySet()) { + final PsiType substitute = substitutor.substitute(parameter); + if (substitute instanceof PsiCapturedWildcardType) { + if (resultSubstitution == null) resultSubstitution = substitutor; + resultSubstitution = resultSubstitution.put(parameter, ((PsiCapturedWildcardType)substitute).getWildcard()); + } + } + + if (resultSubstitution != null) { + final PsiElementFactory factory = JavaPsiFacade.getElementFactory(context.getProject()); + return captureToplevelWildcards(factory.createType(aClass, resultSubstitution), context); + } + } + } + else if (type instanceof PsiArrayType) { + return recaptureWildcards(((PsiArrayType)type).getComponentType(), context).createArrayType(); + } + return type; + } + public static boolean isInsideJavadocComment(PsiElement element) { return PsiTreeUtil.getParentOfType(element, PsiDocComment.class, true) != null; } diff --git a/java/java-psi-api/src/com/intellij/psi/util/RedundantCastUtil.java b/java/java-psi-api/src/com/intellij/psi/util/RedundantCastUtil.java index d4c9d0960cac..d4829aa8b268 100644 --- a/java/java-psi-api/src/com/intellij/psi/util/RedundantCastUtil.java +++ b/java/java-psi-api/src/com/intellij/psi/util/RedundantCastUtil.java @@ -385,7 +385,7 @@ public class RedundantCastUtil { if (oldMethod.equals(newResult.getElement()) && (!(newCall instanceof PsiCallExpression) || oldAnonymousClass != null && newAnonymousClass != null && Comparing.equal(oldAnonymousClass.getBaseClassType(), newAnonymousClass.getBaseClassType()) || - Comparing.equal(((PsiCallExpression)newCall).getType(), ((PsiCallExpression)expression).getType())) && + Comparing.equal(PsiUtil.recaptureWildcards(((PsiCallExpression)newCall).getType(), expression), ((PsiCallExpression)expression).getType())) && newResult.isValidResult()) { if (!(newArgs[i] instanceof PsiFunctionalExpression) || castType != null && castType.equals(((PsiFunctionalExpression)newArgs[i]).getFunctionalInterfaceType())) { addToResults(cast); diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting7/UncheckedGenericsArrayCreation.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting7/UncheckedGenericsArrayCreation.java index ce0660b63538..1a2f4ce9a7b0 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting7/UncheckedGenericsArrayCreation.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting7/UncheckedGenericsArrayCreation.java @@ -29,6 +29,14 @@ class Test { public static void main(String[] args) { asList(new ArrayList()); + ArrayList[] arrayOfStrings = null; + asList(arrayOfStrings); + asList((ArrayList[])null); + + //overload should be chosen before target type is known -> inference failure + List[]> arraysList = asList(arrayOfStrings); + System.out.println(arraysList); + asListSuppressed(new ArrayList()); //noinspection unchecked diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/redundantCast/CapturedWildcardInCast.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/redundantCast/CapturedWildcardInCast.java new file mode 100644 index 000000000000..85a53bf77ba4 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/redundantCast/CapturedWildcardInCast.java @@ -0,0 +1,9 @@ + +import java.util.function.IntFunction; +import java.util.stream.Stream; + +class MyTest { + private static void getArguments(final Stream> classStream) { + final Class[] classes = classStream.toArray(((IntFunction[]>) (value) -> new Class[value]) ); + } +} diff --git a/java/java-tests/testData/refactoring/introduceVariable/DenotableType3.after.java b/java/java-tests/testData/refactoring/introduceVariable/DenotableType3.after.java new file mode 100644 index 000000000000..793e6c8130bc --- /dev/null +++ b/java/java-tests/testData/refactoring/introduceVariable/DenotableType3.after.java @@ -0,0 +1,9 @@ +import java.util.function.IntFunction; +import java.util.stream.Stream; + +class MyTest { + private static void getArguments(final Stream> classStream) { + IntFunction[]> m = (value) -> new Class[value]; + final Class[] classes = classStream.toArray(m); + } +} diff --git a/java/java-tests/testData/refactoring/introduceVariable/DenotableType3.java b/java/java-tests/testData/refactoring/introduceVariable/DenotableType3.java new file mode 100644 index 000000000000..fa27ffb44e62 --- /dev/null +++ b/java/java-tests/testData/refactoring/introduceVariable/DenotableType3.java @@ -0,0 +1,7 @@ +import java.util.stream.Stream; + +class MyTest { + private static void getArguments(final Stream> classStream) { + final Class[] classes = classStream.toArray((value) -> new Class[value]); + } +} diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/LambdaRedundantCastTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/LambdaRedundantCastTest.java index 820925a1bd2e..dbe905446a8d 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/LambdaRedundantCastTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/LambdaRedundantCastTest.java @@ -61,6 +61,10 @@ public class LambdaRedundantCastTest extends LightDaemonAnalyzerTestCase { doTest(); } + public void testCapturedWildcardInCast() throws Exception { + doTest(); + } + private void doTest() { doTest(BASE_PATH + "/" + getTestName(false) + ".java", true, false); } diff --git a/java/java-tests/testSrc/com/intellij/refactoring/IntroduceVariableTest.java b/java/java-tests/testSrc/com/intellij/refactoring/IntroduceVariableTest.java index ae1fb74ff029..24eb5804792b 100644 --- a/java/java-tests/testSrc/com/intellij/refactoring/IntroduceVariableTest.java +++ b/java/java-tests/testSrc/com/intellij/refactoring/IntroduceVariableTest.java @@ -512,6 +512,10 @@ public class IntroduceVariableTest extends LightCodeInsightTestCase { doTest(new MockIntroduceVariableHandler("m", false, false, false, "I>")); } + public void testDenotableType3() { + doTest(new MockIntroduceVariableHandler("m", false, false, false, "java.util.function.IntFunction[]>")); + } + public void testReturnNonExportedArray() { doTest(new MockIntroduceVariableHandler("i", false, false, false, "java.io.File[]") { @Override diff --git a/platform/core-api/src/com/intellij/openapi/application/TransactionGuard.java b/platform/core-api/src/com/intellij/openapi/application/TransactionGuard.java index 9134e9c58fbd..6c41ce40deca 100644 --- a/platform/core-api/src/com/intellij/openapi/application/TransactionGuard.java +++ b/platform/core-api/src/com/intellij/openapi/application/TransactionGuard.java @@ -161,11 +161,6 @@ public abstract class TransactionGuard { @NotNull public abstract AccessToken startSynchronousTransaction(@NotNull TransactionKind kind); - /** - * @return whether there's a transaction currently running - */ - public abstract boolean isInsideTransaction(); - /** * When on UI thread and there's no other transaction running, executes the given runnable. If there is a transaction running, * but the given {@code kind} is allowed via {@link #acceptNestedTransactions(TransactionKind...)}, merges two transactions @@ -190,4 +185,11 @@ public abstract class TransactionGuard { */ @NotNull public abstract AccessToken acceptNestedTransactions(TransactionKind... kinds); + + /** + * Asserts that a transaction is currently running, or not. Callable only on Swing thread. + * @param transactionRequired whether the assertion should check that the application is inside transaction or not + * @param errorMessage the message that will be logged if current transaction status differs from the expected one + */ + public abstract void assertInsideTransaction(boolean transactionRequired, @NotNull String errorMessage); } diff --git a/platform/core-api/src/com/intellij/pom/PomTransaction.java b/platform/core-api/src/com/intellij/pom/PomTransaction.java index 679b00a45050..788e6c90603c 100644 --- a/platform/core-api/src/com/intellij/pom/PomTransaction.java +++ b/platform/core-api/src/com/intellij/pom/PomTransaction.java @@ -18,16 +18,20 @@ package com.intellij.pom; import com.intellij.pom.event.PomModelEvent; import com.intellij.psi.PsiElement; import com.intellij.util.IncorrectOperationException; +import org.jetbrains.annotations.NotNull; /** * @author ik */ public interface PomTransaction { + @NotNull PomModelEvent getAccumulatedEvent(); void run() throws IncorrectOperationException; + @NotNull PsiElement getChangeScope(); + @NotNull PomModelAspect getTransactionAspect(); } diff --git a/platform/core-api/src/com/intellij/pom/impl/PomTransactionBase.java b/platform/core-api/src/com/intellij/pom/impl/PomTransactionBase.java index 78383f215857..d40a8581c26b 100644 --- a/platform/core-api/src/com/intellij/pom/impl/PomTransactionBase.java +++ b/platform/core-api/src/com/intellij/pom/impl/PomTransactionBase.java @@ -21,18 +21,20 @@ import com.intellij.pom.PomTransaction; import com.intellij.pom.event.PomModelEvent; import com.intellij.psi.PsiElement; import com.intellij.util.IncorrectOperationException; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; public abstract class PomTransactionBase implements PomTransaction{ private final PsiElement myScope; private final PomModelAspect myAspect; private final PomModelEvent myAccumulatedEvent; - public PomTransactionBase(PsiElement scope, final PomModelAspect aspect){ + public PomTransactionBase(@NotNull PsiElement scope, @NotNull final PomModelAspect aspect){ myScope = scope; myAspect = aspect; myAccumulatedEvent = new PomModelEvent(PomManager.getModel(scope.getProject())); } + @NotNull @Override public PomModelEvent getAccumulatedEvent() { return myAccumulatedEvent; @@ -53,11 +55,13 @@ public abstract class PomTransactionBase implements PomTransaction{ @Nullable public abstract PomModelEvent runInner() throws IncorrectOperationException; + @NotNull @Override public PsiElement getChangeScope() { return myScope; } + @NotNull @Override public PomModelAspect getTransactionAspect() { return myAspect; diff --git a/platform/core-impl/src/com/intellij/openapi/application/TransactionGuardImpl.java b/platform/core-impl/src/com/intellij/openapi/application/TransactionGuardImpl.java index 2c40b8549527..671f56094a74 100644 --- a/platform/core-impl/src/com/intellij/openapi/application/TransactionGuardImpl.java +++ b/platform/core-impl/src/com/intellij/openapi/application/TransactionGuardImpl.java @@ -36,24 +36,40 @@ public class TransactionGuardImpl extends TransactionGuard { private final Queue myQueue = new LinkedBlockingQueue(); private final Set myMergeableKinds = ContainerUtil.newHashSet(); private String myTransactionStartTrace; + private ModalityState myTransactionModality; @Override @NotNull public AccessToken startSynchronousTransaction(@NotNull TransactionKind kind) throws IllegalStateException { - ApplicationManager.getApplication().assertIsDispatchThread(); - if (myTransactionStartTrace != null) { - if (!myMergeableKinds.contains(kind) && !ApplicationManager.getApplication().isUnitTestMode()) { - // please assign exceptions that occur here to Peter - LOG.error("Nested transactions are not allowed, see FAQ in TransactionGuard class javadoc. Transaction start trace is in attachment. Kind is " + kind, - new Attachment("trace.txt", myTransactionStartTrace)); + ModalityState modality = ModalityState.current(); + if (isInsideTransaction()) { + if (myTransactionModality == modality) { + return AccessToken.EMPTY_ACCESS_TOKEN; } + + if (myMergeableKinds.contains(kind)) { + final ModalityState prev = myTransactionModality; + myTransactionModality = modality; + return new AccessToken() { + @Override + public void finish() { + myTransactionModality = prev; + } + }; + } + + // please assign exceptions that occur here to Peter + LOG.error("Nested transactions are not allowed, see FAQ in TransactionGuard class javadoc. Transaction start trace is in attachment. Kind is " + kind, + new Attachment("trace.txt", myTransactionStartTrace)); return AccessToken.EMPTY_ACCESS_TOKEN; } + myTransactionModality = modality; myTransactionStartTrace = DebugUtil.currentStackTrace(); return new AccessToken() { @Override public void finish() { myTransactionStartTrace = null; + myTransactionModality = null; if (!myQueue.isEmpty()) { pollQueueLater(); } @@ -87,8 +103,7 @@ public class TransactionGuardImpl extends TransactionGuard { } } - @Override - public boolean isInsideTransaction() { + private boolean isInsideTransaction() { ApplicationManager.getApplication().assertIsDispatchThread(); return myTransactionStartTrace != null; } @@ -144,11 +159,18 @@ public class TransactionGuardImpl extends TransactionGuard { }; } + @Override + public void assertInsideTransaction(boolean transactionRequired, @NotNull String errorMessage) { + if (transactionRequired != isInsideTransaction()) { + LOG.error(errorMessage); + } + } + @Override public void submitTransactionAndWait(@NotNull TransactionKind kind, @NotNull final Runnable transaction) throws ProcessCanceledException { Application app = ApplicationManager.getApplication(); if (app.isDispatchThread()) { - if (!canRunTransactionNow(kind)) { + if (!canRunTransactionNow(kind) && myTransactionModality != ModalityState.current()) { throw new AssertionError("Cannot run submitTransactionAndWait from another transaction, kind " + kind + " is not allowed"); } runSyncTransaction(kind, transaction); diff --git a/platform/editor-ui-api/src/com/intellij/openapi/actionSystem/AnAction.java b/platform/editor-ui-api/src/com/intellij/openapi/actionSystem/AnAction.java index 5cd2c2148ac6..dad449da8ba8 100644 --- a/platform/editor-ui-api/src/com/intellij/openapi/actionSystem/AnAction.java +++ b/platform/editor-ui-api/src/com/intellij/openapi/actionSystem/AnAction.java @@ -279,7 +279,7 @@ public abstract class AnAction implements PossiblyDumbAware { protected void setShortcutSet(ShortcutSet shortcutSet) { if (myIsGlobal && myShortcutSet != shortcutSet) { - LOG.error("Shortcuts of global AnActions should not be changed outside of KeymapManager"); + LOG.warn("Shortcuts of global AnActions should not be changed outside of KeymapManager", new Throwable()); } myShortcutSet = shortcutSet; } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/editorActions/EndHandler.java b/platform/lang-impl/src/com/intellij/codeInsight/editorActions/EndHandler.java index d43e47dfb3cb..72a64b2b49c4 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/editorActions/EndHandler.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/editorActions/EndHandler.java @@ -94,9 +94,7 @@ public class EndHandler extends EditorActionHandler { // here just as a boolean value holder due to requirement to declare variable used from inner class as final. final AtomicBoolean stopProcessing = new AtomicBoolean(true); - TransactionGuard guard = TransactionGuard.getInstance(); - // sometimes this handler is invoked from other actions, then we're already inside a transaction - try (AccessToken ignore = guard.isInsideTransaction() ? null : guard.startSynchronousTransaction(TransactionKind.TEXT_EDITING)) { + try (AccessToken ignore = TransactionGuard.getInstance().startSynchronousTransaction(TransactionKind.TEXT_EDITING)) { PsiDocumentManager.getInstance(project).commitAllDocuments(); ApplicationManager.getApplication().runWriteAction(() -> { CodeStyleManager styleManager = CodeStyleManager.getInstance(project); diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionTreeUpdater.java b/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionTreeUpdater.java index 19cb4d24288d..3eaac402798a 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionTreeUpdater.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionTreeUpdater.java @@ -20,8 +20,6 @@ import com.intellij.util.ui.update.MergingUpdateQueue; import com.intellij.util.ui.update.Update; import javax.swing.tree.DefaultTreeModel; -import javax.swing.tree.TreeNode; -import javax.swing.tree.TreePath; import java.util.concurrent.atomic.AtomicBoolean; /** @@ -34,7 +32,7 @@ public class InspectionTreeUpdater { public InspectionTreeUpdater(InspectionResultsView view) { myView = view; - myUpdateQueue = new MergingUpdateQueue("InspectionView", 100, true, view); + myUpdateQueue = new MergingUpdateQueue("InspectionView", 100, true, view, view); } public void updateWithPreviewPanel() { diff --git a/platform/lang-impl/src/com/intellij/refactoring/changeSignature/ChangeSignatureProcessorBase.java b/platform/lang-impl/src/com/intellij/refactoring/changeSignature/ChangeSignatureProcessorBase.java index 4bf5cd412a3c..d3c0f6a202bc 100644 --- a/platform/lang-impl/src/com/intellij/refactoring/changeSignature/ChangeSignatureProcessorBase.java +++ b/platform/lang-impl/src/com/intellij/refactoring/changeSignature/ChangeSignatureProcessorBase.java @@ -22,6 +22,7 @@ import com.intellij.openapi.command.undo.UndoManager; import com.intellij.openapi.command.undo.UndoableAction; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Ref; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiManager; import com.intellij.refactoring.BaseRefactoringProcessor; @@ -36,15 +37,13 @@ import com.intellij.refactoring.util.MoveRenameUsageInfo; import com.intellij.usageView.UsageInfo; import com.intellij.util.IncorrectOperationException; import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.containers.MultiMap; import com.intellij.util.containers.hash.HashMap; import com.intellij.util.containers.hash.HashSet; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; /** * @author Maxim.Medvedev @@ -71,23 +70,41 @@ public abstract class ChangeSignatureProcessorBase extends BaseRefactoringProces @Override @NotNull protected UsageInfo[] findUsages() { - List infos = new ArrayList(); + return findUsages(myChangeInfo); + } + public static void collectConflictsFromExtensions(@NotNull Ref refUsages, + MultiMap conflictDescriptions, + ChangeInfo changeInfo) { + for (ChangeSignatureUsageProcessor usageProcessor : ChangeSignatureUsageProcessor.EP_NAME.getExtensions()) { + final MultiMap conflicts = usageProcessor.findConflicts(changeInfo, refUsages); + for (PsiElement key : conflicts.keySet()) { + Collection collection = conflictDescriptions.get(key); + if (collection.isEmpty()) collection = new com.intellij.util.containers.HashSet(); + collection.addAll(conflicts.get(key)); + conflictDescriptions.put(key, collection); + } + } + } + + @NotNull + public static UsageInfo[] findUsages(ChangeInfo changeInfo) { + List infos = new ArrayList(); final ChangeSignatureUsageProcessor[] processors = ChangeSignatureUsageProcessor.EP_NAME.getExtensions(); for (ChangeSignatureUsageProcessor processor : processors) { - ContainerUtil.addAll(infos, processor.findUsages(myChangeInfo)); + ContainerUtil.addAll(infos, processor.findUsages(changeInfo)); } infos = filterUsages(infos); return infos.toArray(new UsageInfo[infos.size()]); } - protected List filterUsages(List infos) { + protected static List filterUsages(List infos) { Map moveRenameInfos = new HashMap(); Set usedElements = new HashSet(); List result = new ArrayList(infos.size() / 2); for (UsageInfo info : infos) { - LOG.assertTrue(info != null, getClass()); + LOG.assertTrue(info != null); PsiElement element = info.getElement(); if (info instanceof MoveRenameUsageInfo) { if (usedElements.contains(element)) continue; @@ -139,14 +156,15 @@ public abstract class ChangeSignatureProcessorBase extends BaseRefactoringProces @Override protected void performRefactoring(@NotNull UsageInfo[] usages) { RefactoringTransaction transaction = getTransaction(); - final RefactoringElementListener elementListener = transaction == null ? null : transaction.getElementListener(myChangeInfo.getMethod()); - final String fqn = CopyReferenceAction.elementToFqn(myChangeInfo.getMethod()); + final ChangeInfo changeInfo = myChangeInfo; + final RefactoringElementListener elementListener = transaction == null ? null : transaction.getElementListener(changeInfo.getMethod()); + final String fqn = CopyReferenceAction.elementToFqn(changeInfo.getMethod()); if (fqn != null) { UndoableAction action = new BasicUndoableAction() { @Override public void undo() { if (elementListener instanceof UndoRefactoringElementListener) { - ((UndoRefactoringElementListener)elementListener).undoElementMovedOrRenamed(myChangeInfo.getMethod(), fqn); + ((UndoRefactoringElementListener)elementListener).undoElementMovedOrRenamed(changeInfo.getMethod(), fqn); } } @@ -157,44 +175,10 @@ public abstract class ChangeSignatureProcessorBase extends BaseRefactoringProces UndoManager.getInstance(myProject).undoableActionPerformed(action); } try { - final ChangeSignatureUsageProcessor[] processors = ChangeSignatureUsageProcessor.EP_NAME.getExtensions(); - - final ResolveSnapshotProvider resolveSnapshotProvider = myChangeInfo.isParameterNamesChanged() ? - VariableInplaceRenamer.INSTANCE.forLanguage(myChangeInfo.getMethod().getLanguage()) : null; - final List snapshots = new ArrayList(); - for (ChangeSignatureUsageProcessor processor : processors) { - if (resolveSnapshotProvider != null) { - processor.registerConflictResolvers(snapshots, resolveSnapshotProvider, usages, myChangeInfo); - } - } - - for (UsageInfo usage : usages) { - for (ChangeSignatureUsageProcessor processor : processors) { - if (processor.processUsage(myChangeInfo, usage, true, usages)) break; - } - } - - LOG.assertTrue(myChangeInfo.getMethod().isValid()); - for (ChangeSignatureUsageProcessor processor : processors) { - if (processor.processPrimaryMethod(myChangeInfo)) break; - } - - for (UsageInfo usage : usages) { - for (ChangeSignatureUsageProcessor processor : processors) { - if (processor.processUsage(myChangeInfo, usage, false, usages)) break; - } - } - - if (!snapshots.isEmpty()) { - for (ParameterInfo parameterInfo : myChangeInfo.getNewParameters()) { - for (ResolveSnapshotProvider.ResolveSnapshot snapshot : snapshots) { - snapshot.apply(parameterInfo.getName()); - } - } - } - final PsiElement method = myChangeInfo.getMethod(); + doChangeSignature(changeInfo, usages); + final PsiElement method = changeInfo.getMethod(); LOG.assertTrue(method.isValid()); - if (elementListener != null && myChangeInfo.isNameChanged()) { + if (elementListener != null && changeInfo.isNameChanged()) { elementListener.elementRenamed(method); } } @@ -203,6 +187,44 @@ public abstract class ChangeSignatureProcessorBase extends BaseRefactoringProces } } + public static void doChangeSignature(ChangeInfo changeInfo, @NotNull UsageInfo[] usages) { + final ChangeSignatureUsageProcessor[] processors = ChangeSignatureUsageProcessor.EP_NAME.getExtensions(); + + final ResolveSnapshotProvider resolveSnapshotProvider = changeInfo.isParameterNamesChanged() ? + VariableInplaceRenamer.INSTANCE.forLanguage(changeInfo.getMethod().getLanguage()) : null; + final List snapshots = new ArrayList(); + for (ChangeSignatureUsageProcessor processor : processors) { + if (resolveSnapshotProvider != null) { + processor.registerConflictResolvers(snapshots, resolveSnapshotProvider, usages, changeInfo); + } + } + + for (UsageInfo usage : usages) { + for (ChangeSignatureUsageProcessor processor : processors) { + if (processor.processUsage(changeInfo, usage, true, usages)) break; + } + } + + LOG.assertTrue(changeInfo.getMethod().isValid()); + for (ChangeSignatureUsageProcessor processor : processors) { + if (processor.processPrimaryMethod(changeInfo)) break; + } + + for (UsageInfo usage : usages) { + for (ChangeSignatureUsageProcessor processor : processors) { + if (processor.processUsage(changeInfo, usage, false, usages)) break; + } + } + + if (!snapshots.isEmpty()) { + for (ParameterInfo parameterInfo : changeInfo.getNewParameters()) { + for (ResolveSnapshotProvider.ResolveSnapshot snapshot : snapshots) { + snapshot.apply(parameterInfo.getName()); + } + } + } + } + @Override protected String getCommandName() { return RefactoringBundle.message("changing.signature.of.0", DescriptiveNameUtil.getDescriptiveName(myChangeInfo.getMethod())); diff --git a/platform/platform-api/src/com/intellij/openapi/ui/DialogWrapper.java b/platform/platform-api/src/com/intellij/openapi/ui/DialogWrapper.java index b39ef3d91bb1..6c5da11ab3b3 100644 --- a/platform/platform-api/src/com/intellij/openapi/ui/DialogWrapper.java +++ b/platform/platform-api/src/com/intellij/openapi/ui/DialogWrapper.java @@ -1644,9 +1644,8 @@ public abstract class DialogWrapper { if (ApplicationManager.getApplication().isWriteAccessAllowed()) { LOG.error("Project-modal dialogs should not be shown under a write action."); } - if (TransactionGuard.getInstance().isInsideTransaction()) { - LOG.error("Project-modal dialogs should not be shown inside a transaction. See TransactionGuard documentation."); - } + TransactionGuard.getInstance().assertInsideTransaction( + false, "Project-modal dialogs should not be shown inside a transaction. See TransactionGuard documentation."); } final AsyncResult result = new AsyncResult(); diff --git a/platform/platform-api/src/com/intellij/ui/SearchTextField.java b/platform/platform-api/src/com/intellij/ui/SearchTextField.java index 58fbfd79909f..4540d2ee1fbb 100644 --- a/platform/platform-api/src/com/intellij/ui/SearchTextField.java +++ b/platform/platform-api/src/com/intellij/ui/SearchTextField.java @@ -17,8 +17,8 @@ package com.intellij.ui; import com.intellij.icons.AllIcons; import com.intellij.openapi.actionSystem.ActionManager; -import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.CommonShortcuts; +import com.intellij.openapi.actionSystem.EmptyAction; import com.intellij.openapi.actionSystem.IdeActions; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.ui.JBMenuItem; @@ -203,10 +203,7 @@ public class SearchTextField extends JPanel { if (ApplicationManager.getApplication() != null) { //tests final ActionManager actionManager = ActionManager.getInstance(); if (actionManager != null) { - final AnAction clearTextAction = actionManager.getAction(IdeActions.ACTION_CLEAR_TEXT); - if (clearTextAction.getShortcutSet().getShortcuts().length == 0) { - clearTextAction.registerCustomShortcutSet(CommonShortcuts.ESCAPE, this); - } + EmptyAction.registerWithShortcutSet(IdeActions.ACTION_CLEAR_TEXT, CommonShortcuts.ESCAPE, this); } } } diff --git a/platform/platform-api/src/com/intellij/ui/TableToolbarDecorator.java b/platform/platform-api/src/com/intellij/ui/TableToolbarDecorator.java index af62010bc1c0..2f6155cf2b8e 100644 --- a/platform/platform-api/src/com/intellij/ui/TableToolbarDecorator.java +++ b/platform/platform-api/src/com/intellij/ui/TableToolbarDecorator.java @@ -15,6 +15,7 @@ */ package com.intellij.ui; +import com.intellij.util.ArrayUtil; import com.intellij.util.ui.EditableModel; import com.intellij.util.ui.ElementProducer; import com.intellij.util.ui.ListTableModel; @@ -27,6 +28,7 @@ import javax.swing.event.ListSelectionListener; import java.awt.*; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; +import java.util.Arrays; /** * @author Konstantin Bulenkov @@ -149,46 +151,41 @@ class TableToolbarDecorator extends ToolbarDecorator { } }; - myUpAction = new AnActionButtonRunnable() { + class MoveRunnable implements AnActionButtonRunnable { + final int delta; + + MoveRunnable(int delta) { + this.delta = delta; + } + @Override - public void run(AnActionButton button) { - final int row = table.getEditingRow(); - final int col = table.getEditingColumn(); + public void run(AnActionButton button) { + int row = table.getEditingRow(); + int col = table.getEditingColumn(); TableUtil.stopEditing(table); - final int[] indexes = table.getSelectedRows(); - for (int index : indexes) { - if (0 < index && index < table.getModel().getRowCount()) { - tableModel.exchangeRows(index, index - 1); - table.setRowSelectionInterval(index - 1, index - 1); - } - } + int[] idx = table.getSelectedRows(); + Arrays.sort(idx); + if (delta > 0) { + idx = ArrayUtil.reverseArray(idx); + } + + if (idx.length == 0) return; + if (idx[0] + delta < 0) return; + if (idx[idx.length - 1] + delta > table.getModel().getRowCount()) return; + + for (int i = 0; i < idx.length; i++) { + tableModel.exchangeRows(idx[i], idx[i] + delta); + idx[i] += delta; + } + TableUtil.selectRows(table, idx); table.requestFocus(); if (row > 0 && col != -1) { table.editCellAt(row - 1, col); } } - }; - - myDownAction = new AnActionButtonRunnable() { - @Override - public void run(AnActionButton button) { - final int row = table.getEditingRow(); - final int col = table.getEditingColumn(); - - TableUtil.stopEditing(table); - final int[] indexes = table.getSelectedRows(); - for (int index : indexes) { - if (0 <= index && index < table.getModel().getRowCount() - 1) { - tableModel.exchangeRows(index, index + 1); - table.setRowSelectionInterval(index + 1, index + 1); - } - } - table.requestFocus(); - if (row < table.getRowCount() - 1 && col != -1) { - table.editCellAt(row + 1, col); - } - } - }; + } + myUpAction = new MoveRunnable(-1); + myDownAction = new MoveRunnable(1); } @Override diff --git a/platform/platform-impl/src/com/intellij/openapi/application/impl/ApplicationImpl.java b/platform/platform-impl/src/com/intellij/openapi/application/impl/ApplicationImpl.java index 0578a0550dc9..beee7d02aef0 100644 --- a/platform/platform-impl/src/com/intellij/openapi/application/impl/ApplicationImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/application/impl/ApplicationImpl.java @@ -1229,9 +1229,9 @@ public class ApplicationImpl extends PlatformComponentManagerImpl implements App private void startWrite(/*@NotNull*/ Class clazz) { assertIsDispatchThread(getStatus(), "Write access is allowed from event dispatch thread only"); HeavyProcessLatch.INSTANCE.stopThreadPrioritizing(); // let non-cancellable read actions complete faster, if present - if (!TransactionGuard.getInstance().isInsideTransaction() && Registry.is("ide.require.transaction.for.model.changes", false)) { - // please assign exceptions that occur here to Peter - LOG.error("Write access is allowed from model transactions only, see TransactionGuard documentation for details"); + if (Registry.is("ide.require.transaction.for.model.changes", false)) { + TransactionGuard.getInstance().assertInsideTransaction( + true, "Write access is allowed from model transactions only, see TransactionGuard documentation for details"); } boolean writeActionPending = myWriteActionPending; if (gatherWriteActionStatistics && myWriteActionsStack.isEmpty() && !writeActionPending) { diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/welcomeScreen/ChangeProjectIconForm.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/welcomeScreen/ChangeProjectIconForm.java index d5689502d50f..e278af001833 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/welcomeScreen/ChangeProjectIconForm.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/welcomeScreen/ChangeProjectIconForm.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 JetBrains s.r.o. + * Copyright 2000-2016 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. @@ -122,7 +122,7 @@ public class ChangeProjectIconForm { pathToIcon = files[0]; } } - catch (Exception e1) { + catch (Exception ignore) { } } } diff --git a/platform/platform-impl/src/com/intellij/util/ui/table/JBListTable.java b/platform/platform-impl/src/com/intellij/util/ui/table/JBListTable.java index b491438ac5a8..b0a3d46eabc8 100644 --- a/platform/platform-impl/src/com/intellij/util/ui/table/JBListTable.java +++ b/platform/platform-impl/src/com/intellij/util/ui/table/JBListTable.java @@ -43,6 +43,7 @@ import javax.swing.table.TableColumn; import javax.swing.table.TableModel; import java.awt.*; import java.awt.event.*; +import java.util.EventObject; import java.util.List; import static java.awt.event.KeyEvent.*; @@ -142,6 +143,14 @@ public abstract class JBListTable { myEditor = editor; } + @Override + public boolean isCellEditable(EventObject e) { + if (e instanceof MouseEvent && UIUtil.isSelectionButtonDown((MouseEvent)e)) { + return false; + } + return super.isCellEditable(e); + } + @Override public Component getTableCellEditorComponent(final JTable table, Object value, boolean isSelected, final int row, int column) { final JPanel p = new JPanel(new BorderLayout()) { diff --git a/platform/platform-resources/src/META-INF/JsonPlugin.xml b/platform/platform-resources/src/META-INF/JsonPlugin.xml index cf8cffb68459..c553ac02ae7b 100644 --- a/platform/platform-resources/src/META-INF/JsonPlugin.xml +++ b/platform/platform-resources/src/META-INF/JsonPlugin.xml @@ -62,7 +62,7 @@ - diff --git a/platform/platform-resources/src/idea/Keymap_Default.xml b/platform/platform-resources/src/idea/Keymap_Default.xml index d55e485180aa..a843d5c6cf53 100644 --- a/platform/platform-resources/src/idea/Keymap_Default.xml +++ b/platform/platform-resources/src/idea/Keymap_Default.xml @@ -15,9 +15,6 @@ - diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/history/impl/VcsSelectionHistoryDialog.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/history/impl/VcsSelectionHistoryDialog.java index ea47c68786dd..661586bc8cd4 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/history/impl/VcsSelectionHistoryDialog.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/history/impl/VcsSelectionHistoryDialog.java @@ -216,6 +216,10 @@ public class VcsSelectionHistoryDialog extends FrameWrapper implements DataProvi popupActions.add(ActionManager.getInstance().getAction(VcsActions.ACTION_COPY_REVISION_NUMBER)); PopupHandler.installPopupHandler(myList, popupActions, ActionPlaces.UPDATE_POPUP, ActionManager.getInstance()); + for (AnAction action : popupActions.getChildren(null)) { + action.registerCustomShortcutSet(action.getShortcutSet(), mySplitter); + } + setTitle(title); setComponent(mySplitter); setPreferredFocusedComponent(myList); @@ -241,7 +245,7 @@ public class VcsSelectionHistoryDialog extends FrameWrapper implements DataProvi return myCachedContents.getContentOf(revision); } - private void loadContentsFor(final VcsFileRevision[] revisions) throws VcsException { + private void loadContentsFor(final VcsFileRevision... revisions) throws VcsException { myCachedContents.loadContentsFor(revisions); } @@ -426,6 +430,8 @@ public class VcsSelectionHistoryDialog extends FrameWrapper implements DataProvi } private void ensureBlocksCreated(int requiredIndex) throws VcsException { + loadContentsFor(myRevisions.get(requiredIndex)); + for (int i = 0; i <= requiredIndex; i++) { if (myBlocks.get(i) == null) { myBlocks.set(i, createBlock(i)); @@ -460,6 +466,7 @@ public class VcsSelectionHistoryDialog extends FrameWrapper implements DataProvi private class MyDiffAction extends DumbAwareAction { public MyDiffAction() { super(VcsBundle.message("action.name.compare"), VcsBundle.message("action.description.compare"), AllIcons.Actions.Diff); + setShortcutSet(CommonShortcuts.getDiff()); } public void update(final AnActionEvent e) { @@ -489,6 +496,7 @@ public class VcsSelectionHistoryDialog extends FrameWrapper implements DataProvi super(VcsBundle.message("show.diff.with.local.action.text"), VcsBundle.message("show.diff.with.local.action.description"), AllIcons.Actions.DiffWithCurrent); + setShortcutSet(ActionManager.getInstance().getAction("Vcs.ShowDiffWithLocal").getShortcutSet()); } public void update(final AnActionEvent e) { diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/InspectionGadgetsBundle.properties b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/InspectionGadgetsBundle.properties index c432dd0f0876..779ba44914f1 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/InspectionGadgetsBundle.properties +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/InspectionGadgetsBundle.properties @@ -1835,7 +1835,6 @@ overloaded.methods.with.same.number.parameters.option=Ignore overloaded me string.concatenation.in.format.call.display.name=String concatenation as argument to 'format()' call string.concatenation.in.format.call.problem.descriptor=#ref() call has a String concatenation argument #loc string.concatenation.in.format.call.quickfix=Replace concatenation with separate argument -string.concatenation.in.format.call.plural.quickfix=Replace concatenation with separate arguments string.concatenation.in.message.format.call.display.name=String concatenation as argument to 'MessageFormat.format()' call string.concatenation.in.message.format.call.problem.descriptor=String concatenation as argument to 'MessageFormat.format()' call #loc shift.out.of.range.quickfix=Replace ''{0}'' with ''{1}'' diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/StringConcatenationInFormatCallInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/StringConcatenationInFormatCallInspection.java index d228809e03b6..20213454d4dc 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/StringConcatenationInFormatCallInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/StringConcatenationInFormatCallInspection.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2012 Bas Leijdekkers + * Copyright 2010-2016 Bas Leijdekkers * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,15 +15,10 @@ */ package com.siyeh.ig.bugs; -import com.intellij.codeInspection.ProblemDescriptor; -import com.intellij.openapi.project.Project; import com.intellij.psi.*; -import com.intellij.util.IncorrectOperationException; import com.siyeh.InspectionGadgetsBundle; import com.siyeh.ig.BaseInspection; import com.siyeh.ig.BaseInspectionVisitor; -import com.siyeh.ig.InspectionGadgetsFix; -import com.siyeh.ig.PsiReplacementUtil; import com.siyeh.ig.psiutils.ExpressionUtils; import com.siyeh.ig.psiutils.FormatUtils; import org.jetbrains.annotations.Nls; @@ -44,67 +39,6 @@ public class StringConcatenationInFormatCallInspection extends BaseInspection { return InspectionGadgetsBundle.message("string.concatenation.in.format.call.problem.descriptor"); } - @Override - protected InspectionGadgetsFix buildFix(Object... infos) { - return new StringConcatenationInFormatCallFix(((Boolean)infos[0]).booleanValue()); - } - - private static class StringConcatenationInFormatCallFix extends InspectionGadgetsFix { - - - private final boolean myPlural; - - public StringConcatenationInFormatCallFix(boolean plural) { - myPlural = plural; - } - - @Override - @NotNull - public String getName() { - if (myPlural) { - return InspectionGadgetsBundle.message("string.concatenation.in.format.call.plural.quickfix"); - } - else { - return InspectionGadgetsBundle.message("string.concatenation.in.format.call.quickfix"); - } - } - - @NotNull - @Override - public String getFamilyName() { - return InspectionGadgetsBundle.message("string.concatenation.in.format.call.plural.quickfix"); - } - - @Override - protected void doFix(Project project, ProblemDescriptor descriptor) throws IncorrectOperationException { - final PsiElement element = descriptor.getPsiElement().getParent().getParent(); - if (!(element instanceof PsiMethodCallExpression)) { - return; - } - final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)element; - final PsiExpressionList argumentList = methodCallExpression.getArgumentList(); - final PsiExpression formatArgument = FormatUtils.getFormatArgument(argumentList); - if (!(formatArgument instanceof PsiPolyadicExpression)) { - return; - } - final PsiPolyadicExpression polyadicExpression = (PsiPolyadicExpression)formatArgument; - final StringBuilder newExpression = new StringBuilder(); - final PsiExpression[] operands = polyadicExpression.getOperands(); - for (PsiExpression operand : operands) { - if (operand instanceof PsiReferenceExpression) { - argumentList.add(operand); - continue; - } - final PsiJavaToken token = polyadicExpression.getTokenBeforeOperand(operand); - if (token != null) { - newExpression.append(token.getText()); - } - newExpression.append(operand.getText()); - } - PsiReplacementUtil.replaceExpression(polyadicExpression, newExpression.toString()); - } - } - @Override public BaseInspectionVisitor buildVisitor() { return new StringConcatenationInFormatCallVisitor(); @@ -141,7 +75,7 @@ public class StringConcatenationInFormatCallInspection extends BaseInspection { if (count == 0) { return; } - registerMethodCallError(expression, Boolean.valueOf(count > 1)); + registerMethodCallError(expression); } } } diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/UtilityClassCanBeEnumInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/UtilityClassCanBeEnumInspection.java index 7b5416fa6975..cfe033dd2498 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/UtilityClassCanBeEnumInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/UtilityClassCanBeEnumInspection.java @@ -72,6 +72,9 @@ public class UtilityClassCanBeEnumInspection extends BaseInspection { @Override protected void doFix(Project project, ProblemDescriptor descriptor) { final PsiElement element = descriptor.getPsiElement(); + if (!PsiUtil.isLanguageLevel5OrHigher(element)) { + return; + } final PsiElement parent = element.getParent(); if (!(parent instanceof PsiClass)) { return; diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/fixes/AddThisQualifierFix.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/fixes/AddThisQualifierFix.java index a1f95383b762..3e83acf92c1b 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/fixes/AddThisQualifierFix.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/fixes/AddThisQualifierFix.java @@ -1,5 +1,5 @@ /* - * Copyright 2011 Bas Leijdekkers + * Copyright 2011-2016 Bas Leijdekkers * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -74,7 +74,11 @@ public class AddThisQualifierFix extends InspectionGadgetsFix { return; } } - newExpression = containingClass.getQualifiedName() + ".this." + expression.getText(); + final String qualifiedName = containingClass.getQualifiedName(); + if (qualifiedName == null) { + return; + } + newExpression = qualifiedName + ".this." + expression.getText(); } PsiReplacementUtil.replaceExpressionAndShorten(expression, newExpression); } diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/numeric/ImplicitNumericConversionInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/numeric/ImplicitNumericConversionInspection.java index 18e650160ade..92f21d4d0ce9 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/numeric/ImplicitNumericConversionInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/numeric/ImplicitNumericConversionInspection.java @@ -1,5 +1,5 @@ /* - * Copyright 2003-2015 Dave Griffith, Bas Leijdekkers + * Copyright 2003-2016 Dave Griffith, Bas Leijdekkers * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -156,27 +156,35 @@ public class ImplicitNumericConversionInspection extends BaseInspection { if (expressionType == null) { return null; } + final String text = expression.getText(); if (expressionType.equals(PsiType.INT) && expectedType.equals(PsiType.LONG)) { - return expression.getText() + 'L'; + return text + 'L'; } if (expressionType.equals(PsiType.INT) && expectedType.equals(PsiType.FLOAT)) { - return expression.getText() + ".0F"; + if (!isDecimalLiteral(text)) { + return null; + } + return text + ".0F"; } if (expressionType.equals(PsiType.INT) && expectedType.equals(PsiType.DOUBLE)) { - return expression.getText() + ".0"; + if (!isDecimalLiteral(text)) { + return null; + } + return text + ".0"; } if (expressionType.equals(PsiType.LONG) && expectedType.equals(PsiType.FLOAT)) { - final String text = expression.getText(); - final int length = text.length(); - return text.substring(0, length - 1) + ".0F"; + if (!isDecimalLiteral(text)) { + return null; + } + return text.substring(0, text.length() - 1) + ".0F"; } if (expressionType.equals(PsiType.LONG) && expectedType.equals(PsiType.DOUBLE)) { - final String text = expression.getText(); - final int length = text.length(); - return text.substring(0, length - 1) + ".0"; + if (!isDecimalLiteral(text)) { + return null; + } + return text.substring(0, text.length() - 1) + ".0"; } if (expressionType.equals(PsiType.DOUBLE) && expectedType.equals(PsiType.FLOAT)) { - final String text = expression.getText(); final int length = text.length(); if (text.charAt(length - 1) == 'd' || text.charAt(length - 1) == 'D') { return text.substring(0, length - 1) + 'F'; @@ -186,13 +194,17 @@ public class ImplicitNumericConversionInspection extends BaseInspection { } } if (expressionType.equals(PsiType.FLOAT) && expectedType.equals(PsiType.DOUBLE)) { - final String text = expression.getText(); final int length = text.length(); return text.substring(0, length - 1); } return null; } + private static boolean isDecimalLiteral(String text) { + // should not be binary, octal or hexadecimal: 0b101, 077, 0xFF + return text.length() > 0 && text.charAt(0) != '0'; + } + private static boolean isNegatedLiteral(PsiExpression expression) { if (!(expression instanceof PsiPrefixExpression)) { return false; diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/style/UnqualifiedFieldAccessInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/style/UnqualifiedFieldAccessInspection.java index 57fe539de724..4746a0aa1448 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/style/UnqualifiedFieldAccessInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/style/UnqualifiedFieldAccessInspection.java @@ -1,5 +1,5 @@ /* - * Copyright 2006-2013 Bas Leijdekkers + * Copyright 2006-2016 Bas Leijdekkers * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,6 +18,7 @@ package com.siyeh.ig.style; import com.intellij.codeInspection.CleanupLocalInspectionTool; import com.intellij.psi.*; import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.util.PsiUtil; import com.siyeh.InspectionGadgetsBundle; import com.siyeh.ig.BaseInspection; import com.siyeh.ig.BaseInspectionVisitor; @@ -71,10 +72,13 @@ public class UnqualifiedFieldAccessInspection extends BaseInspection implements return; } final PsiClass fieldClass = field.getContainingClass(); - if (fieldClass instanceof PsiAnonymousClass) { + if (fieldClass == null) { + return; + } + if (PsiUtil.isLocalOrAnonymousClass(fieldClass)) { final PsiClass expressionClass = PsiTreeUtil.getParentOfType(expression, PsiClass.class); if (expressionClass != null && !expressionClass.equals(fieldClass)) { - // qualified this expression not possible for anonymous class + // qualified this expression not possible for anonymous or local class return; } } diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/style/UnqualifiedMethodAccessInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/style/UnqualifiedMethodAccessInspection.java index 85aa3ee7c603..f7086af1cd76 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/style/UnqualifiedMethodAccessInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/style/UnqualifiedMethodAccessInspection.java @@ -1,5 +1,5 @@ /* - * Copyright 2006-2012 Bas Leijdekkers + * Copyright 2006-2016 Bas Leijdekkers * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,6 +17,8 @@ package com.siyeh.ig.style; import com.intellij.codeInspection.CleanupLocalInspectionTool; import com.intellij.psi.*; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.util.PsiUtil; import com.siyeh.InspectionGadgetsBundle; import com.siyeh.ig.BaseInspection; import com.siyeh.ig.BaseInspectionVisitor; @@ -70,9 +72,16 @@ public class UnqualifiedMethodAccessInspection extends BaseInspection implements return; } final PsiClass containingClass = method.getContainingClass(); - if (containingClass instanceof PsiAnonymousClass) { + if (containingClass == null) { return; } + if (PsiUtil.isLocalOrAnonymousClass(containingClass)) { + final PsiClass expressionClass = PsiTreeUtil.getParentOfType(expression, PsiClass.class); + if (expressionClass == null || !expressionClass.equals(containingClass)) { + // qualified this expression not possible for anonymous or local class + return; + } + } registerError(expression); } } diff --git a/plugins/InspectionGadgets/test/com/siyeh/igfixes/numeric/implicit_numeric_conversion/HexadecimalLiteral.after.java b/plugins/InspectionGadgets/test/com/siyeh/igfixes/numeric/implicit_numeric_conversion/HexadecimalLiteral.after.java new file mode 100644 index 000000000000..53d289fafc20 --- /dev/null +++ b/plugins/InspectionGadgets/test/com/siyeh/igfixes/numeric/implicit_numeric_conversion/HexadecimalLiteral.after.java @@ -0,0 +1,6 @@ +class HexadecimalLiteral { + + void a() { + double value = (double) 0xFF; + } +} \ No newline at end of file diff --git a/plugins/InspectionGadgets/test/com/siyeh/igfixes/numeric/implicit_numeric_conversion/HexadecimalLiteral.java b/plugins/InspectionGadgets/test/com/siyeh/igfixes/numeric/implicit_numeric_conversion/HexadecimalLiteral.java new file mode 100644 index 000000000000..f31ee9dbec5e --- /dev/null +++ b/plugins/InspectionGadgets/test/com/siyeh/igfixes/numeric/implicit_numeric_conversion/HexadecimalLiteral.java @@ -0,0 +1,6 @@ +class HexadecimalLiteral { + + void a() { + double value = 0xFF; + } +} \ No newline at end of file diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/string_concatenation_in_format_call/StringConcatenationInFormatCall.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/string_concatenation_in_format_call/StringConcatenationInFormatCall.java new file mode 100644 index 000000000000..8171071df705 --- /dev/null +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/string_concatenation_in_format_call/StringConcatenationInFormatCall.java @@ -0,0 +1,11 @@ +package com.siyeh.igtest.bugs.string_concatenation_in_format_call; + + + +public class StringConcatenationInFormatCall { + + void foo(int i) { + String.format("a" + "b" + i); + String.format("c: " + i); + } +} \ No newline at end of file diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/string_concatenation_in_format_call/StringContenationInFormatCall.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/string_concatenation_in_format_call/StringContenationInFormatCall.java deleted file mode 100644 index f192146ff6ff..000000000000 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/string_concatenation_in_format_call/StringContenationInFormatCall.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.siyeh.igtest.bugs.string_concatenation_in_format_call; - - - -public class StringContenationInFormatCall { - - void foo(int i) { - String.format("a" + "b" + i); - String.format("c: " + i); - } -} \ No newline at end of file diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/string_concatenation_in_format_call/expected.xml b/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/string_concatenation_in_format_call/expected.xml deleted file mode 100644 index a41bfb82f655..000000000000 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/string_concatenation_in_format_call/expected.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - StringContenationInFormatCall.java - 8 - String concatenation as argument to 'format()' call - <code>format()</code> call has a String concatenation argument #loc - - - - StringContenationInFormatCall.java - 9 - String concatenation as argument to 'format()' call - <code>format()</code> call has a String concatenation argument #loc - - \ No newline at end of file diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/style/unqualified_field_access/UnqualifiedFieldAccess.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/style/unqualified_field_access/UnqualifiedFieldAccess.java index ac24026a8ae9..27ba03166372 100644 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/style/unqualified_field_access/UnqualifiedFieldAccess.java +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/style/unqualified_field_access/UnqualifiedFieldAccess.java @@ -5,8 +5,8 @@ public class UnqualifiedFieldAccess { private String field; public void x () { - field = "foofoo"; - final String s = String.valueOf(field.hashCode()); + field = "foofoo"; + final String s = String.valueOf(field.hashCode()); System.out.println(s); } @@ -21,6 +21,16 @@ public class UnqualifiedFieldAccess { }; } }; + class A { + int i; + void a() { + new Object() { + void b() { + System.out.println(i); + } + }; + } + } } void simpleAnonymous() { @@ -28,7 +38,7 @@ public class UnqualifiedFieldAccess { String s; void foo() { - System.out.println(s); + System.out.println(s); } }; } diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/style/unqualified_field_access/expected.xml b/plugins/InspectionGadgets/test/com/siyeh/igtest/style/unqualified_field_access/expected.xml deleted file mode 100644 index aa352acb6311..000000000000 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/style/unqualified_field_access/expected.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - UnqualifiedFieldAccess.java - 8 - Instance field access not qualified with 'this' - Instance field access <code>field</code> is not qualified with 'this' #loc - - - - UnqualifiedFieldAccess.java - 9 - Instance field access not qualified with 'this' - Instance field access <code>field</code> is not qualified with 'this' #loc - - - - UnqualifiedFieldAccess.java - 31 - Instance field access not qualified with 'this' - Instance field access <code>s</code> is not qualified with 'this' #loc - - \ No newline at end of file diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/style/unqualified_method_access/UnqualifiedMethodAccess.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/style/unqualified_method_access/UnqualifiedMethodAccess.java index 0e626f3fe31d..c4e1087bb011 100644 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/style/unqualified_method_access/UnqualifiedMethodAccess.java +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/style/unqualified_method_access/UnqualifiedMethodAccess.java @@ -9,11 +9,21 @@ public class UnqualifiedMethodAccess extends JPanel { void foo() {} void bar() { - foo(); + foo(); } void foo(String s) { this.foo(); + class A { + void a() { + a(); + new Object() { + void b() { + a(); + } + }; + } + } } void anonymous() { @@ -22,6 +32,7 @@ public class UnqualifiedMethodAccess extends JPanel { new Object() { void foo() { bar(); + foo(); } }; } diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/style/unqualified_method_access/expected.xml b/plugins/InspectionGadgets/test/com/siyeh/igtest/style/unqualified_method_access/expected.xml deleted file mode 100644 index a2183473e31a..000000000000 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/style/unqualified_method_access/expected.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - UnqualifiedMethodAccess.java - 12 - Instance method call not qualified with 'this' - Instance method call <code>foo</code> is not qualified with 'this' #loc - - \ No newline at end of file diff --git a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/bugs/StringConcatenationInFormatCallInspectionTest.java b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/bugs/StringConcatenationInFormatCallInspectionTest.java index 46c74708bec9..b3a979122a17 100644 --- a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/bugs/StringConcatenationInFormatCallInspectionTest.java +++ b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/bugs/StringConcatenationInFormatCallInspectionTest.java @@ -1,10 +1,33 @@ +/* + * Copyright 2000-2016 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.siyeh.ig.bugs; -import com.siyeh.ig.IGInspectionTestCase; +import com.intellij.codeInspection.InspectionProfileEntry; +import com.siyeh.ig.LightInspectionTestCase; +import org.jetbrains.annotations.Nullable; -public class StringConcatenationInFormatCallInspectionTest extends IGInspectionTestCase { +public class StringConcatenationInFormatCallInspectionTest extends LightInspectionTestCase { - public void test() throws Exception { - doTest("com/siyeh/igtest/bugs/string_concatenation_in_format_call", new StringConcatenationInFormatCallInspection()); + public void testStringConcatenationInFormatCall() { + doTest(); + } + + @Nullable + @Override + protected InspectionProfileEntry getInspection() { + return new StringConcatenationInFormatCallInspection(); } } \ No newline at end of file diff --git a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/numeric/ImplicitNumericConversionFixTest.java b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/numeric/ImplicitNumericConversionFixTest.java index f5487306fd74..b0cdec66a489 100644 --- a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/numeric/ImplicitNumericConversionFixTest.java +++ b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/numeric/ImplicitNumericConversionFixTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 JetBrains s.r.o. + * Copyright 2000-2016 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. @@ -24,9 +24,8 @@ import com.siyeh.ig.numeric.ImplicitNumericConversionInspection; */ public class ImplicitNumericConversionFixTest extends IGQuickFixesTestCase { - public void testOperatorAssignment() { - doTest(); - } + public void testOperatorAssignment() { doTest(); } + public void testHexadecimalLiteral() { doTest(); } @Override protected void setUp() throws Exception { diff --git a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/style/UnqualifiedFieldAccessInspectionTest.java b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/style/UnqualifiedFieldAccessInspectionTest.java index 07d99ddb4de4..45a14d03333b 100644 --- a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/style/UnqualifiedFieldAccessInspectionTest.java +++ b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/style/UnqualifiedFieldAccessInspectionTest.java @@ -1,10 +1,33 @@ +/* + * Copyright 2000-2016 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.siyeh.ig.style; -import com.siyeh.ig.IGInspectionTestCase; +import com.intellij.codeInspection.InspectionProfileEntry; +import com.siyeh.ig.LightInspectionTestCase; +import org.jetbrains.annotations.Nullable; -public class UnqualifiedFieldAccessInspectionTest extends IGInspectionTestCase { +public class UnqualifiedFieldAccessInspectionTest extends LightInspectionTestCase { - public void test() throws Exception { - doTest("com/siyeh/igtest/style/unqualified_field_access", new UnqualifiedFieldAccessInspection()); + public void testUnqualifiedFieldAccess() throws Exception { + doTest(); + } + + @Nullable + @Override + protected InspectionProfileEntry getInspection() { + return new UnqualifiedFieldAccessInspection(); } } \ No newline at end of file diff --git a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/style/UnqualifiedMethodAccessInspectionTest.java b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/style/UnqualifiedMethodAccessInspectionTest.java index 57128c4e694d..dc7db8157758 100644 --- a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/style/UnqualifiedMethodAccessInspectionTest.java +++ b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/style/UnqualifiedMethodAccessInspectionTest.java @@ -1,11 +1,33 @@ +/* + * Copyright 2000-2016 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.siyeh.ig.style; -import com.siyeh.ig.IGInspectionTestCase; +import com.intellij.codeInspection.InspectionProfileEntry; +import com.siyeh.ig.LightInspectionTestCase; +import org.jetbrains.annotations.Nullable; -public class UnqualifiedMethodAccessInspectionTest - extends IGInspectionTestCase { +public class UnqualifiedMethodAccessInspectionTest extends LightInspectionTestCase { - public void test() throws Exception { - doTest("com/siyeh/igtest/style/unqualified_method_access", new UnqualifiedMethodAccessInspection()); + public void testUnqualifiedMethodAccess() { + doTest(); + } + + @Nullable + @Override + protected InspectionProfileEntry getInspection() { + return new UnqualifiedMethodAccessInspection(); } } \ No newline at end of file diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/changeSignature/GrChangeSignatureProcessor.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/changeSignature/GrChangeSignatureProcessor.java index f6e340cc92da..0cab91db1a1c 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/changeSignature/GrChangeSignatureProcessor.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/changeSignature/GrChangeSignatureProcessor.java @@ -22,7 +22,6 @@ import com.intellij.openapi.util.Ref; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiMethod; import com.intellij.refactoring.changeSignature.ChangeSignatureProcessorBase; -import com.intellij.refactoring.changeSignature.ChangeSignatureUsageProcessor; import com.intellij.refactoring.changeSignature.ChangeSignatureViewDescriptor; import com.intellij.refactoring.rename.RenameUtil; import com.intellij.refactoring.ui.ConflictsDialog; @@ -33,7 +32,6 @@ import com.intellij.util.containers.MultiMap; import org.jetbrains.annotations.NotNull; import java.util.Arrays; -import java.util.Collection; import java.util.Set; /** @@ -68,15 +66,7 @@ public class GrChangeSignatureProcessor extends ChangeSignatureProcessorBase { @Override protected boolean preprocessUsages(@NotNull Ref refUsages) { MultiMap conflictDescriptions = new MultiMap(); - for (ChangeSignatureUsageProcessor usageProcessor : ChangeSignatureUsageProcessor.EP_NAME.getExtensions()) { - final MultiMap conflicts = usageProcessor.findConflicts(myChangeInfo, refUsages); - for (PsiElement key : conflicts.keySet()) { - Collection collection = conflictDescriptions.get(key); - if (collection.isEmpty()) collection = new HashSet(); - collection.addAll(conflicts.get(key)); - conflictDescriptions.put(key, collection); - } - } + collectConflictsFromExtensions(refUsages, conflictDescriptions, myChangeInfo); final UsageInfo[] usagesIn = refUsages.get(); RenameUtil.addConflictDescriptions(usagesIn, conflictDescriptions); diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/intentions/AddConstructorMatchingSuperTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/intentions/AddConstructorMatchingSuperTest.groovy index 7dc8e6ca6e83..9e7ea7a89f99 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/intentions/AddConstructorMatchingSuperTest.groovy +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/intentions/AddConstructorMatchingSuperTest.groovy @@ -34,6 +34,7 @@ public class AddConstructorMatchingSuperTest extends GrIntentionTestCase { void testGroovyToGroovy() { doTextTest('''\ +@interface Anno {} class Base { Base(int p, @Anno int x) throws Exception {} } @@ -41,6 +42,7 @@ class Base { class Derived extends Base { } ''', '''\ +@interface Anno {} class Base { Base(int p, @Anno int x) throws Exception {} } @@ -55,6 +57,7 @@ class Derived extends Base { void testJavaToGroovy() { myFixture.addClass('''\ +@interface Anno {} class Base { Base(int p, @Anno int x) throws Exception {} } diff --git a/python/helpers/pydev/_pydev_bundle/pydev_monkey.py b/python/helpers/pydev/_pydev_bundle/pydev_monkey.py index f692ece44586..a2f951996273 100644 --- a/python/helpers/pydev/_pydev_bundle/pydev_monkey.py +++ b/python/helpers/pydev/_pydev_bundle/pydev_monkey.py @@ -566,12 +566,10 @@ _UseNewThreadStartup = _NewThreadStartupWithTrace def _get_threading_modules_to_patch(): threading_modules_to_patch = [] - try: - import thread as _thread - threading_modules_to_patch.append(_thread) - except: - import _thread # @UnresolvedImport @Reimport - threading_modules_to_patch.append(_thread) + + from _pydev_imps._pydev_saved_modules import thread as _thread + threading_modules_to_patch.append(_thread) + return threading_modules_to_patch threading_modules_to_patch = _get_threading_modules_to_patch() diff --git a/python/helpers/pydev/_pydev_imps/_pydev_saved_modules.py b/python/helpers/pydev/_pydev_imps/_pydev_saved_modules.py index 16fe21bd25db..6ff3939d7b39 100644 --- a/python/helpers/pydev/_pydev_imps/_pydev_saved_modules.py +++ b/python/helpers/pydev/_pydev_imps/_pydev_saved_modules.py @@ -1,15 +1,8 @@ import sys -IS_PY2 = True -if sys.version_info[0] >= 3: - IS_PY2 = False +IS_PY2 = sys.version_info < (3,) import threading -if IS_PY2: - import thread -else: - import _thread as thread - import time import socket @@ -17,21 +10,14 @@ import socket import select if IS_PY2: + import thread import Queue as _queue -else: - import queue as _queue - -if IS_PY2: import xmlrpclib -else: - import xmlrpc.client as xmlrpclib - -if IS_PY2: import SimpleXMLRPCServer as _pydev_SimpleXMLRPCServer -else: - import xmlrpc.server as _pydev_SimpleXMLRPCServer - -if IS_PY2: import BaseHTTPServer else: + import _thread as thread + import queue as _queue + import xmlrpc.client as xmlrpclib + import xmlrpc.server as _pydev_SimpleXMLRPCServer import http.server as BaseHTTPServer \ No newline at end of file diff --git a/python/helpers/pydev/_pydevd_bundle/pydevd_comm.py b/python/helpers/pydev/_pydevd_bundle/pydevd_comm.py index 14fb74d11792..d3c12f164dbb 100644 --- a/python/helpers/pydev/_pydevd_bundle/pydevd_comm.py +++ b/python/helpers/pydev/_pydevd_bundle/pydevd_comm.py @@ -61,6 +61,7 @@ each command has a format: from _pydev_bundle.pydev_imports import _queue from _pydev_imps._pydev_saved_modules import time from _pydev_imps._pydev_saved_modules import thread +from _pydev_imps._pydev_saved_modules import threading from _pydev_imps._pydev_saved_modules import socket from socket import socket, AF_INET, SOCK_STREAM, SHUT_RD, SHUT_WR from _pydevd_bundle.pydevd_constants import * #@UnusedWildImport diff --git a/python/helpers/pydev/_pydevd_bundle/pydevd_constants.py b/python/helpers/pydev/_pydevd_bundle/pydevd_constants.py index 50d1b971a469..6ba6bd0030be 100644 --- a/python/helpers/pydev/_pydevd_bundle/pydevd_constants.py +++ b/python/helpers/pydev/_pydevd_bundle/pydevd_constants.py @@ -91,6 +91,10 @@ except AttributeError: try: SUPPORT_GEVENT = os.getenv('GEVENT_SUPPORT', 'False') == 'True' + try: + import gevent + except: + SUPPORT_GEVENT = False except: # Jython 2.1 doesn't accept that construct SUPPORT_GEVENT = False @@ -102,6 +106,11 @@ USE_LIB_COPY = SUPPORT_GEVENT and \ def protect_libraries_from_patching(): + """ + In this function we delete some modules from `sys.modules` dictionary and import them again inside + `_pydev_saved_modules` in order to save their original copies there. After that we can use these + saved modules within the debugger to protect them from patching by external libraries (e.g. gevent). + """ patched = ['threading', 'thread', '_thread', 'time', 'socket', 'Queue', 'queue', 'select', 'xmlrpclib', 'SimpleXMLRPCServer', 'BaseHTTPServer', 'SocketServer', 'xmlrpc.client', 'xmlrpc.server', 'http.server', 'socketserver'] diff --git a/python/pydevSrc/com/jetbrains/python/debugger/PySignature.java b/python/pydevSrc/com/jetbrains/python/debugger/PySignature.java index 9d13e6421554..03192a47eb97 100644 --- a/python/pydevSrc/com/jetbrains/python/debugger/PySignature.java +++ b/python/pydevSrc/com/jetbrains/python/debugger/PySignature.java @@ -117,13 +117,18 @@ public class PySignature { public String getTypeQualifiedName() { if (myTypes.size() == 1) { - return myTypes.get(0); + return noneTypeToNone(myTypes.get(0)); } else { - return StringUtil.join(myTypes, " or "); + return "Union[" + StringUtil.join(myTypes, NamedParameter::noneTypeToNone, ", ") + "]"; } } + @Nullable + private static String noneTypeToNone(@Nullable String type) { + return "NoneType".equals(type) ? "None" : type; + } + public void addType(String type) { if (!myTypes.contains(type)) { myTypes.add(type); diff --git a/python/src/com/jetbrains/python/codeInsight/intentions/PyAnnotateTypesIntention.java b/python/src/com/jetbrains/python/codeInsight/intentions/PyAnnotateTypesIntention.java index 3f91a6a692aa..0d0605d527cd 100644 --- a/python/src/com/jetbrains/python/codeInsight/intentions/PyAnnotateTypesIntention.java +++ b/python/src/com/jetbrains/python/codeInsight/intentions/PyAnnotateTypesIntention.java @@ -203,7 +203,7 @@ public class PyAnnotateTypesIntention implements IntentionAction { PyParameter[] params = function.getParameterList().getParameters(); for (int i = params.length - 1; i >= 0; i--) { - if (params[i] instanceof PyNamedParameter) { + if (params[i] instanceof PyNamedParameter && !params[i].isSelf()) { params[i] = annotateParameter(project, editor, (PyNamedParameter)params[i], false); } } diff --git a/python/src/com/jetbrains/python/codeInsight/intentions/SpecifyTypeInPy3AnnotationsIntention.java b/python/src/com/jetbrains/python/codeInsight/intentions/SpecifyTypeInPy3AnnotationsIntention.java index f9b41ab1c7b7..a2b2735c5aa1 100644 --- a/python/src/com/jetbrains/python/codeInsight/intentions/SpecifyTypeInPy3AnnotationsIntention.java +++ b/python/src/com/jetbrains/python/codeInsight/intentions/SpecifyTypeInPy3AnnotationsIntention.java @@ -137,8 +137,7 @@ public class SpecifyTypeInPy3AnnotationsIntention extends TypeIntention { static String returnType(@NotNull PyFunction function) { String returnType = PyNames.OBJECT; - final PySignature signature = PySignatureCacheManager.getInstance(function.getProject()).findSignature( - function); + final PySignature signature = PySignatureCacheManager.getInstance(function.getProject()).findSignature(function); if (signature != null) { returnType = ObjectUtils.chooseNotNull(signature.getReturnTypeQualifiedName(), returnType); } @@ -148,22 +147,28 @@ public class SpecifyTypeInPy3AnnotationsIntention extends TypeIntention { public static PyExpression annotateReturnType(Project project, PyFunction function, boolean createTemplate) { String returnType = returnType(function); - final String annotationText = " -> " + returnType; - - final PsiElement prevElem = PyPsiUtils.getPrevNonCommentSibling(function.getStatementList(), true); - assert prevElem != null; + final String annotationText = "-> " + returnType; final PsiDocumentManager manager = PsiDocumentManager.getInstance(project); Document documentWithCallable = manager.getDocument(function.getContainingFile()); if (documentWithCallable != null) { try { - final TextRange range = prevElem.getTextRange(); manager.doPostponedOperationsAndUnblockDocument(documentWithCallable); - if (prevElem.getNode().getElementType() == PyTokenTypes.COLON) { - documentWithCallable.insertString(range.getStartOffset(), annotationText); + final PyAnnotation oldAnnotation = function.getAnnotation(); + if (oldAnnotation != null) { + final TextRange oldRange = oldAnnotation.getTextRange(); + documentWithCallable.replaceString(oldRange.getStartOffset(), oldRange.getEndOffset(), annotationText); } else { - documentWithCallable.insertString(range.getEndOffset(), annotationText + ":"); + final PsiElement prevElem = PyPsiUtils.getPrevNonCommentSibling(function.getStatementList(), true); + assert prevElem != null; + final TextRange range = prevElem.getTextRange(); + if (prevElem.getNode().getElementType() == PyTokenTypes.COLON) { + documentWithCallable.insertString(range.getStartOffset(), " " + annotationText); + } + else { + documentWithCallable.insertString(range.getEndOffset(), " " + annotationText + ":"); + } } } finally {