search for functional interface implementations by lambdas and method references: change signature/find usages; to be continued (IDEA-104286; IDEA-90824)

This commit is contained in:
Anna Kozlova
2014-04-07 16:56:08 +02:00
parent 737a7d5a7a
commit ec1d4927bd
23 changed files with 677 additions and 195 deletions

View File

@@ -39,13 +39,13 @@ import com.intellij.psi.impl.search.ThrowSearchUtil;
import com.intellij.psi.meta.PsiMetaData;
import com.intellij.psi.meta.PsiMetaOwner;
import com.intellij.psi.search.*;
import com.intellij.psi.search.searches.ClassInheritorsSearch;
import com.intellij.psi.search.searches.MethodReferencesSearch;
import com.intellij.psi.search.searches.OverridingMethodsSearch;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.psi.search.searches.*;
import com.intellij.psi.targets.AliasingPsiTarget;
import com.intellij.psi.targets.AliasingPsiTargetMapper;
import com.intellij.psi.util.*;
import com.intellij.psi.util.MethodSignature;
import com.intellij.psi.util.PropertyUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.PsiUtilCore;
import com.intellij.psi.xml.XmlAttributeValue;
import com.intellij.refactoring.util.JavaNonCodeSearchElementDescriptionProvider;
import com.intellij.refactoring.util.NonCodeSearchDescriptionLocation;
@@ -347,6 +347,14 @@ public class JavaFindUsagesHandler extends FindUsagesHandler{
else if (classOptions.isImplementingClasses){
if (!addImplementingClasses(psiClass, processor, classOptions)) return false;
}
FunctionalExpressionSearch.search(psiClass, classOptions.searchScope).forEach(new PsiElementProcessorAdapter<PsiFunctionalExpression>(
new PsiElementProcessor<PsiFunctionalExpression>() {
@Override
public boolean execute(@NotNull PsiFunctionalExpression expression) {
return addResult(processor, expression, options);
}
}));
}
else if (classOptions.isDerivedClasses) {
if (!addInheritors(psiClass, processor, classOptions)) return false;

View File

@@ -21,6 +21,7 @@ import com.intellij.openapi.util.Comparing;
import com.intellij.psi.*;
import com.intellij.psi.javadoc.PsiDocTagValue;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.searches.FunctionalExpressionSearch;
import com.intellij.psi.search.searches.MethodReferencesSearch;
import com.intellij.psi.search.searches.OverridingMethodsSearch;
import com.intellij.psi.search.searches.ReferencesSearch;
@@ -39,6 +40,7 @@ import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.HashSet;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Set;
/**
@@ -71,6 +73,15 @@ class JavaChangeSignatureUsageSearcher {
PsiMethod[] overridingMethods = findSimpleUsagesWithoutParameters(method, result, true, true, true);
findUsagesInCallers(result);
final ArrayList<PsiMethod> methods = new ArrayList<>(Arrays.asList(overridingMethods));
methods.add(method);
for (PsiMethod psiMethod : methods) {
for (PsiFunctionalExpression functionalExpression : FunctionalExpressionSearch.search(psiMethod.getContainingClass())) {
result.add(new FunctionalInterfaceChangedUsageInfo(functionalExpression, psiMethod));
}
}
//Parameter name changes are not propagated
findParametersUsage(method, result, overridingMethods);
}
@@ -318,4 +329,16 @@ class JavaChangeSignatureUsageSearcher {
RefactoringUIUtil.getDescription(myMethod, true));
}
}
private static class FunctionalInterfaceChangedUsageInfo extends UnresolvableCollisionUsageInfo {
public FunctionalInterfaceChangedUsageInfo(PsiElement element, PsiElement referencedElement) {
super(element, referencedElement);
}
@Override
public String getDescription() {
return "Functional expression will be corrupted";
}
}
}

View File

@@ -232,7 +232,7 @@ public class InlineMethodProcessor extends BaseRefactoringProcessor {
GenericInlineHandler.collectConflicts(ref, myMethod, myInliners, conflicts);
}
final PsiReturnStatement[] returnStatements = RefactoringUtil.findReturnStatements(myMethod);
final PsiReturnStatement[] returnStatements = PsiUtil.findReturnStatements(myMethod);
for (PsiReturnStatement statement : returnStatements) {
PsiExpression value = statement.getReturnValue();
if (value != null && !(value instanceof PsiCallExpression)) {
@@ -819,7 +819,7 @@ public class InlineMethodProcessor extends BaseRefactoringProcessor {
}
if (resultName != null || tailCallType == InlineUtil.TailCallType.Simple) {
PsiReturnStatement[] returnStatements = RefactoringUtil.findReturnStatements(myMethodCopy);
PsiReturnStatement[] returnStatements = PsiUtil.findReturnStatements(myMethodCopy);
for (PsiReturnStatement returnStatement : returnStatements) {
final PsiExpression returnValue = returnStatement.getReturnValue();
if (returnValue == null) continue;
@@ -1464,7 +1464,7 @@ public class InlineMethodProcessor extends BaseRefactoringProcessor {
}
public static boolean checkBadReturns(PsiMethod method) {
PsiReturnStatement[] returns = RefactoringUtil.findReturnStatements(method);
PsiReturnStatement[] returns = PsiUtil.findReturnStatements(method);
if (returns.length == 0) return false;
PsiCodeBlock body = method.getBody();
ControlFlow controlFlow;

View File

@@ -557,7 +557,7 @@ public abstract class TurnRefsToSuperProcessorBase extends BaseRefactoringProces
addLink(parent, returnType);
}
final PsiReturnStatement[] returnStatements = RefactoringUtil.findReturnStatements(method);
final PsiReturnStatement[] returnStatements = PsiUtil.findReturnStatements(method);
for (final PsiReturnStatement returnStatement : returnStatements) {
final PsiExpression returnValue = returnStatement.getReturnValue();
if (returnValue != null) {

View File

@@ -252,27 +252,6 @@ public class RefactoringUtil {
}
}
public static PsiReturnStatement[] findReturnStatements(PsiMethod method) {
ArrayList<PsiReturnStatement> vector = new ArrayList<PsiReturnStatement>();
PsiCodeBlock body = method.getBody();
if (body != null) {
addReturnStatements(vector, body);
}
return vector.toArray(new PsiReturnStatement[vector.size()]);
}
private static void addReturnStatements(ArrayList<PsiReturnStatement> vector, PsiElement element) {
if (element instanceof PsiReturnStatement) {
vector.add((PsiReturnStatement)element);
}
else if (!(element instanceof PsiClass)) {
PsiElement[] children = element.getChildren();
for (PsiElement child : children) {
addReturnStatements(vector, child);
}
}
}
public static PsiElement getParentStatement(PsiElement place, boolean skipScopingStatements) {
PsiElement parent = place;

View File

@@ -0,0 +1,59 @@
/*
* Copyright 2000-2014 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.intellij.psi.search.searches;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiFunctionalExpression;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.PsiSearchHelper;
import com.intellij.psi.search.SearchScope;
import com.intellij.util.Query;
import com.intellij.util.QueryExecutor;
import org.jetbrains.annotations.NotNull;
public class FunctionalExpressionSearch extends ExtensibleQueryFactory<PsiFunctionalExpression, FunctionalExpressionSearch.SearchParameters> {
public static ExtensionPointName<QueryExecutor> EP_NAME = ExtensionPointName.create("com.intellij.functionalInterfaceSearch");
public static final FunctionalExpressionSearch INSTANCE = new FunctionalExpressionSearch();
public static class SearchParameters {
private final PsiClass myElementToSearch;
private final SearchScope myScope;
public SearchParameters(PsiClass aClass, SearchScope scope) {
myElementToSearch = aClass;
myScope = scope;
}
public PsiClass getElementToSearch() {
return myElementToSearch;
}
@NotNull
public SearchScope getEffectiveSearchScope () {
SearchScope accessScope = PsiSearchHelper.SERVICE.getInstance(myElementToSearch.getProject()).getUseScope(myElementToSearch);
return myScope.intersectWith(accessScope);
}
}
public static Query<PsiFunctionalExpression> search(final PsiClass aClass, SearchScope scope) {
return INSTANCE.createUniqueResultsQuery(new SearchParameters(aClass, scope));
}
public static Query<PsiFunctionalExpression> search(final PsiClass aClass) {
return search(aClass, GlobalSearchScope.allScope(aClass.getProject()));
}
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright 2000-2014 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.
*/
/*
* @author max
*/
package com.intellij.psi.impl.java.stubs.index;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.impl.search.JavaSourceFilterScope;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.stubs.StringStubIndexExtension;
import com.intellij.psi.stubs.StubIndex;
import com.intellij.psi.stubs.StubIndexKey;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
public class JavaMethodParameterTypesIndex extends StringStubIndexExtension<PsiMethod> {
private static final JavaMethodParameterTypesIndex ourInstance = new JavaMethodParameterTypesIndex();
public static JavaMethodParameterTypesIndex getInstance() {
return ourInstance;
}
@NotNull
@Override
public StubIndexKey<String, PsiMethod> getKey() {
return JavaStubIndexKeys.METHOD_TYPES;
}
@Override
public Collection<PsiMethod> get(final String s, final Project project, @NotNull final GlobalSearchScope scope) {
return StubIndex.getElements(getKey(), s, project, new JavaSourceFilterScope(scope), PsiMethod.class);
}
}

View File

@@ -0,0 +1,148 @@
/*
* Copyright 2000-2014 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.intellij.psi.impl.search;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Computable;
import com.intellij.psi.*;
import com.intellij.psi.impl.java.stubs.index.JavaMethodParameterTypesIndex;
import com.intellij.psi.search.EverythingGlobalScope;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.SearchScope;
import com.intellij.psi.search.searches.FunctionalExpressionSearch;
import com.intellij.psi.search.searches.MethodReferencesSearch;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.Processor;
import com.intellij.util.QueryExecutor;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Collection;
public class JavaFunctionalExpressionSearcher implements QueryExecutor<PsiFunctionalExpression, FunctionalExpressionSearch.SearchParameters> {
@Override
public boolean execute(@NotNull FunctionalExpressionSearch.SearchParameters queryParameters,
@NotNull Processor<PsiFunctionalExpression> consumer) {
final PsiClass aClass = queryParameters.getElementToSearch();
if (!ApplicationManager.getApplication().runReadAction(new Computable<Boolean>() {
@Override
public Boolean compute() {
return LambdaUtil.isFunctionalClass(aClass);
}
}) || !PsiUtil.isLanguageLevel8OrHigher(aClass)) {
return true;
}
final ArrayList<PsiFunctionalExpression> functionalExpressions = new ArrayList<>();
collectFunctionalExpressions(aClass, functionalExpressions, queryParameters.getEffectiveSearchScope());
for (PsiFunctionalExpression functionalExpression : functionalExpressions) {
if (!consumer.process(functionalExpression)) return false;
}
return true;
}
public static void collectFunctionalExpressions(final PsiClass aClass, final Collection<PsiFunctionalExpression> result, final SearchScope searchScope) {
final SearchScope classScope = ApplicationManager.getApplication().runReadAction(new Computable<SearchScope>() {
@Override
public SearchScope compute() {
return aClass.getUseScope();
}
});
final SearchScope useScope = searchScope.intersectWith(classScope);
final Project project = aClass.getProject();
final GlobalSearchScope scope = useScope instanceof GlobalSearchScope ? (GlobalSearchScope)useScope : new EverythingGlobalScope(project);
final Collection<PsiMethod> lambdaCandidates = ApplicationManager.getApplication().runReadAction(new Computable<Collection<PsiMethod>>() {
@Override
public Collection<PsiMethod> compute() {
final String functionalInterfaceName = aClass.getName();
final GlobalSearchScope useClassScope = classScope instanceof GlobalSearchScope ? (GlobalSearchScope)classScope : scope;
return JavaMethodParameterTypesIndex.getInstance().get(functionalInterfaceName, project, useClassScope);
}
});
for (PsiMethod psiMethod : lambdaCandidates) {
for (PsiReference ref : MethodReferencesSearch.search(psiMethod, scope, false)) {
final PsiElement refElement = ref.getElement();
if (refElement != null) {
final PsiElement candidateElement = refElement.getParent();
if (candidateElement instanceof PsiCallExpression) {
final PsiExpressionList argumentList = ((PsiCallExpression)candidateElement).getArgumentList();
if (argumentList != null) {
ApplicationManager.getApplication().runReadAction(new Runnable() {
public void run() {
final PsiExpression[] args = argumentList.getExpressions();
for (PsiExpression arg : args) {
if (arg instanceof PsiFunctionalExpression) {
final PsiFunctionalExpression functionalExpression = (PsiFunctionalExpression)arg;
final PsiType functionalType = functionalExpression.getFunctionalInterfaceType();
if (PsiUtil.resolveClassInType(functionalType) == aClass) {
result.add(functionalExpression);
}
}
}
}
});
}
}
}
}
}
for (PsiReference reference : ReferencesSearch.search(aClass, scope)) {
final PsiElement element = reference.getElement();
if (element != null) {
final PsiElement parent = element.getParent();
if (parent instanceof PsiTypeElement) {
final PsiElement gParent = parent.getParent();
if (gParent instanceof PsiVariable) {
final PsiExpression initializer = PsiUtil.skipParenthesizedExprDown(((PsiVariable)gParent).getInitializer());
if (initializer instanceof PsiFunctionalExpression) {
result.add((PsiFunctionalExpression)initializer);
}
for (PsiReference varRef : ReferencesSearch.search(parent, scope)) {
final PsiElement varElement = varRef.getElement();
if (varElement != null) {
final PsiElement varElementParent = varElement.getParent();
if (varElementParent instanceof PsiAssignmentExpression &&
((PsiAssignmentExpression)varElementParent).getLExpression() == varElement) {
final PsiExpression rExpression = PsiUtil.skipParenthesizedExprDown(((PsiAssignmentExpression)varElementParent).getRExpression());
if (rExpression instanceof PsiFunctionalExpression) {
result.add((PsiFunctionalExpression)rExpression);
}
}
}
}
} else if (gParent instanceof PsiMethod) {
final PsiReturnStatement[] returnStatements = ApplicationManager.getApplication().runReadAction(
new Computable<PsiReturnStatement[]>() {
@Override
public PsiReturnStatement[] compute() {
return PsiUtil.findReturnStatements((PsiMethod)gParent);
}
});
for (PsiReturnStatement returnStatement : returnStatements) {
final PsiExpression returnValue = returnStatement.getReturnValue();
if (returnValue instanceof PsiFunctionalExpression) {
result.add((PsiFunctionalExpression)returnValue);
}
}
}
}
}
}
}
}

View File

@@ -21,6 +21,7 @@ import com.intellij.openapi.util.Computable;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.infos.MethodCandidateInfo;
import com.intellij.psi.util.*;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -72,12 +73,14 @@ public class LambdaUtil {
@Nullable
public static PsiMethod getFunctionalInterfaceMethod(PsiClassType.ClassResolveResult result) {
final PsiClass psiClass = result.getElement();
if (psiClass != null) {
final MethodSignature methodSignature = getFunction(psiClass);
if (methodSignature != null) {
return getMethod(psiClass, methodSignature);
}
return getFunctionalInterfaceMethod(result.getElement());
}
@Nullable
public static PsiMethod getFunctionalInterfaceMethod(PsiClass aClass) {
final MethodSignature methodSignature = getFunction(aClass);
if (methodSignature != null) {
return getMethod(aClass, methodSignature);
}
return null;
}
@@ -108,6 +111,11 @@ public class LambdaUtil {
}
final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(GenericsUtil.eliminateWildcards(type));
final PsiClass aClass = resolveResult.getElement();
return isFunctionalClass(aClass);
}
@Contract("null -> false")
public static boolean isFunctionalClass(PsiClass aClass) {
if (aClass != null) {
if (aClass instanceof PsiTypeParameter) return false;
final List<MethodSignature> signatures = findFunctionCandidates(aClass);
@@ -115,7 +123,7 @@ public class LambdaUtil {
}
return false;
}
public static boolean isValidLambdaContext(@Nullable PsiElement context) {
return context instanceof PsiTypeCastExpression ||
context instanceof PsiAssignmentExpression ||

View File

@@ -0,0 +1,27 @@
/*
* Copyright 2000-2014 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.intellij.psi;
import org.jetbrains.annotations.Nullable;
public interface PsiFunctionalExpression extends PsiExpression {
/**
* @return SAM type the lambda expression corresponds to
* null when no SAM type could be found
*/
@Nullable
PsiType getFunctionalInterfaceType();
}

View File

@@ -21,7 +21,7 @@ import org.jetbrains.annotations.Nullable;
/**
* Represents a Java lambda expression.
*/
public interface PsiLambdaExpression extends PsiExpression {
public interface PsiLambdaExpression extends PsiFunctionalExpression {
/**
* Returns this lambda expression's parameter list.
*
@@ -39,13 +39,6 @@ public interface PsiLambdaExpression extends PsiExpression {
@Nullable
PsiElement getBody();
/**
* @return SAM type the lambda expression corresponds to
* null when no SAM type could be found
*/
@Nullable
PsiType getFunctionalInterfaceType();
boolean isVoidCompatible();
boolean isValueCompatible();

View File

@@ -20,7 +20,7 @@ import org.jetbrains.annotations.Nullable;
/**
* Represents a method or constructor reference.
*/
public interface PsiMethodReferenceExpression extends PsiReferenceExpression {
public interface PsiMethodReferenceExpression extends PsiReferenceExpression, PsiFunctionalExpression {
/**
* Returns the type element used as the qualifier of the reference.
*
@@ -28,13 +28,6 @@ public interface PsiMethodReferenceExpression extends PsiReferenceExpression {
*/
@Nullable
PsiTypeElement getQualifierType();
/**
* @return SAM type the method reference expression corresponds to
* null when no SAM type could be found
*/
@Nullable
PsiType getFunctionalInterfaceType();
/**
* @return if there is only one possible compile-time declaration with only one possible invocation type,

View File

@@ -1092,4 +1092,25 @@ public final class PsiUtil extends PsiUtilCore {
}
return false;
}
public static PsiReturnStatement[] findReturnStatements(PsiMethod method) {
ArrayList<PsiReturnStatement> vector = new ArrayList<PsiReturnStatement>();
PsiCodeBlock body = method.getBody();
if (body != null) {
addReturnStatements(vector, body);
}
return vector.toArray(new PsiReturnStatement[vector.size()]);
}
private static void addReturnStatements(ArrayList<PsiReturnStatement> vector, PsiElement element) {
if (element instanceof PsiReturnStatement) {
vector.add((PsiReturnStatement)element);
}
else if (!(element instanceof PsiClass)) {
PsiElement[] children = element.getChildren();
for (PsiElement child : children) {
addReturnStatements(vector, child);
}
}
}
}

View File

@@ -146,5 +146,16 @@ public abstract class JavaMethodElementType extends JavaStubElementType<PsiMetho
sink.occurrence(JavaStubIndexKeys.JVM_STATIC_MEMBERS_TYPES, stub.getReturnTypeText(false).getShortTypeText());
}
}
for (StubElement stubElement : stub.getChildrenStubs()) {
if (stubElement instanceof PsiParameterListStub) {
for (StubElement paramStub : ((PsiParameterListStub)stubElement).getChildrenStubs()) {
if (paramStub instanceof PsiParameterStub) {
sink.occurrence(JavaStubIndexKeys.METHOD_TYPES, ((PsiParameterStub)paramStub).getType(false).getShortTypeText());
}
}
break;
}
}
}
}

View File

@@ -28,8 +28,9 @@ public class JavaStubIndexKeys {
public static final StubIndexKey<String, PsiMethod> METHODS = StubIndexKey.createIndexKey("java.method.name");
public static final StubIndexKey<String, PsiMember> JVM_STATIC_MEMBERS_NAMES = StubIndexKey.createIndexKey("jvm.static.member.name");
public static final StubIndexKey<String, PsiMember> JVM_STATIC_MEMBERS_TYPES = StubIndexKey.createIndexKey("jvm.static.member.type");
public static final StubIndexKey<String,PsiAnonymousClass> ANONYMOUS_BASEREF = StubIndexKey.createIndexKey("java.anonymous.baseref");
public static final StubIndexKey<String,PsiClass> CLASS_SHORT_NAMES = StubIndexKey.createIndexKey("java.class.shortname");
public static final StubIndexKey<String, PsiAnonymousClass> ANONYMOUS_BASEREF = StubIndexKey.createIndexKey("java.anonymous.baseref");
public static final StubIndexKey<String, PsiMethod> METHOD_TYPES = StubIndexKey.createIndexKey("java.method.parameter.types");
public static final StubIndexKey<String, PsiClass> CLASS_SHORT_NAMES = StubIndexKey.createIndexKey("java.class.shortname");
public static final StubIndexKey<Integer,PsiClass> CLASS_FQN = StubIndexKey.createIndexKey("java.class.fqn");
private JavaStubIndexKeys() {

View File

@@ -0,0 +1,11 @@
interface SAM {
void <caret>foo();
}
class Test {
{
bar(() -> {});
}
void bar(SAM sam){}
}

View File

@@ -0,0 +1,9 @@
interface SAM {
void <caret>foo();
}
class Test {
{
SAM sam = () -> {};
}
}

View File

@@ -0,0 +1,11 @@
interface SA<caret>M {
void foo();
}
class Test {
{
bar(() -> {});
}
void bar(SAM sam){}
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright 2000-2014 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.intellij.codeInsight.daemon.lambda;
import com.intellij.psi.PsiType;
import com.intellij.refactoring.BaseRefactoringProcessor;
import com.intellij.refactoring.ChangeSignatureBaseTest;
import com.intellij.refactoring.changeSignature.ParameterInfoImpl;
import com.intellij.refactoring.changeSignature.ThrownExceptionInfo;
public class ChangeSignatureTouchLambdaTest extends ChangeSignatureBaseTest {
public void testVariableDeclaration() {
doTestConflict();
}
public void testMethodArgument() throws Exception {
doTestConflict();
}
private void doTestConflict() {
try {
doTest(null, null, null, new ParameterInfoImpl[] {new ParameterInfoImpl(-1, "b", PsiType.BOOLEAN)}, new ThrownExceptionInfo[0], false);
fail("Conflict expected");
}
catch (BaseRefactoringProcessor.ConflictsInTestsException ignored) { }
}
@Override
protected String getRelativePath() {
return "/codeInsight/daemonCodeAnalyzer/lambda/changeSignature/";
}
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright 2000-2014 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.intellij.codeInsight.daemon.lambda;
import com.intellij.JavaTestUtil;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFunctionalExpression;
import com.intellij.psi.search.searches.FunctionalExpressionSearch;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase;
import java.util.Collection;
public class FindFunctionalInterfaceTest extends LightCodeInsightFixtureTestCase {
public void testMethodArgument() throws Exception {
myFixture.configureByFile(getTestName(false) + ".java");
final PsiElement elementAtCaret = myFixture.getElementAtCaret();
assertNotNull(elementAtCaret);
final PsiClass psiClass = PsiTreeUtil.getParentOfType(elementAtCaret, PsiClass.class, false);
assertTrue(psiClass != null && psiClass.isInterface());
final Collection<PsiFunctionalExpression> expressions = FunctionalExpressionSearch.search(psiClass).findAll();
assertTrue(expressions.size() == 1);
final PsiFunctionalExpression next = expressions.iterator().next();
assertNotNull(next);
assertEquals("() -> {}", next.getText());
}
@Override
protected String getBasePath() {
return JavaTestUtil.getRelativeJavaTestDataPath() + "/codeInsight/daemonCodeAnalyzer/lambda/findUsages/";
}
}

View File

@@ -0,0 +1,172 @@
/*
* Copyright 2000-2014 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.intellij.refactoring;
import com.intellij.JavaTestUtil;
import com.intellij.codeInsight.TargetElementUtilBase;
import com.intellij.psi.*;
import com.intellij.refactoring.changeSignature.ChangeSignatureProcessor;
import com.intellij.refactoring.changeSignature.JavaThrownExceptionInfo;
import com.intellij.refactoring.changeSignature.ParameterInfoImpl;
import com.intellij.refactoring.changeSignature.ThrownExceptionInfo;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public abstract class ChangeSignatureBaseTest extends LightRefactoringTestCase {
protected PsiElementFactory myFactory;
@NotNull
@Override
protected String getTestDataPath() {
return JavaTestUtil.getJavaTestDataPath();
}
@Override
public void setUp() throws Exception {
super.setUp();
myFactory = JavaPsiFacade.getInstance(getProject()).getElementFactory();
}
protected void doTest(@Nullable String returnType,
@Nullable final String[] parameters,
@Nullable final String[] exceptions,
boolean delegate) {
GenParams genParams = parameters == null ? new SimpleParameterGen() : new GenParams() {
@Override
public ParameterInfoImpl[] genParams(PsiMethod method) throws IncorrectOperationException {
ParameterInfoImpl[] parameterInfos = new ParameterInfoImpl[parameters.length];
for (int i = 0; i < parameters.length; i++) {
PsiType type = myFactory.createTypeFromText(parameters[i], method);
parameterInfos[i] = new ParameterInfoImpl(-1, "p" + (i + 1), type);
}
return parameterInfos;
}
};
GenExceptions genExceptions = exceptions == null ? new SimpleExceptionsGen() : new GenExceptions() {
@Override
public ThrownExceptionInfo[] genExceptions(PsiMethod method) throws IncorrectOperationException {
ThrownExceptionInfo[] exceptionInfos = new ThrownExceptionInfo[exceptions.length];
for (int i = 0; i < exceptions.length; i++) {
PsiType type = myFactory.createTypeFromText(exceptions[i], method);
exceptionInfos[i] = new JavaThrownExceptionInfo(-1, (PsiClassType)type);
}
return exceptionInfos;
}
};
doTest(null, null, returnType, genParams, genExceptions, delegate);
}
protected void doTest(@Nullable String newReturnType, ParameterInfoImpl[] parameterInfos, boolean generateDelegate) {
doTest(null, null, newReturnType, parameterInfos, new ThrownExceptionInfo[0], generateDelegate);
}
protected void doTest(@PsiModifier.ModifierConstant @Nullable String newVisibility,
@Nullable String newName,
@Nullable String newReturnType,
ParameterInfoImpl[] parameterInfo,
ThrownExceptionInfo[] exceptionInfo,
boolean generateDelegate) {
SimpleParameterGen params = new SimpleParameterGen(parameterInfo);
SimpleExceptionsGen exceptions = new SimpleExceptionsGen(exceptionInfo);
doTest(newVisibility, newName, newReturnType, params, exceptions, generateDelegate);
}
protected void doTest(@PsiModifier.ModifierConstant @Nullable String newVisibility,
@Nullable String newName,
@Nullable @NonNls String newReturnType,
GenParams genParams,
boolean generateDelegate) {
doTest(newVisibility, newName, newReturnType, genParams, new SimpleExceptionsGen(), generateDelegate);
}
protected void doTest(@PsiModifier.ModifierConstant @Nullable String newVisibility,
@Nullable String newName,
@Nullable String newReturnType,
GenParams genParams,
GenExceptions genExceptions,
boolean generateDelegate) {
String basePath = getRelativePath() + getTestName(false);
configureByFile(basePath + ".java");
PsiElement targetElement = TargetElementUtilBase.findTargetElement(getEditor(), TargetElementUtilBase.ELEMENT_NAME_ACCEPTED);
assertTrue("<caret> is not on method name", targetElement instanceof PsiMethod);
PsiMethod method = (PsiMethod)targetElement;
PsiType newType = newReturnType != null ? myFactory.createTypeFromText(newReturnType, method) : method.getReturnType();
new ChangeSignatureProcessor(getProject(), method, generateDelegate, newVisibility,
newName != null ? newName : method.getName(),
newType, genParams.genParams(method), genExceptions.genExceptions(method)).run();
checkResultByFile(basePath + "_after.java");
}
protected String getRelativePath() {
return "/refactoring/changeSignature/";
}
protected interface GenParams {
ParameterInfoImpl[] genParams(PsiMethod method) throws IncorrectOperationException;
}
protected interface GenExceptions {
ThrownExceptionInfo[] genExceptions(PsiMethod method) throws IncorrectOperationException;
}
protected static class SimpleParameterGen implements GenParams {
private ParameterInfoImpl[] myInfos;
public SimpleParameterGen() { }
public SimpleParameterGen(ParameterInfoImpl[] infos) {
myInfos = infos;
}
@Override
public ParameterInfoImpl[] genParams(PsiMethod method) {
if (myInfos == null) {
myInfos = new ParameterInfoImpl[method.getParameterList().getParametersCount()];
for (int i = 0; i < myInfos.length; i++) {
myInfos[i] = new ParameterInfoImpl(i);
}
}
for (ParameterInfoImpl info : myInfos) {
info.updateFromMethod(method);
}
return myInfos;
}
}
protected static class SimpleExceptionsGen implements GenExceptions {
private final ThrownExceptionInfo[] myInfos;
public SimpleExceptionsGen() {
myInfos = new ThrownExceptionInfo[0];
}
public SimpleExceptionsGen(ThrownExceptionInfo[] infos) {
myInfos = infos;
}
@Override
public ThrownExceptionInfo[] genExceptions(PsiMethod method) {
for (ThrownExceptionInfo info : myInfos) {
info.updateFromMethod(method);
}
return myInfos;
}
}
}

View File

@@ -15,7 +15,6 @@
*/
package com.intellij.refactoring;
import com.intellij.JavaTestUtil;
import com.intellij.codeInsight.TargetElementUtilBase;
import com.intellij.psi.*;
import com.intellij.refactoring.changeSignature.ChangeSignatureProcessor;
@@ -24,29 +23,13 @@ import com.intellij.refactoring.changeSignature.ParameterInfoImpl;
import com.intellij.refactoring.changeSignature.ThrownExceptionInfo;
import com.intellij.refactoring.util.CanonicalTypes;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.HashSet;
/**
* @author dsl
*/
public class ChangeSignatureTest extends LightRefactoringTestCase {
private PsiElementFactory myFactory;
@NotNull
@Override
protected String getTestDataPath() {
return JavaTestUtil.getJavaTestDataPath();
}
@Override
public void setUp() throws Exception {
super.setUp();
myFactory = JavaPsiFacade.getInstance(getProject()).getElementFactory();
}
public class ChangeSignatureTest extends ChangeSignatureBaseTest {
public void testSimple() {
doTest(null, null, null, new ParameterInfoImpl[0], new ThrownExceptionInfo[0], false);
@@ -399,7 +382,7 @@ public class ChangeSignatureTest extends LightRefactoringTestCase {
}
public void testPropagateParameter() {
String basePath = "/refactoring/changeSignature/" + getTestName(false);
String basePath = getRelativePath() + getTestName(false);
configureByFile(basePath + ".java");
final PsiElement targetElement = TargetElementUtilBase.findTargetElement(getEditor(), TargetElementUtilBase.ELEMENT_NAME_ACCEPTED);
assertTrue("<caret> is not on method name", targetElement instanceof PsiMethod);
@@ -430,125 +413,4 @@ public class ChangeSignatureTest extends LightRefactoringTestCase {
}
/* workers */
private void doTest(@Nullable String returnType, @Nullable final String[] parameters, @Nullable final String[] exceptions, boolean delegate) {
GenParams genParams = parameters == null ? new SimpleParameterGen() : new GenParams() {
@Override
public ParameterInfoImpl[] genParams(PsiMethod method) throws IncorrectOperationException {
ParameterInfoImpl[] parameterInfos = new ParameterInfoImpl[parameters.length];
for (int i = 0; i < parameters.length; i++) {
PsiType type = myFactory.createTypeFromText(parameters[i], method);
parameterInfos[i] = new ParameterInfoImpl(-1, "p" + (i + 1), type);
}
return parameterInfos;
}
};
GenExceptions genExceptions = exceptions == null ? new SimpleExceptionsGen() : new GenExceptions() {
@Override
public ThrownExceptionInfo[] genExceptions(PsiMethod method) throws IncorrectOperationException {
ThrownExceptionInfo[] exceptionInfos = new ThrownExceptionInfo[exceptions.length];
for (int i = 0; i < exceptions.length; i++) {
PsiType type = myFactory.createTypeFromText(exceptions[i], method);
exceptionInfos[i] = new JavaThrownExceptionInfo(-1, (PsiClassType)type);
}
return exceptionInfos;
}
};
doTest(null, null, returnType, genParams, genExceptions, delegate);
}
private void doTest(@Nullable String newReturnType, ParameterInfoImpl[] parameterInfos, boolean generateDelegate) {
doTest(null, null, newReturnType, parameterInfos, new ThrownExceptionInfo[0], generateDelegate);
}
private void doTest(@PsiModifier.ModifierConstant @Nullable String newVisibility,
@Nullable String newName,
@Nullable String newReturnType,
ParameterInfoImpl[] parameterInfo,
ThrownExceptionInfo[] exceptionInfo,
boolean generateDelegate) {
SimpleParameterGen params = new SimpleParameterGen(parameterInfo);
SimpleExceptionsGen exceptions = new SimpleExceptionsGen(exceptionInfo);
doTest(newVisibility, newName, newReturnType, params, exceptions, generateDelegate);
}
private void doTest(@PsiModifier.ModifierConstant @Nullable String newVisibility,
@Nullable String newName,
@Nullable @NonNls String newReturnType,
GenParams genParams,
boolean generateDelegate) {
doTest(newVisibility, newName, newReturnType, genParams, new SimpleExceptionsGen(), generateDelegate);
}
private void doTest(@PsiModifier.ModifierConstant @Nullable String newVisibility,
@Nullable String newName,
@Nullable String newReturnType,
GenParams genParams,
GenExceptions genExceptions,
boolean generateDelegate) {
String basePath = "/refactoring/changeSignature/" + getTestName(false);
configureByFile(basePath + ".java");
PsiElement targetElement = TargetElementUtilBase.findTargetElement(getEditor(), TargetElementUtilBase.ELEMENT_NAME_ACCEPTED);
assertTrue("<caret> is not on method name", targetElement instanceof PsiMethod);
PsiMethod method = (PsiMethod)targetElement;
PsiType newType = newReturnType != null ? myFactory.createTypeFromText(newReturnType, method) : method.getReturnType();
new ChangeSignatureProcessor(getProject(), method, generateDelegate, newVisibility,
newName != null ? newName : method.getName(),
newType, genParams.genParams(method), genExceptions.genExceptions(method)).run();
checkResultByFile(basePath + "_after.java");
}
private interface GenParams {
ParameterInfoImpl[] genParams(PsiMethod method) throws IncorrectOperationException;
}
private static class SimpleParameterGen implements GenParams {
private ParameterInfoImpl[] myInfos;
public SimpleParameterGen() { }
public SimpleParameterGen(ParameterInfoImpl[] infos) {
myInfos = infos;
}
@Override
public ParameterInfoImpl[] genParams(PsiMethod method) {
if (myInfos == null) {
myInfos = new ParameterInfoImpl[method.getParameterList().getParametersCount()];
for (int i = 0; i < myInfos.length; i++) {
myInfos[i] = new ParameterInfoImpl(i);
}
}
for (ParameterInfoImpl info : myInfos) {
info.updateFromMethod(method);
}
return myInfos;
}
}
private interface GenExceptions {
ThrownExceptionInfo[] genExceptions(PsiMethod method) throws IncorrectOperationException;
}
private static class SimpleExceptionsGen implements GenExceptions {
private final ThrownExceptionInfo[] myInfos;
public SimpleExceptionsGen() {
myInfos = new ThrownExceptionInfo[0];
}
public SimpleExceptionsGen(ThrownExceptionInfo[] infos) {
myInfos = infos;
}
@Override
public ThrownExceptionInfo[] genExceptions(PsiMethod method) {
for (ThrownExceptionInfo info : myInfos) {
info.updateFromMethod(method);
}
return myInfos;
}
}
}

View File

@@ -107,6 +107,7 @@
<extensionPoint name="overridingMethodsSearch" interface="com.intellij.util.QueryExecutor"/>
<extensionPoint name="superMethodsSearch" interface="com.intellij.util.QueryExecutor"/>
<extensionPoint name="allClassesSearch" interface="com.intellij.util.QueryExecutor"/>
<extensionPoint name="functionalExpressionSearch" interface="com.intellij.util.QueryExecutor"/>
<extensionPoint name="compiler.inspectionValidator"
interface="com.intellij.openapi.compiler.util.InspectionValidator"
@@ -277,6 +278,8 @@
<definitionsScopedSearch implementation="com.intellij.codeInsight.navigation.MethodImplementationsSearch"/>
<definitionsScopedSearch implementation="com.intellij.codeInsight.navigation.ClassImplementationsSearch"/>
<functionalExpressionSearch implementation="com.intellij.psi.impl.search.JavaFunctionalExpressionSearcher"/>
<fileTypeFactory implementation="com.intellij.openapi.fileTypes.impl.JavaFileTypeFactory"/>
@@ -1318,6 +1321,7 @@
<stubIndex implementation="com.intellij.psi.impl.java.stubs.index.JavaStaticMemberTypeIndex"/>
<stubIndex implementation="com.intellij.psi.impl.java.stubs.index.JavaShortClassNameIndex"/>
<stubIndex implementation="com.intellij.psi.impl.java.stubs.index.JavaSuperClassNameOccurenceIndex"/>
<stubIndex implementation="com.intellij.psi.impl.java.stubs.index.JavaMethodParameterTypesIndex"/>
<stubElementTypeHolder class="com.intellij.psi.impl.java.stubs.JavaStubElementTypes"/>