mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[Johnny Clasrk] IsNull IsNotNull True False Checks and Assertions (IDEA-35808)
This commit is contained in:
@@ -0,0 +1,252 @@
|
||||
/*
|
||||
* Copyright 2000-2012 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;
|
||||
|
||||
import com.intellij.openapi.components.*;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.PsiMethod;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:johnnyclark@gmail.com">Johnny Clark</a>
|
||||
* Creation Date: 8/3/12
|
||||
*/
|
||||
@State(
|
||||
name = "IsNullIsNotNullCheckManager",
|
||||
storages = {@Storage(id = "dir", file = StoragePathMacros.PROJECT_CONFIG_DIR + "/checker.xml", scheme = StorageScheme.DIRECTORY_BASED)}
|
||||
)
|
||||
public class ConditionCheckManager implements PersistentStateComponent<ConditionCheckManager.State> {
|
||||
@SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"}) private State state;
|
||||
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.ConditionCheckManager");
|
||||
|
||||
private List<MethodConditionCheck> myIsNullCheckMethods = new ArrayList<MethodConditionCheck>();
|
||||
private List<MethodConditionCheck> myIsNotNullCheckMethods = new ArrayList<MethodConditionCheck>();
|
||||
|
||||
private List<MethodConditionCheck> myAssertIsNullMethods = new ArrayList<MethodConditionCheck>();
|
||||
private List<MethodConditionCheck> myAssertIsNotNullMethods = new ArrayList<MethodConditionCheck>();
|
||||
|
||||
private List<MethodConditionCheck> myAssertTrueMethods = new ArrayList<MethodConditionCheck>();
|
||||
private List<MethodConditionCheck> myAssertFalseMethods = new ArrayList<MethodConditionCheck>();
|
||||
|
||||
private static Project myProject;
|
||||
|
||||
public static ConditionCheckManager getInstance(Project project) {
|
||||
myProject = project;
|
||||
return ServiceManager.getService(project, ConditionCheckManager.class);
|
||||
}
|
||||
|
||||
public void setIsNullCheckMethods(List<MethodConditionCheck> methodConditionChecks) {
|
||||
myIsNullCheckMethods.clear();
|
||||
myIsNullCheckMethods.addAll(methodConditionChecks);
|
||||
}
|
||||
|
||||
public void setIsNotNullCheckMethods(List<MethodConditionCheck> methodConditionChecks) {
|
||||
myIsNotNullCheckMethods.clear();
|
||||
myIsNotNullCheckMethods.addAll(methodConditionChecks);
|
||||
}
|
||||
|
||||
public void setAssertNullMethods(List<MethodConditionCheck> methodConditionChecks) {
|
||||
myAssertIsNullMethods.clear();
|
||||
myAssertIsNullMethods.addAll(methodConditionChecks);
|
||||
}
|
||||
|
||||
public void setAssertNotNullMethods(List<MethodConditionCheck> methodConditionChecks) {
|
||||
myAssertIsNotNullMethods.clear();
|
||||
myAssertIsNotNullMethods.addAll(methodConditionChecks);
|
||||
}
|
||||
|
||||
public void setAssertTrueMethods(List<MethodConditionCheck> psiMethodWrappers) {
|
||||
myAssertTrueMethods.clear();
|
||||
myAssertTrueMethods.addAll(psiMethodWrappers);
|
||||
}
|
||||
|
||||
public void setAssertFalseMethods(List<MethodConditionCheck> psiMethodWrappers) {
|
||||
myAssertFalseMethods.clear();
|
||||
myAssertFalseMethods.addAll(psiMethodWrappers);
|
||||
}
|
||||
|
||||
public List<MethodConditionCheck> getIsNullCheckMethods() {
|
||||
return myIsNullCheckMethods;
|
||||
}
|
||||
|
||||
public List<MethodConditionCheck> getIsNotNullCheckMethods() {
|
||||
return myIsNotNullCheckMethods;
|
||||
}
|
||||
|
||||
public List<MethodConditionCheck> getAssertIsNullMethods() {
|
||||
return myAssertIsNullMethods;
|
||||
}
|
||||
|
||||
public List<MethodConditionCheck> getAssertIsNotNullMethods() {
|
||||
return myAssertIsNotNullMethods;
|
||||
}
|
||||
|
||||
public List<MethodConditionCheck> getAssertFalseMethods() {
|
||||
return myAssertFalseMethods;
|
||||
}
|
||||
|
||||
public List<MethodConditionCheck> getAssertTrueMethods() {
|
||||
return myAssertTrueMethods;
|
||||
}
|
||||
|
||||
public static class State {
|
||||
public List<String> myIsNullCheckMethods = new ArrayList<String>();
|
||||
public List<String> myIsNotNullCheckMethods = new ArrayList<String>();
|
||||
public List<String> myAssertIsNullMethods = new ArrayList<String>();
|
||||
public List<String> myAssertIsNotNullMethods = new ArrayList<String>();
|
||||
public List<String> myAssertTrueMethods = new ArrayList<String>();
|
||||
public List<String> myAssertFalseMethods = new ArrayList<String>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public State getState() {
|
||||
State state = new State();
|
||||
|
||||
loadMethodChecksToState(state.myIsNullCheckMethods, myIsNullCheckMethods);
|
||||
loadMethodChecksToState(state.myIsNotNullCheckMethods, myIsNotNullCheckMethods);
|
||||
loadMethodChecksToState(state.myAssertIsNullMethods, myAssertIsNullMethods);
|
||||
loadMethodChecksToState(state.myAssertIsNotNullMethods, myAssertIsNotNullMethods);
|
||||
loadMethodChecksToState(state.myAssertTrueMethods, myAssertTrueMethods);
|
||||
loadMethodChecksToState(state.myAssertFalseMethods, myAssertFalseMethods);
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
private static void loadMethodChecksToState(List<String> listToLoadTo, List<MethodConditionCheck> listToLoadFrom) {
|
||||
for (MethodConditionCheck checker : listToLoadFrom) {
|
||||
listToLoadTo.add(checker.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadState(State state) {
|
||||
this.state = state;
|
||||
loadMethods(myIsNullCheckMethods, state.myIsNullCheckMethods, ConditionChecker.Type.IS_NULL_METHOD);
|
||||
loadMethods(myIsNotNullCheckMethods, state.myIsNotNullCheckMethods, ConditionChecker.Type.IS_NOT_NULL_METHOD);
|
||||
loadMethods(myAssertIsNullMethods, state.myAssertIsNullMethods, ConditionChecker.Type.ASSERT_IS_NULL_METHOD);
|
||||
loadMethods(myAssertIsNotNullMethods, state.myAssertIsNotNullMethods, ConditionChecker.Type.ASSERT_IS_NOT_NULL_METHOD);
|
||||
loadMethods(myAssertTrueMethods, state.myAssertTrueMethods, ConditionChecker.Type.ASSERT_TRUE_METHOD);
|
||||
loadMethods(myAssertFalseMethods, state.myAssertFalseMethods, ConditionChecker.Type.ASSERT_FALSE_METHOD);
|
||||
}
|
||||
|
||||
public void loadMethods(List<MethodConditionCheck> listToLoadTo, List<String> listToLoadFrom, ConditionChecker.Type type){
|
||||
listToLoadTo.clear();
|
||||
for (String setting : listToLoadFrom) {
|
||||
try {
|
||||
listToLoadTo.add(new MethodConditionCheck.Builder(setting, type, myProject).build());
|
||||
} catch (Exception e) {
|
||||
LOG.error("Problem occurred while attempting to load Condition Check from configuration file. " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isMethod(@NotNull PsiMethod psiMethod, List<MethodConditionCheck> checkers) {
|
||||
for (MethodConditionCheck checker : checkers) {
|
||||
if (checker.matches(psiMethod)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean isCheck(PsiMethod psiMethod) {
|
||||
ConditionCheckManager manager = getInstance(psiMethod.getProject());
|
||||
return isMethod(psiMethod, manager.getIsNullCheckMethods()) ||
|
||||
isMethod(psiMethod, manager.getIsNotNullCheckMethods()) ||
|
||||
isMethod(psiMethod, manager.getAssertIsNullMethods()) ||
|
||||
isMethod(psiMethod, manager.getAssertIsNotNullMethods()) ||
|
||||
isAssertTrueCheckMethod(psiMethod) ||
|
||||
isAssertFalseCheckMethod(psiMethod);
|
||||
}
|
||||
|
||||
public static boolean isNullCheckMethod(PsiMethod psiMethod) {
|
||||
return methodMatches(psiMethod, getInstance(psiMethod.getProject()).getIsNullCheckMethods());
|
||||
}
|
||||
|
||||
public static boolean isNotNullCheckMethod(PsiMethod psiMethod) {
|
||||
return methodMatches(psiMethod, getInstance(psiMethod.getProject()).getIsNotNullCheckMethods());
|
||||
}
|
||||
|
||||
public static boolean isAssertIsNullCheckMethod(PsiMethod psiMethod) {
|
||||
return methodMatches(psiMethod, getInstance(psiMethod.getProject()).getAssertIsNullMethods());
|
||||
}
|
||||
|
||||
public static boolean isAssertIsNotNullCheckMethod(PsiMethod psiMethod) {
|
||||
return methodMatches(psiMethod, getInstance(psiMethod.getProject()).getAssertIsNotNullMethods());
|
||||
}
|
||||
|
||||
public static boolean isAssertTrueCheckMethod(PsiMethod psiMethod) {
|
||||
return methodMatches(psiMethod, getInstance(psiMethod.getProject()).getAssertTrueMethods());
|
||||
}
|
||||
|
||||
public static boolean isAssertFalseCheckMethod(PsiMethod psiMethod) {
|
||||
return methodMatches(psiMethod, getInstance(psiMethod.getProject()).getAssertFalseMethods());
|
||||
}
|
||||
|
||||
public static boolean isNullCheckMethod(PsiMethod psiMethod, int paramIndex) {
|
||||
return methodMatches(psiMethod, paramIndex, getInstance(psiMethod.getProject()).getIsNullCheckMethods());
|
||||
}
|
||||
|
||||
public static boolean isNotNullCheckMethod(PsiMethod psiMethod, int paramIndex) {
|
||||
return methodMatches(psiMethod, paramIndex, getInstance(psiMethod.getProject()).getIsNotNullCheckMethods());
|
||||
}
|
||||
|
||||
public static boolean isAssertIsNullCheckMethod(PsiMethod psiMethod, int paramIndex) {
|
||||
return methodMatches(psiMethod, paramIndex, getInstance(psiMethod.getProject()).getAssertIsNullMethods());
|
||||
}
|
||||
|
||||
public static boolean isAssertIsNotNullCheckMethod(PsiMethod psiMethod, int paramIndex) {
|
||||
return methodMatches(psiMethod, paramIndex, getInstance(psiMethod.getProject()).getAssertIsNotNullMethods());
|
||||
}
|
||||
|
||||
public static boolean isAssertTrueCheckMethod(PsiMethod psiMethod, int paramIndex) {
|
||||
return methodMatches(psiMethod, paramIndex, getInstance(psiMethod.getProject()).getAssertTrueMethods());
|
||||
}
|
||||
|
||||
public static boolean isAssertFalseCheckMethod(PsiMethod psiMethod, int paramIndex) {
|
||||
return methodMatches(psiMethod, paramIndex, getInstance(psiMethod.getProject()).getAssertFalseMethods());
|
||||
}
|
||||
|
||||
public static boolean methodMatches(PsiMethod psiMethod, List<MethodConditionCheck> checkers) {
|
||||
for (MethodConditionCheck checker : checkers) {
|
||||
if (checker.matches(psiMethod))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean methodMatches(PsiMethod psiMethod, int paramIndex, List<MethodConditionCheck> checkers) {
|
||||
for (MethodConditionCheck checker : checkers) {
|
||||
if (checker.matches(psiMethod, paramIndex))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean isNullCheck(PsiMethod psiMethod) {
|
||||
ConditionCheckManager manager = getInstance(psiMethod.getProject());
|
||||
return isMethod(psiMethod, manager.getIsNullCheckMethods()) || isMethod(psiMethod, manager.getAssertIsNullMethods());
|
||||
}
|
||||
|
||||
public static boolean isNotNullCheck(PsiMethod psiMethod) {
|
||||
ConditionCheckManager manager = getInstance(psiMethod.getProject());
|
||||
return isMethod(psiMethod, manager.getIsNotNullCheckMethods()) || isMethod(psiMethod, manager.getAssertIsNotNullMethods());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright 2000-2012 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;
|
||||
|
||||
import com.intellij.psi.*;
|
||||
|
||||
/**
|
||||
* Interface for IsNull, IsNotNull Method Checks and Assert True/False/IsNull/IsNotNull Method Checks to be performed by the Constant Condition Inspection.
|
||||
* These Checkers allow the user to specify that the method in question performs some type of validation on the parameter passed into the method.
|
||||
* For example, if the method is defined as performing an IsNotNull Check, and variable x is passed into the method, then all code after the method call
|
||||
* will assume that x is Not Null.
|
||||
*
|
||||
* @author <a href="mailto:johnnyclark@gmail.com">Johnny Clark</a>
|
||||
* Creation Date: 8/14/12
|
||||
*/
|
||||
public interface ConditionChecker {
|
||||
enum Type {
|
||||
IS_NULL_METHOD("IsNull Method"),
|
||||
IS_NOT_NULL_METHOD("IsNotNull Method"),
|
||||
ASSERT_IS_NULL_METHOD("Assert IsNull Method"),
|
||||
ASSERT_IS_NOT_NULL_METHOD("Assert IsNotNull Method"),
|
||||
ASSERT_TRUE_METHOD("Assert True Method"),
|
||||
ASSERT_FALSE_METHOD("Assert False Method");
|
||||
private final String myStringRepresentation;
|
||||
|
||||
Type(String stringRepresentation) {
|
||||
myStringRepresentation = stringRepresentation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return myStringRepresentation;
|
||||
}
|
||||
}
|
||||
|
||||
boolean matches(PsiMethod psiMethod);
|
||||
boolean matches(PsiMethod psiMethod, int paramIndex);
|
||||
|
||||
boolean overlaps(ConditionChecker checker);
|
||||
|
||||
Type getType();
|
||||
}
|
||||
@@ -0,0 +1,396 @@
|
||||
/*
|
||||
* Copyright 2000-2012 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;
|
||||
|
||||
import com.intellij.openapi.project.*;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.search.*;
|
||||
import org.jetbrains.annotations.*;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static com.intellij.codeInsight.ConditionChecker.Type.*;
|
||||
|
||||
/**
|
||||
* Used by Constant Condition Inspection to identify methods which perform some type of Validation on the parameters passed into them.
|
||||
* For example given the following method
|
||||
* <pre>
|
||||
* {@code
|
||||
* class Foo {
|
||||
* static boolean validateNotNull(Object o) {
|
||||
* if (o == null) return false;
|
||||
* else return true;
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* The corresponding MethodConditionCheck would be <p/>
|
||||
* myType=Type.IS_NOT_NULL_METHOD
|
||||
* myPsiClass=Foo
|
||||
* myPsiMethod=validateNotNull
|
||||
* myPsiParameter=o
|
||||
*
|
||||
* The following block of code would produce a Inspection Warning that o is always true
|
||||
*
|
||||
* <pre>
|
||||
* {@code
|
||||
* if (Value.isNotNull(o)) {
|
||||
* if(o != null) {}
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @author <a href="mailto:johnnyclark@gmail.com">Johnny Clark</a>
|
||||
* Creation Date: 8/14/12
|
||||
*/
|
||||
public class MethodConditionCheck implements ConditionChecker, Comparable<MethodConditionCheck> {
|
||||
private final @NotNull Type myType;
|
||||
private final @NotNull PsiClass myPsiClass;
|
||||
private final @NotNull PsiMethod myPsiMethod;
|
||||
private final @NotNull PsiParameter myPsiParameter;
|
||||
private final String fullName;
|
||||
private final String shortName;
|
||||
|
||||
public MethodConditionCheck(@NotNull PsiMethod psiMethod, @NotNull PsiParameter psiParameter, @NotNull Type type) {
|
||||
myPsiMethod = psiMethod;
|
||||
myPsiParameter = psiParameter;
|
||||
if (type != IS_NULL_METHOD && type != IS_NOT_NULL_METHOD &&
|
||||
type != ASSERT_IS_NULL_METHOD && type != ASSERT_IS_NOT_NULL_METHOD &&
|
||||
type != ASSERT_TRUE_METHOD && type != ASSERT_FALSE_METHOD)
|
||||
throw new IllegalArgumentException("Type is invalid " + type);
|
||||
|
||||
PsiClass containingClass = psiMethod.getContainingClass();
|
||||
if (containingClass == null)
|
||||
throw new IllegalArgumentException("PsiMethod has null Containing Class");
|
||||
|
||||
myPsiClass = containingClass;
|
||||
myType = type;
|
||||
|
||||
validatePsiMethod();
|
||||
String className = initClassNameFromPsiMethod();
|
||||
String methodName = initMethodNameFromPsiMethod();
|
||||
List<String> parameters = initParameterNamesFromPsiMethod(myPsiParameter);
|
||||
fullName = initFullName(className, methodName, parameters);
|
||||
shortName = initShortName(methodName, parameters);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(PsiMethod psiMethod) {
|
||||
if (myPsiMethod.equals(psiMethod)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// The equals method in PsiMethod compares to see if they are the same object, but sometimes they are not the same object but do represent the same method
|
||||
if (!myPsiMethod.getName().equals(psiMethod.getName())) return false;
|
||||
|
||||
PsiClass myContainingClass = myPsiMethod.getContainingClass();
|
||||
PsiClass containingClass = myPsiMethod.getContainingClass();
|
||||
if (myContainingClass == null && containingClass != null) return false;
|
||||
if (myContainingClass != null && containingClass == null) return false;
|
||||
if (myContainingClass != null) { // Both must be non-null
|
||||
String myQualifiedName = myContainingClass.getQualifiedName();
|
||||
String qualifiedName = containingClass.getQualifiedName();
|
||||
if (myQualifiedName == null && qualifiedName != null) return false;
|
||||
if (myQualifiedName != null && qualifiedName == null) return false;
|
||||
if (myQualifiedName != null && !myQualifiedName.equals(qualifiedName)) return false;
|
||||
}
|
||||
|
||||
PsiParameterList myPsiParameterList = myPsiMethod.getParameterList();
|
||||
PsiParameterList psiParameterList = psiMethod.getParameterList();
|
||||
if (myPsiParameterList.getParameters().length != psiParameterList.getParameters().length) return false;
|
||||
for (int i = 0; i < myPsiParameterList.getParameters().length; i++) {
|
||||
PsiParameter myPsiParameter = myPsiParameterList.getParameters()[i];
|
||||
PsiParameter psiParameter = psiParameterList.getParameters()[i];
|
||||
if (myPsiParameter == null && psiParameter != null) return false;
|
||||
if (myPsiParameter != null && psiParameter == null) return false;
|
||||
if (myPsiParameter != null) { // Both must be non-null
|
||||
PsiTypeElement myPsiTypeElement = myPsiParameter.getTypeElement();
|
||||
PsiTypeElement psiTypeElement = psiParameter.getTypeElement();
|
||||
if (myPsiTypeElement == null && psiTypeElement != null) return false;
|
||||
if (myPsiTypeElement != null && psiTypeElement == null) return false;
|
||||
if (myPsiTypeElement != null && myPsiTypeElement.getType() == psiTypeElement.getType()) return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(PsiMethod psiMethod, int paramIndex) {
|
||||
if (matches(psiMethod)) {
|
||||
PsiParameter[] parameters = myPsiMethod.getParameterList().getParameters();
|
||||
if (parameters.length <= paramIndex)
|
||||
return false;
|
||||
|
||||
PsiParameter parameter = parameters[paramIndex];
|
||||
if (parameter.equals(myPsiParameter))
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean overlaps(ConditionChecker checker) {
|
||||
MethodConditionCheck otherChecker = (MethodConditionCheck) checker;
|
||||
if (myPsiClass.equals(otherChecker.myPsiClass) && myPsiMethod.equals(otherChecker.myPsiMethod) && myPsiParameter.equals(otherChecker.myPsiParameter))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType() {
|
||||
return myType;
|
||||
}
|
||||
|
||||
private void validatePsiMethod() {
|
||||
PsiElement psiElement = myPsiMethod.getContainingClass();
|
||||
if (!(psiElement instanceof PsiClass))
|
||||
throw new IllegalArgumentException("PsiMethod " + myPsiMethod + " can not have a null containing class.");
|
||||
|
||||
PsiType returnType = myPsiMethod.getReturnType();
|
||||
if (!isAssert()) {
|
||||
if (returnType == null)
|
||||
throw new IllegalArgumentException("PsiMethod " + myPsiMethod + " has a null return type PsiType.");
|
||||
|
||||
if (returnType != PsiType.BOOLEAN && !returnType.getCanonicalText().equals(Boolean.class.toString())) {
|
||||
throw new IllegalArgumentException("PsiMethod " + myPsiMethod + " must have a null return type PsiType of boolean or Boolean.");
|
||||
}
|
||||
}
|
||||
|
||||
boolean parameterFound = false;
|
||||
for (int i = 0; i < myPsiMethod.getParameterList().getParameters().length; i++) {
|
||||
if (myPsiParameter.equals(myPsiMethod.getParameterList().getParameters()[i])) {
|
||||
parameterFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!parameterFound) {
|
||||
throw new IllegalArgumentException("PsiMethod " + myPsiMethod + " must have parameter " + getFullyQualifiedName(myPsiParameter));
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isAssert() {
|
||||
return myType == ASSERT_IS_NULL_METHOD || myType == ASSERT_IS_NOT_NULL_METHOD || myType == ASSERT_TRUE_METHOD || myType == ASSERT_FALSE_METHOD;
|
||||
}
|
||||
|
||||
private String initClassNameFromPsiMethod() {
|
||||
PsiElement psiElement = myPsiMethod.getContainingClass();
|
||||
PsiClass psiClass = (PsiClass) psiElement;
|
||||
return psiClass.getQualifiedName();
|
||||
}
|
||||
|
||||
private String initMethodNameFromPsiMethod() {
|
||||
return myPsiMethod.getName();
|
||||
}
|
||||
|
||||
private List<String> initParameterNamesFromPsiMethod(PsiParameter selectedParameter) {
|
||||
List<String> parameters = new ArrayList<String>();
|
||||
for (int i = 0; i < myPsiMethod.getParameterList().getParameters().length; i++) {
|
||||
PsiParameter param = myPsiMethod.getParameterList().getParameters()[i];
|
||||
String parameter = getFullyQualifiedName(param);
|
||||
if (param.equals(selectedParameter))
|
||||
parameters.add("*" + parameter + "*");
|
||||
else
|
||||
parameters.add(parameter);
|
||||
}
|
||||
return parameters;
|
||||
}
|
||||
|
||||
public static String getFullyQualifiedName(PsiParameter psiParameter) {
|
||||
PsiTypeElement typeElement = psiParameter.getTypeElement();
|
||||
if (typeElement == null)
|
||||
throw new RuntimeException("Parameter has null typeElement " + psiParameter.getName());
|
||||
|
||||
PsiType psiType = typeElement.getType();
|
||||
|
||||
return psiType.getCanonicalText() + " " + psiParameter.getName();
|
||||
}
|
||||
|
||||
private String initFullName(String className, String methodName, List<String> parameters) {
|
||||
String s = className + "." + methodName + "(";
|
||||
for (String parameterName : parameters) {
|
||||
s += parameterName + ", ";
|
||||
}
|
||||
s = s.substring(0, s.length() - 2);
|
||||
s += ")";
|
||||
return s;
|
||||
}
|
||||
|
||||
private String initShortName(String methodName, List<String> parameterNames) {
|
||||
String shortName = methodName + "(";
|
||||
for (String parameterName : parameterNames) {
|
||||
if (parameterNames.lastIndexOf(".") > -1)
|
||||
shortName += parameterName.substring(parameterName.lastIndexOf(".") + 1) + ", ";
|
||||
else
|
||||
shortName += parameterName + ", ";
|
||||
}
|
||||
shortName = shortName.substring(0, shortName.lastIndexOf(", "));
|
||||
shortName += ")";
|
||||
return shortName;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public PsiMethod getPsiMethod() {
|
||||
return myPsiMethod;
|
||||
}
|
||||
|
||||
public String getShortName() {
|
||||
return shortName;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public PsiParameter getPsiParameter() {
|
||||
return myPsiParameter;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int compareTo(MethodConditionCheck o) {
|
||||
return fullName.compareToIgnoreCase(fullName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return fullName;
|
||||
}
|
||||
|
||||
static class Builder {
|
||||
private final @NotNull String serializedRepresentation;
|
||||
private final @NotNull Project project;
|
||||
private final @NotNull Type type;
|
||||
|
||||
|
||||
Builder(@NotNull String serializedRepresentation, @NotNull Type type, @NotNull Project project) {
|
||||
this.serializedRepresentation = serializedRepresentation;
|
||||
this.project = project;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
private MethodConditionCheck validateFullyQualifiedClassMethodAndParameterNameAndGetPsiMethod(String fullyQualifiedClassMethodAndParameterName) {
|
||||
String classNameAndMethodName = parseClassNameAndMethodName(fullyQualifiedClassMethodAndParameterName);
|
||||
|
||||
String className = classNameAndMethodName.substring(0, classNameAndMethodName.lastIndexOf("."));
|
||||
String methodName = classNameAndMethodName.substring(classNameAndMethodName.lastIndexOf(".") + 1);
|
||||
|
||||
String allParametersSubString = fullyQualifiedClassMethodAndParameterName.substring(fullyQualifiedClassMethodAndParameterName.indexOf("(") + 1, fullyQualifiedClassMethodAndParameterName.lastIndexOf(")")).trim();
|
||||
if (allParametersSubString.isEmpty()) {
|
||||
throw new IllegalArgumentException("Name should contain 1+ parameter (between opening and closing parenthesis). " + fullyQualifiedClassMethodAndParameterName);
|
||||
} else if (allParametersSubString.contains("*") && allParametersSubString.indexOf("*") == allParametersSubString.lastIndexOf("*")) {
|
||||
throw new IllegalArgumentException("Selected Parameter should be surrounded by asterisks. " + fullyQualifiedClassMethodAndParameterName);
|
||||
}
|
||||
|
||||
String parameterClassAndName = allParametersSubString.substring(allParametersSubString.indexOf("*") + 1, allParametersSubString.lastIndexOf("*")).trim();
|
||||
|
||||
PsiClass psiClass = JavaPsiFacade.getInstance(project).findClass(className, GlobalSearchScope.allScope(project));
|
||||
if (psiClass == null) {
|
||||
throw new IllegalArgumentException("Unable to locate class " + className + " which was parsed from full name " + fullyQualifiedClassMethodAndParameterName);
|
||||
}
|
||||
|
||||
List<PsiMethod> psiMethods = findPsiMethodsInPsiClassWithMatchingMethodName(psiClass, methodName);
|
||||
if (psiMethods.size() == 0) {
|
||||
throw new IllegalArgumentException("Unable to locate method in class " + className + " named " + methodName + ", which was parsed from full name " + fullyQualifiedClassMethodAndParameterName);
|
||||
}
|
||||
|
||||
PsiMethod psiMethod = findPsiMethodWithMatchingParameters(psiMethods, allParametersSubString);
|
||||
if (psiMethod == null) {
|
||||
throw new IllegalArgumentException("Unable to locate method in class " + className + " named " + methodName + " with a parameter named " + parameterClassAndName + " which was parsed from full name " + fullyQualifiedClassMethodAndParameterName + ". The following methods matched on method name but not parameter name " + psiMethods);
|
||||
}
|
||||
|
||||
PsiParameter psiParameter = null;
|
||||
for (int i = 0; i < psiMethod.getParameterList().getParameters().length; i++) {
|
||||
PsiParameter parameter = psiMethod.getParameterList().getParameters()[i];
|
||||
if (parameterClassAndName.equals(getFullyQualifiedName(parameter))) {
|
||||
psiParameter = parameter;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (psiParameter == null) {
|
||||
throw new IllegalArgumentException("Unable to locate parameter " + parameterClassAndName + " in class " + className + " named " + methodName + ", which was parsed from full name " + fullyQualifiedClassMethodAndParameterName);
|
||||
}
|
||||
|
||||
return new MethodConditionCheck(psiMethod, psiParameter, type);
|
||||
}
|
||||
|
||||
private String parseClassNameAndMethodName(String fullyQualifiedClassMethodAndParameterName) {
|
||||
if (!fullyQualifiedClassMethodAndParameterName.contains("(")) {
|
||||
throw new IllegalArgumentException("Name should contain a opening parenthesis. " + fullyQualifiedClassMethodAndParameterName);
|
||||
} else if (!fullyQualifiedClassMethodAndParameterName.contains(")")) {
|
||||
throw new IllegalArgumentException("Name should contain a closing parenthesis. " + fullyQualifiedClassMethodAndParameterName);
|
||||
} else if (fullyQualifiedClassMethodAndParameterName.indexOf("(", fullyQualifiedClassMethodAndParameterName.indexOf("(") + 1) > -1) {
|
||||
throw new IllegalArgumentException("Name should only contain one opening parenthesis. " + fullyQualifiedClassMethodAndParameterName);
|
||||
} else if (fullyQualifiedClassMethodAndParameterName.indexOf(")", fullyQualifiedClassMethodAndParameterName.indexOf(")") + 1) > -1) {
|
||||
throw new IllegalArgumentException("Name should only contain one closing parenthesis. " + fullyQualifiedClassMethodAndParameterName);
|
||||
} else if (fullyQualifiedClassMethodAndParameterName.indexOf(")") < fullyQualifiedClassMethodAndParameterName.indexOf("(")) {
|
||||
throw new IllegalArgumentException("Opening parenthesis should precede closing parenthesis. " + fullyQualifiedClassMethodAndParameterName);
|
||||
}
|
||||
|
||||
String classNameAndMethodName = fullyQualifiedClassMethodAndParameterName.substring(0, fullyQualifiedClassMethodAndParameterName.indexOf("("));
|
||||
if (!classNameAndMethodName.contains(".")) {
|
||||
throw new IllegalArgumentException("Name should contain a dot between the class name and method name (before the opening parenthesis). " + fullyQualifiedClassMethodAndParameterName);
|
||||
}
|
||||
return classNameAndMethodName;
|
||||
}
|
||||
|
||||
private PsiMethod findPsiMethodWithMatchingParameters(List<PsiMethod> psiMethods, String allParametersSubString) {
|
||||
String[] parameterClassAndNameArray = allParametersSubString.split(",");
|
||||
List<String> parameterClassToMatch = new ArrayList<String>();
|
||||
for (String parameterClassAndName : parameterClassAndNameArray) {
|
||||
parameterClassAndName = parameterClassAndName.replace("*", "").trim();
|
||||
parameterClassAndName = parameterClassAndName.substring(0, parameterClassAndName.indexOf(" ")).trim();
|
||||
parameterClassToMatch.add(parameterClassAndName);
|
||||
}
|
||||
|
||||
for (PsiMethod method : psiMethods) {
|
||||
List<String> parameterForCurrentMethod = new ArrayList<String>();
|
||||
for (int i = 0; i < method.getParameterList().getParameters().length; i++) {
|
||||
PsiParameter psiParameter = method.getParameterList().getParameters()[i];
|
||||
PsiTypeElement typeElement = psiParameter.getTypeElement();
|
||||
if (typeElement == null)
|
||||
break;
|
||||
|
||||
PsiType psiType = typeElement.getType();
|
||||
parameterForCurrentMethod.add(psiType.getCanonicalText().trim());
|
||||
}
|
||||
|
||||
if (parameterForCurrentMethod.equals(parameterClassToMatch))
|
||||
return method;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private List<PsiMethod> findPsiMethodsInPsiClassWithMatchingMethodName(PsiClass psiClass, String methodName) {
|
||||
List<PsiMethod> psiMethods = new ArrayList<PsiMethod>();
|
||||
for (int i = 0; i < psiClass.getMethods().length; i++) {
|
||||
PsiMethod possibleMatchPsiMethod = psiClass.getMethods()[i];
|
||||
if (methodName.equals(possibleMatchPsiMethod.getName())) {
|
||||
psiMethods.add(possibleMatchPsiMethod);
|
||||
}
|
||||
}
|
||||
return psiMethods;
|
||||
}
|
||||
|
||||
public MethodConditionCheck build() {
|
||||
return validateFullyQualifiedClassMethodAndParameterNameAndGetPsiMethod(serializedRepresentation);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,264 @@
|
||||
/*
|
||||
* Copyright 2000-2012 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.codeInspection.dataFlow;
|
||||
|
||||
import com.intellij.codeInsight.*;
|
||||
import com.intellij.codeInspection.*;
|
||||
import com.intellij.openapi.project.*;
|
||||
import com.intellij.openapi.ui.*;
|
||||
import com.intellij.ui.*;
|
||||
import com.intellij.ui.components.*;
|
||||
import org.jetbrains.annotations.*;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Dialog that appears when user clicks the "Configure IsNull/IsNotNull/True/False Check/Assertion Methods"
|
||||
* on the Errors dialog for the Constant Conditions Inspection. It is divided into 6 parts
|
||||
* <ol>
|
||||
* <li>Is Null Check MethodsPanel</li>
|
||||
* <li>Is Not Null Check MethodsPanel</li>
|
||||
* <li>Assert Is Null MethodsPanel</li>
|
||||
* <li>Assert Is Not Null MethodsPanel</li>
|
||||
* <li>Assert True MethodsPanel</li>
|
||||
* <li>Assert False MethodsPanel</li>
|
||||
* </ol>
|
||||
*
|
||||
* @author <a href="mailto:johnnyclark@gmail.com">Johnny Clark</a>
|
||||
* Creation Date: 8/3/12
|
||||
*/
|
||||
public class ConditionCheckDialog extends DialogWrapper {
|
||||
private final Project myProject;
|
||||
private final @NotNull Splitter mainSplitter;
|
||||
private final @NotNull MethodsPanel myIsNullCheckMethodPanel;
|
||||
private final @NotNull MethodsPanel myIsNotNullCheckMethodPanel;
|
||||
private final @NotNull MethodsPanel myAssertIsNullMethodPanel;
|
||||
private final @NotNull MethodsPanel myAssertIsNotNullMethodPanel;
|
||||
private final @NotNull MethodsPanel myAssertTrueMethodPanel;
|
||||
private final @NotNull MethodsPanel myAssertFalseMethodPanel;
|
||||
|
||||
public ConditionCheckDialog(Project project, String mainDialogTitle) {
|
||||
super(project, true);
|
||||
myProject = project;
|
||||
|
||||
final ConditionCheckManager manager = ConditionCheckManager.getInstance(myProject);
|
||||
mainSplitter = new Splitter(true, 0.3f);
|
||||
final Splitter topThirdSplitter = new Splitter(false);
|
||||
final Splitter bottomTwoThirdsSplitter = new Splitter(true);
|
||||
final Splitter isNullIsNotNullCheckMethodSplitter = new Splitter(false);
|
||||
final Splitter assertTrueFalseMethodSplitter = new Splitter(false);
|
||||
|
||||
List<MethodConditionCheck> isNullCheckMethods = new ArrayList<MethodConditionCheck>(manager.getIsNullCheckMethods());
|
||||
List<MethodConditionCheck> isNotNullCheckMethods = new ArrayList<MethodConditionCheck>(manager.getIsNotNullCheckMethods());
|
||||
List<MethodConditionCheck> assertIsNullMethods = new ArrayList<MethodConditionCheck>(manager.getAssertIsNullMethods());
|
||||
List<MethodConditionCheck> assertIsNotNullMethods = new ArrayList<MethodConditionCheck>(manager.getAssertIsNotNullMethods());
|
||||
List<MethodConditionCheck> assertTrueMethods = new ArrayList<MethodConditionCheck>(manager.getAssertTrueMethods());
|
||||
List<MethodConditionCheck> assertFalseMethods = new ArrayList<MethodConditionCheck>(manager.getAssertFalseMethods());
|
||||
|
||||
myAssertIsNullMethodPanel = new MethodsPanel(assertIsNullMethods, ConditionChecker.Type.ASSERT_IS_NULL_METHOD, myProject);
|
||||
myAssertIsNotNullMethodPanel = new MethodsPanel(assertIsNotNullMethods, ConditionChecker.Type.ASSERT_IS_NOT_NULL_METHOD, myProject);
|
||||
myIsNullCheckMethodPanel = new MethodsPanel(isNullCheckMethods, ConditionChecker.Type.IS_NULL_METHOD, myProject);
|
||||
myIsNotNullCheckMethodPanel = new MethodsPanel(isNotNullCheckMethods, ConditionChecker.Type.IS_NOT_NULL_METHOD, myProject);
|
||||
myAssertTrueMethodPanel = new MethodsPanel(assertTrueMethods, ConditionChecker.Type.ASSERT_TRUE_METHOD, myProject);
|
||||
myAssertFalseMethodPanel = new MethodsPanel(assertFalseMethods, ConditionChecker.Type.ASSERT_FALSE_METHOD, myProject);
|
||||
|
||||
isNullIsNotNullCheckMethodSplitter.setFirstComponent(myIsNullCheckMethodPanel.getComponent());
|
||||
isNullIsNotNullCheckMethodSplitter.setSecondComponent(myIsNotNullCheckMethodPanel.getComponent());
|
||||
assertTrueFalseMethodSplitter.setFirstComponent(myAssertTrueMethodPanel.getComponent());
|
||||
assertTrueFalseMethodSplitter.setSecondComponent(myAssertFalseMethodPanel.getComponent());
|
||||
|
||||
topThirdSplitter.setFirstComponent(myAssertIsNullMethodPanel.getComponent());
|
||||
topThirdSplitter.setSecondComponent(myAssertIsNotNullMethodPanel.getComponent());
|
||||
bottomTwoThirdsSplitter.setFirstComponent(isNullIsNotNullCheckMethodSplitter);
|
||||
bottomTwoThirdsSplitter.setSecondComponent(assertTrueFalseMethodSplitter);
|
||||
|
||||
mainSplitter.setFirstComponent(topThirdSplitter);
|
||||
mainSplitter.setSecondComponent(bottomTwoThirdsSplitter);
|
||||
|
||||
topThirdSplitter.setPreferredSize(new Dimension(600, 400));
|
||||
bottomTwoThirdsSplitter.setPreferredSize(new Dimension(600, 800));
|
||||
|
||||
myAssertIsNullMethodPanel.setOtherMethodsPanels(myAssertIsNotNullMethodPanel, myIsNullCheckMethodPanel, myIsNotNullCheckMethodPanel, myAssertTrueMethodPanel, myAssertFalseMethodPanel);
|
||||
myAssertIsNotNullMethodPanel.setOtherMethodsPanels(myAssertIsNullMethodPanel, myIsNullCheckMethodPanel, myIsNotNullCheckMethodPanel, myAssertTrueMethodPanel, myAssertFalseMethodPanel);
|
||||
myIsNullCheckMethodPanel.setOtherMethodsPanels(myAssertIsNullMethodPanel, myAssertIsNotNullMethodPanel, myIsNotNullCheckMethodPanel, myAssertTrueMethodPanel, myAssertFalseMethodPanel);
|
||||
myIsNotNullCheckMethodPanel.setOtherMethodsPanels(myAssertIsNullMethodPanel, myAssertIsNotNullMethodPanel, myIsNullCheckMethodPanel, myAssertTrueMethodPanel, myAssertFalseMethodPanel);
|
||||
myAssertTrueMethodPanel.setOtherMethodsPanels(myAssertIsNullMethodPanel, myAssertIsNotNullMethodPanel, myIsNotNullCheckMethodPanel, myIsNullCheckMethodPanel, myAssertFalseMethodPanel);
|
||||
myAssertFalseMethodPanel.setOtherMethodsPanels(myAssertIsNullMethodPanel, myAssertIsNotNullMethodPanel, myIsNotNullCheckMethodPanel, myIsNullCheckMethodPanel, myAssertTrueMethodPanel);
|
||||
|
||||
init();
|
||||
setTitle(mainDialogTitle);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JComponent createCenterPanel() {
|
||||
return mainSplitter;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doOKAction() {
|
||||
final ConditionCheckManager manager = ConditionCheckManager.getInstance(myProject);
|
||||
manager.setIsNotNullCheckMethods(myIsNotNullCheckMethodPanel.getMethodConditionChecker());
|
||||
manager.setIsNullCheckMethods(myIsNullCheckMethodPanel.getMethodConditionChecker());
|
||||
manager.setAssertNotNullMethods(myAssertIsNotNullMethodPanel.getMethodConditionChecker());
|
||||
manager.setAssertNullMethods(myAssertIsNullMethodPanel.getMethodConditionChecker());
|
||||
manager.setAssertTrueMethods(myAssertTrueMethodPanel.getMethodConditionChecker());
|
||||
manager.setAssertFalseMethods(myAssertFalseMethodPanel.getMethodConditionChecker());
|
||||
|
||||
super.doOKAction();
|
||||
}
|
||||
|
||||
/**
|
||||
* Is Null, Is Not Null, Assert True and Assert False Method Panel at the top of the main Dialog.
|
||||
*/
|
||||
class MethodsPanel {
|
||||
private final @NotNull JBList myList;
|
||||
private final @NotNull JPanel myPanel;
|
||||
private final @NotNull Project myProject;
|
||||
private Set<MethodsPanel> otherPanels;
|
||||
|
||||
public MethodsPanel(final List<MethodConditionCheck> checkers, final ConditionChecker.Type type, final Project myProject) {
|
||||
this.myProject = myProject;
|
||||
myList = new JBList(new CollectionListModel<MethodConditionCheck>(checkers));
|
||||
myPanel = new JPanel(new BorderLayout());
|
||||
myPanel.setBorder(IdeBorderFactory.createTitledBorder(initTitle(type), false, new Insets(10, 0, 0, 0)));
|
||||
myPanel.setPreferredSize(new Dimension(500, 500));
|
||||
|
||||
myList.setCellRenderer(new ColoredListCellRenderer() {
|
||||
@Override
|
||||
protected void customizeCellRenderer(JList list, Object value, int index, boolean selected, boolean hasFocus) {
|
||||
String s = value.toString();
|
||||
if (s.contains("*")) {
|
||||
int indexOfAsterix1 = s.indexOf("*");
|
||||
int indexOfAsterix2 = s.lastIndexOf("*");
|
||||
if (indexOfAsterix1 >= 0 && indexOfAsterix1 < s.length() && indexOfAsterix2 >= 0 && indexOfAsterix2 < s.length() && indexOfAsterix1 < indexOfAsterix2) {
|
||||
append(s.substring(0, indexOfAsterix1), SimpleTextAttributes.REGULAR_ATTRIBUTES);
|
||||
append(s.substring(indexOfAsterix1 + 1, indexOfAsterix2), SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES);
|
||||
append(s.substring(indexOfAsterix2 + 1), SimpleTextAttributes.REGULAR_ATTRIBUTES);
|
||||
} else {
|
||||
append(s, SimpleTextAttributes.REGULAR_ATTRIBUTES);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
final ToolbarDecorator toolbarDecorator = ToolbarDecorator.createDecorator(myList).disableUpDownActions()
|
||||
.setAddAction(new AnActionButtonRunnable() {
|
||||
@Override
|
||||
public void run(AnActionButton anActionButton) {
|
||||
chooseMethod(null, type, myList.getModel().getSize());
|
||||
}
|
||||
})
|
||||
.setRemoveAction(new AnActionButtonRunnable() {
|
||||
@Override
|
||||
public void run(AnActionButton anActionButton) {
|
||||
CollectionListModel model = getCollectionListModel();
|
||||
if (myList.getSelectedIndex() >= 0 && myList.getSelectedIndex() < model.getSize()) {
|
||||
model.remove(myList.getSelectedIndex());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
myList.addMouseListener(
|
||||
new MouseAdapter() {
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
if (e.getClickCount() == 2) {
|
||||
int index = myList.locationToIndex(e.getPoint());
|
||||
CollectionListModel<MethodConditionCheck> model = getCollectionListModel();
|
||||
if (index >= 0 && model.getSize() > index) {
|
||||
chooseMethod(model.getElementAt(index), type, index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
final JPanel panel = toolbarDecorator.createPanel();
|
||||
myPanel.add(panel);
|
||||
myList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||
}
|
||||
|
||||
private String initTitle(@NotNull ConditionChecker.Type type) {
|
||||
if (type.equals(ConditionChecker.Type.IS_NULL_METHOD))
|
||||
return InspectionsBundle.message("configure.checker.option.isNull.method.panel.title");
|
||||
else if (type.equals(ConditionChecker.Type.IS_NOT_NULL_METHOD))
|
||||
return InspectionsBundle.message("configure.checker.option.isNotNull.method.panel.title");
|
||||
else if (type.equals(ConditionChecker.Type.ASSERT_IS_NULL_METHOD))
|
||||
return InspectionsBundle.message("configure.checker.option.assert.isNull.method.panel.title");
|
||||
else if (type.equals(ConditionChecker.Type.ASSERT_IS_NOT_NULL_METHOD))
|
||||
return InspectionsBundle.message("configure.checker.option.assert.isNotNull.method.panel.title");
|
||||
else if (type.equals(ConditionChecker.Type.ASSERT_TRUE_METHOD))
|
||||
return InspectionsBundle.message("configure.checker.option.assert.true.method.panel.title");
|
||||
else if (type.equals(ConditionChecker.Type.ASSERT_FALSE_METHOD))
|
||||
return InspectionsBundle.message("configure.checker.option.assert.false.method.panel.title");
|
||||
else
|
||||
throw new IllegalArgumentException("MethodCheckerDetailsDialog does not support type " + type);
|
||||
}
|
||||
|
||||
private void chooseMethod(@Nullable MethodConditionCheck checker, ConditionChecker.Type type, int index) {
|
||||
MethodCheckerDetailsDialog pickMethodPanel = new MethodCheckerDetailsDialog(checker, type, myProject, myPanel, getConditionCheckers(), getOtherCheckers());
|
||||
pickMethodPanel.show();
|
||||
MethodConditionCheck chk = pickMethodPanel.getMethodConditionChecker();
|
||||
if (chk != null) {
|
||||
CollectionListModel<MethodConditionCheck> model = getCollectionListModel();
|
||||
if (model.getSize() <= index)
|
||||
model.add(chk);
|
||||
else
|
||||
model.setElementAt(chk, index);
|
||||
}
|
||||
}
|
||||
|
||||
private CollectionListModel<MethodConditionCheck> getCollectionListModel() {
|
||||
//noinspection unchecked
|
||||
return (CollectionListModel<MethodConditionCheck>) myList.getModel();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public JPanel getComponent() {
|
||||
return myPanel;
|
||||
}
|
||||
|
||||
public List<MethodConditionCheck> getMethodConditionChecker() {
|
||||
CollectionListModel<MethodConditionCheck> model = getCollectionListModel();
|
||||
return new ArrayList<MethodConditionCheck>(model.getItems());
|
||||
}
|
||||
|
||||
public Set<ConditionChecker> getConditionCheckers() {
|
||||
Set<ConditionChecker> set = new HashSet<ConditionChecker>();
|
||||
set.addAll(getMethodConditionChecker());
|
||||
return set;
|
||||
}
|
||||
|
||||
public void setOtherMethodsPanels(MethodsPanel p1, MethodsPanel p2, MethodsPanel p3, MethodsPanel p4, MethodsPanel p5) {
|
||||
otherPanels = new HashSet<MethodsPanel>();
|
||||
otherPanels.add(p1);
|
||||
otherPanels.add(p2);
|
||||
otherPanels.add(p3);
|
||||
otherPanels.add(p4);
|
||||
otherPanels.add(p5);
|
||||
}
|
||||
|
||||
public Set<ConditionChecker> getOtherCheckers() {
|
||||
Set<ConditionChecker> otherCheckers = new HashSet<ConditionChecker>();
|
||||
for (MethodsPanel otherPanel : otherPanels) {
|
||||
otherCheckers.addAll(otherPanel.getConditionCheckers());
|
||||
}
|
||||
return otherCheckers;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package com.intellij.codeInspection.dataFlow;
|
||||
|
||||
import com.intellij.codeInsight.ConditionCheckManager;
|
||||
import com.intellij.codeInsight.ExceptionUtil;
|
||||
import com.intellij.codeInspection.dataFlow.instructions.*;
|
||||
import com.intellij.codeInspection.dataFlow.value.*;
|
||||
@@ -1342,6 +1343,82 @@ class ControlFlowAnalyzer extends JavaElementVisitor {
|
||||
}
|
||||
}
|
||||
|
||||
if (ConditionCheckManager.isCheck(resolved)) {
|
||||
if (ConditionCheckManager.isAssertIsNullCheckMethod(resolved)) {
|
||||
int paramIndex = 0;
|
||||
for (PsiExpression param : params) {
|
||||
param.accept(this);
|
||||
if (ConditionCheckManager.isAssertIsNullCheckMethod(resolved, paramIndex++)) {
|
||||
addInstruction(new PushInstruction(myFactory.getConstFactory().getNull(), null));
|
||||
addInstruction(new BinopInstruction(JavaTokenType.EQEQ, null, expression.getProject()));
|
||||
conditionalExit(exitPoint, false); // Exit if Equal NULL is True
|
||||
} else {
|
||||
addInstruction(new PopInstruction());
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} else if (ConditionCheckManager.isAssertIsNotNullCheckMethod(resolved)) {
|
||||
int paramIndex = 0;
|
||||
for (PsiExpression param : params) {
|
||||
param.accept(this);
|
||||
if (ConditionCheckManager.isAssertIsNotNullCheckMethod(resolved, paramIndex++)) {
|
||||
addInstruction(new PushInstruction(myFactory.getConstFactory().getNull(), null));
|
||||
addInstruction(new BinopInstruction(JavaTokenType.EQEQ, null, expression.getProject()));
|
||||
conditionalExit(exitPoint, true); // Exit if NotEqual NULL is True
|
||||
} else {
|
||||
addInstruction(new PopInstruction());
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} else if (ConditionCheckManager.isNullCheckMethod(resolved)) {
|
||||
int paramIndex = 0;
|
||||
for (PsiExpression param : params) {
|
||||
param.accept(this);
|
||||
if (ConditionCheckManager.isNullCheckMethod(resolved, paramIndex++)) {
|
||||
addInstruction(new PushInstruction(myFactory.getConstFactory().getNull(), null));
|
||||
addInstruction(new BinopInstruction(JavaTokenType.EQEQ, null, expression.getProject()));
|
||||
} else {
|
||||
addInstruction(new PopInstruction());
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} else if (ConditionCheckManager.isNotNullCheckMethod(resolved)) {
|
||||
int paramIndex = 0;
|
||||
for (PsiExpression param : params) {
|
||||
param.accept(this);
|
||||
if (ConditionCheckManager.isNotNullCheckMethod(resolved, paramIndex++)) {
|
||||
addInstruction(new PushInstruction(myFactory.getConstFactory().getNull(), null));
|
||||
addInstruction(new BinopInstruction(JavaTokenType.NE, null, expression.getProject()));
|
||||
} else {
|
||||
addInstruction(new PopInstruction());
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} else if (ConditionCheckManager.isAssertTrueCheckMethod(resolved)) {
|
||||
int paramIndex = 0;
|
||||
for (PsiExpression param : params) {
|
||||
param.accept(this);
|
||||
if (ConditionCheckManager.isAssertTrueCheckMethod(resolved, paramIndex++)) {
|
||||
conditionalExit(exitPoint, false);
|
||||
} else {
|
||||
addInstruction(new PopInstruction());
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} else if (ConditionCheckManager.isAssertFalseCheckMethod(resolved)) {
|
||||
int paramIndex = 0;
|
||||
for (PsiExpression param : params) {
|
||||
param.accept(this);
|
||||
if (ConditionCheckManager.isAssertFalseCheckMethod(resolved, paramIndex++)) {
|
||||
conditionalExit(exitPoint, true);
|
||||
} else {
|
||||
addInstruction(new PopInstruction());
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Idea project only.
|
||||
if (qualifierExpression != null) {
|
||||
if (qualifierExpression.textMatches("LOG")) {
|
||||
|
||||
@@ -587,6 +587,24 @@ public class DataFlowInspection extends BaseLocalInspectionTool {
|
||||
gc.insets.bottom = 15;
|
||||
add(configureAnnotations, gc);
|
||||
|
||||
final JButton configureCheckAnnotations = new JButton(InspectionsBundle.message("configure.checker.option.button"));
|
||||
configureCheckAnnotations.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
Project project = PlatformDataKeys.PROJECT.getData(DataManager.getInstance().getDataContext(OptionsPanel.this));
|
||||
if (project == null) project = ProjectManager.getInstance().getDefaultProject();
|
||||
final ConditionCheckDialog dialog = new ConditionCheckDialog(project,
|
||||
InspectionsBundle.message("configure.checker.option.main.dialog.title")
|
||||
);
|
||||
dialog.show();
|
||||
}
|
||||
});
|
||||
gc.gridy++;
|
||||
gc.fill = GridBagConstraints.NONE;
|
||||
gc.insets.left = 20;
|
||||
gc.insets.bottom = 15;
|
||||
add(configureCheckAnnotations, gc);
|
||||
|
||||
gc.fill = GridBagConstraints.HORIZONTAL;
|
||||
gc.weighty = 1;
|
||||
gc.insets.left = 0;
|
||||
|
||||
+621
@@ -0,0 +1,621 @@
|
||||
/*
|
||||
* Copyright 2000-2012 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.codeInspection.dataFlow;
|
||||
|
||||
import com.intellij.codeInsight.*;
|
||||
import com.intellij.codeInspection.*;
|
||||
import com.intellij.ide.util.*;
|
||||
import com.intellij.openapi.editor.event.*;
|
||||
import com.intellij.openapi.project.*;
|
||||
import com.intellij.openapi.ui.*;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.search.*;
|
||||
import com.intellij.ui.*;
|
||||
import org.jetbrains.annotations.*;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import java.beans.*;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
|
||||
import static com.intellij.codeInsight.ConditionChecker.Type.*;
|
||||
|
||||
/**
|
||||
* Dialog that appears when the user clicks the Add Button or double clicks a row item in a MethodsPanel. The MethodsPanel is accessed from the ConditionCheckDialog
|
||||
*/
|
||||
class MethodCheckerDetailsDialog extends DialogWrapper implements PropertyChangeListener, ItemListener {
|
||||
private final @NotNull ConditionChecker.Type myType;
|
||||
private final @NotNull Project myProject;
|
||||
private final @NotNull ParameterDropDown parameterDropDown;
|
||||
private final @NotNull MethodDropDown methodDropDown;
|
||||
private final @NotNull ClassField classField;
|
||||
private final @NotNull Set<ConditionChecker> myOtherCheckers;
|
||||
private final @Nullable MethodConditionCheck myPreviouslySelectedChecker;
|
||||
|
||||
/**
|
||||
* Set by the OK and/or Cancel actions so that the caller can retrieve it via a call to getMethodIsNullIsNotNullChecker
|
||||
*/
|
||||
private @Nullable MethodConditionCheck mySelectedChecker;
|
||||
|
||||
MethodCheckerDetailsDialog(@Nullable MethodConditionCheck previouslySelectedChecker, @NotNull ConditionChecker.Type type, @NotNull Project project, @NotNull Component component, @NotNull Set<ConditionChecker> otherCheckersSameType, @NotNull Set<ConditionChecker> otherCheckers) {
|
||||
super(component, true);
|
||||
if (!isSupported(type))
|
||||
throw new IllegalArgumentException("Type is invalid " + type);
|
||||
|
||||
myProject = project;
|
||||
myType = type;
|
||||
myOtherCheckers = new HashSet<ConditionChecker>(otherCheckersSameType);
|
||||
myOtherCheckers.addAll(otherCheckers);
|
||||
myPreviouslySelectedChecker = previouslySelectedChecker;
|
||||
if (myPreviouslySelectedChecker != null)
|
||||
myOtherCheckers.remove(myPreviouslySelectedChecker);
|
||||
|
||||
PsiClass psiClass = null;
|
||||
PsiMethod psiMethod = null;
|
||||
PsiParameter psiParameter = null;
|
||||
if (previouslySelectedChecker != null) {
|
||||
psiMethod = previouslySelectedChecker.getPsiMethod();
|
||||
psiClass = psiMethod.getContainingClass();
|
||||
psiParameter = previouslySelectedChecker.getPsiParameter();
|
||||
}
|
||||
|
||||
classField = new ClassField(myProject, psiClass);
|
||||
methodDropDown = new MethodDropDown(psiClass, psiMethod, myType, MethodDropDown.buildModel());
|
||||
parameterDropDown = new ParameterDropDown(psiMethod, psiParameter, ParameterDropDown.buildModel(), myType);
|
||||
classField.addPropertyChangeListener(methodDropDown);
|
||||
classField.addPropertyChangeListener(parameterDropDown);
|
||||
classField.addPropertyChangeListener(this);
|
||||
methodDropDown.addItemListener(parameterDropDown);
|
||||
methodDropDown.addItemListener(this);
|
||||
parameterDropDown.addItemListener(this);
|
||||
init();
|
||||
checkOkActionEnable();
|
||||
setTitle(initTitle(type));
|
||||
}
|
||||
|
||||
private static boolean isSupported(ConditionChecker.Type type) {
|
||||
return type == IS_NULL_METHOD || type == IS_NOT_NULL_METHOD ||
|
||||
type == ASSERT_IS_NULL_METHOD || type == ASSERT_IS_NOT_NULL_METHOD ||
|
||||
type == ASSERT_TRUE_METHOD || type == ASSERT_FALSE_METHOD;
|
||||
}
|
||||
|
||||
private String initTitle(@NotNull ConditionChecker.Type type) {
|
||||
if (type.equals(IS_NULL_METHOD))
|
||||
return InspectionsBundle.message("configure.checker.option.isNull.add.method.checker.dialog.title");
|
||||
else if (type.equals(IS_NOT_NULL_METHOD))
|
||||
return InspectionsBundle.message("configure.checker.option.isNotNull.add.method.checker.dialog.title");
|
||||
else if (type.equals(ASSERT_IS_NULL_METHOD))
|
||||
return InspectionsBundle.message("configure.checker.option.assert.isNull.add.method.checker.dialog.title");
|
||||
else if (type.equals(ASSERT_IS_NOT_NULL_METHOD))
|
||||
return InspectionsBundle.message("configure.checker.option.assert.isNotNull.add.method.checker.dialog.title");
|
||||
else if (type.equals(ASSERT_TRUE_METHOD))
|
||||
return InspectionsBundle.message("configure.checker.option.assert.true.add.method.checker.dialog.title");
|
||||
else if (type.equals(ASSERT_FALSE_METHOD))
|
||||
return InspectionsBundle.message("configure.checker.option.assert.false.add.method.checker.dialog.title");
|
||||
else
|
||||
throw new IllegalArgumentException("MethodCheckerDetailsDialog does not support type " + type);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JComponent createCenterPanel() {
|
||||
final JPanel panel = new JPanel();
|
||||
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
|
||||
|
||||
final LabeledComponent<ClassField> classComponent = new LabeledComponent<ClassField>();
|
||||
final LabeledComponent<MethodDropDown> methodComponent = new LabeledComponent<MethodDropDown>();
|
||||
final LabeledComponent<ParameterDropDown> parameterComponent = new LabeledComponent<ParameterDropDown>();
|
||||
|
||||
classComponent.setText("Class");
|
||||
methodComponent.setText("Method");
|
||||
parameterComponent.setText("Parameter");
|
||||
|
||||
classComponent.setComponent(classField);
|
||||
methodComponent.setComponent(methodDropDown);
|
||||
parameterComponent.setComponent(parameterDropDown);
|
||||
|
||||
panel.add(classComponent);
|
||||
panel.add(methodComponent);
|
||||
panel.add(parameterComponent);
|
||||
|
||||
return panel;
|
||||
}
|
||||
|
||||
MethodConditionCheck getMethodConditionChecker() {
|
||||
return mySelectedChecker;
|
||||
}
|
||||
|
||||
private MethodConditionCheck buildMethodConditionChecker() {
|
||||
PsiClass psiClass = classField.getPsiClass();
|
||||
PsiMethod psiMethod = methodDropDown.getSelectedPsiMethod();
|
||||
PsiParameter psiParameter = parameterDropDown.getSelectedPsiParameter();
|
||||
if (psiClass != null && psiMethod != null && psiParameter != null) {
|
||||
return new MethodConditionCheck(psiMethod, psiParameter, myType);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean overlaps() {
|
||||
MethodConditionCheck thisChecker = buildMethodConditionChecker();
|
||||
for (ConditionChecker overlappingChecker : myOtherCheckers) {
|
||||
if (thisChecker.overlaps(overlappingChecker)) {
|
||||
Messages.showMessageDialog(myProject,
|
||||
InspectionsBundle.message("configure.checker.option.overlap.error.msg") + " " + overlappingChecker.toString(),
|
||||
InspectionsBundle.message("configure.checker.option.overlap.error.title"),
|
||||
Messages.getErrorIcon());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void propertyChange(PropertyChangeEvent evt) {
|
||||
// if (evt.getPropertyName().equals(ClassField.PROPERTY_PSICLASS)) {
|
||||
checkOkActionEnable();
|
||||
// }
|
||||
}
|
||||
|
||||
@Override
|
||||
public void itemStateChanged(ItemEvent e) {
|
||||
checkOkActionEnable();
|
||||
}
|
||||
|
||||
private void checkOkActionEnable() {
|
||||
if (classField.getPsiClass() == null || methodDropDown.getSelectedPsiMethod() == null || parameterDropDown.getSelectedPsiParameter() == null) {
|
||||
setOKActionEnabled(false);
|
||||
} else {
|
||||
setOKActionEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doOKAction() {
|
||||
if (!overlaps()) {
|
||||
MethodConditionCheck checker = buildMethodConditionChecker();
|
||||
if (checker != null) {
|
||||
if (checker.equals(myPreviouslySelectedChecker)) {
|
||||
mySelectedChecker = myPreviouslySelectedChecker;
|
||||
} else {
|
||||
mySelectedChecker = checker;
|
||||
}
|
||||
}
|
||||
super.doOKAction();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isOKActionEnabled() {
|
||||
if (!myOKAction.isEnabled()) return false;
|
||||
PsiClass psiClass = classField.getPsiClass();
|
||||
PsiMethod psiMethod = methodDropDown.getSelectedPsiMethod();
|
||||
PsiParameter psiParameter = parameterDropDown.getSelectedPsiParameter();
|
||||
if (psiClass == null || psiMethod == null || psiParameter == null)
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Input Text Field for Class Name
|
||||
*/
|
||||
static class ClassField extends EditorTextFieldWithBrowseButton implements ActionListener, DocumentListener {
|
||||
private final @NotNull Project myProject;
|
||||
private @Nullable PsiClass myPsiClass;
|
||||
public static final String PROPERTY_PSICLASS = "ClassField.myPsiClass";
|
||||
|
||||
public ClassField(@NotNull Project project, @Nullable PsiClass psiClass) {
|
||||
super(project, true, buildVisibilityChecker());
|
||||
myProject = project;
|
||||
myPsiClass = psiClass;
|
||||
setPreferredSize(new Dimension(500, (int) getPreferredSize().getHeight()));
|
||||
if (myPsiClass != null)
|
||||
setText(myPsiClass.getQualifiedName());
|
||||
addActionListener(this);
|
||||
getChildComponent().addDocumentListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
final TreeClassChooser chooser = TreeClassChooserFactory.getInstance(myProject)
|
||||
.createNoInnerClassesScopeChooser("Choose Class", new EverythingGlobalScope(myProject), new ClassFilter() {
|
||||
@Override
|
||||
public boolean isAccepted(PsiClass aClass) {
|
||||
return !aClass.isAnnotationType();
|
||||
}
|
||||
}, null);
|
||||
chooser.showDialog();
|
||||
PsiClass psiClass = chooser.getSelected();
|
||||
if (psiClass != null)
|
||||
setText(chooser.getSelected().getQualifiedName());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public PsiClass getPsiClass() {
|
||||
return myPsiClass;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeDocumentChange(DocumentEvent event) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void documentChanged(DocumentEvent event) {
|
||||
String className = event.getDocument().getText();
|
||||
PsiClass psiClass = null;
|
||||
if (className != null) {
|
||||
psiClass = JavaPsiFacade.getInstance(myProject).findClass(className, new EverythingGlobalScope(myProject));
|
||||
}
|
||||
|
||||
if (psiClass != null && myPsiClass != null) {
|
||||
if (!psiClass.equals(myPsiClass)) {
|
||||
firePropertyChange(PROPERTY_PSICLASS, myPsiClass, psiClass);
|
||||
myPsiClass = psiClass;
|
||||
}
|
||||
} else if (psiClass != null) {
|
||||
firePropertyChange(PROPERTY_PSICLASS, myPsiClass, psiClass);
|
||||
myPsiClass = psiClass;
|
||||
} else if (myPsiClass != null) {
|
||||
firePropertyChange(PROPERTY_PSICLASS, myPsiClass, psiClass);
|
||||
myPsiClass = null;
|
||||
}
|
||||
}
|
||||
|
||||
private static JavaCodeFragment.VisibilityChecker buildVisibilityChecker() {
|
||||
return new JavaCodeFragment.VisibilityChecker() {
|
||||
@Override
|
||||
public Visibility isDeclarationVisible(PsiElement declaration, PsiElement place) {
|
||||
return Visibility.VISIBLE;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop Down for picking Method Name
|
||||
*/
|
||||
static class MethodDropDown extends JComboBox implements PropertyChangeListener {
|
||||
private @Nullable PsiClass myPsiClass;
|
||||
private final @NotNull ConditionChecker.Type myType;
|
||||
private final @NotNull SortedComboBoxModel<MethodWrapper> myModel;
|
||||
|
||||
MethodDropDown(@Nullable PsiClass psiClass, @Nullable PsiMethod psiMethod, @NotNull ConditionChecker.Type type, SortedComboBoxModel<MethodWrapper> model) {
|
||||
super(model);
|
||||
|
||||
if (!isSupported(type))
|
||||
throw new IllegalArgumentException("Type is invalid " + type);
|
||||
|
||||
myPsiClass = psiClass;
|
||||
myType = type;
|
||||
myModel = model;
|
||||
setEnabled(myPsiClass != null);
|
||||
initValues();
|
||||
if (psiMethod != null) {
|
||||
for (Iterator<MethodWrapper> iterator = myModel.iterator(); iterator.hasNext(); ) {
|
||||
MethodWrapper methodWrapper = iterator.next();
|
||||
if (methodWrapper.getPsiMethod().equals(psiMethod)) {
|
||||
setSelectedItem(methodWrapper);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void initValues() {
|
||||
if (myPsiClass != null) {
|
||||
myModel.clear();
|
||||
myModel.setSelectedItem(null);
|
||||
PsiMethod[] allMethods = myPsiClass.getAllMethods();
|
||||
for (PsiMethod allMethod : allMethods) {
|
||||
if (qualifies(allMethod))
|
||||
myModel.add(new MethodWrapper(allMethod));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean qualifies(PsiMethod psiMethod) {
|
||||
if (isMethodFromJavaLangObject(psiMethod)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final PsiParameter[] parameters = psiMethod.getParameterList().getParameters();
|
||||
if (parameters.length < 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (myType == IS_NULL_METHOD || myType == IS_NOT_NULL_METHOD) {
|
||||
PsiType returnType = psiMethod.getReturnType();
|
||||
if (returnType != PsiType.BOOLEAN && (returnType == null || !returnType.getCanonicalText().equals(Boolean.class.toString()))) {
|
||||
return false;
|
||||
}
|
||||
} else if (myType == ASSERT_TRUE_METHOD || myType == ASSERT_FALSE_METHOD) {
|
||||
boolean booleanParamExists = false;
|
||||
for (int i = 0; i < psiMethod.getParameterList().getParameters().length; i++) {
|
||||
PsiParameter psiParameter = psiMethod.getParameterList().getParameters()[i];
|
||||
PsiType type = psiParameter.getType();
|
||||
if (type.equals(PsiType.BOOLEAN) || type.getCanonicalText().equals(Boolean.class.toString())) {
|
||||
booleanParamExists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!booleanParamExists) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Else it's ASSERT_IS_NULL_METHOD or ASSERT_IS_NOT_NULL_METHOD.
|
||||
// In that case there is no additional validation
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean isMethodFromJavaLangObject(PsiMethod method) {
|
||||
if (method != null && method.getContainingClass() != null && method.getContainingClass().getName() != null &&
|
||||
(method.getContainingClass().getName().equals("Object") || method.getContainingClass().getName().equals(Object.class.toString()))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void propertyChange(PropertyChangeEvent evt) {
|
||||
if (evt.getPropertyName().equals(ClassField.PROPERTY_PSICLASS)) {
|
||||
if (evt.getNewValue() == null) {
|
||||
clear();
|
||||
} else {
|
||||
setEnabled(true);
|
||||
if (myPsiClass == null || !myPsiClass.equals(evt.getNewValue())) { // ClassChanged so refresh list
|
||||
myPsiClass = (PsiClass) evt.getNewValue();
|
||||
initValues();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
myModel.clear();
|
||||
myModel.setSelectedItem(null);
|
||||
setEnabled(false);
|
||||
myPsiClass = null;
|
||||
}
|
||||
|
||||
public static SortedComboBoxModel<MethodWrapper> buildModel() {
|
||||
return new SortedComboBoxModel<MethodWrapper>(new Comparator<MethodWrapper>() {
|
||||
@Override
|
||||
public int compare(MethodWrapper o1, MethodWrapper o2) {
|
||||
return o1.compareTo(o2);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public PsiMethod getSelectedPsiMethod() {
|
||||
MethodWrapper methodWrapper = myModel.getSelectedItem();
|
||||
if (methodWrapper == null)
|
||||
return null;
|
||||
|
||||
return methodWrapper.getPsiMethod();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop Down for picking Parameter Name
|
||||
*/
|
||||
static class ParameterDropDown extends JComboBox implements PropertyChangeListener, ItemListener {
|
||||
private @Nullable PsiMethod myPsiMethod;
|
||||
private final @NotNull SortedComboBoxModel<ParameterWrapper> myModel;
|
||||
private final @NotNull ConditionChecker.Type myType;
|
||||
public static final String PROPERTY_PARAMETERDROPDOWN = "ParameterDropDown.myModel";
|
||||
|
||||
public ParameterDropDown(@Nullable PsiMethod psiMethod, @Nullable PsiParameter psiParameter, @NotNull SortedComboBoxModel<ParameterWrapper> model, @NotNull ConditionChecker.Type type) {
|
||||
super(model);
|
||||
|
||||
if (!isSupported(type))
|
||||
throw new IllegalArgumentException("Type is invalid " + type);
|
||||
|
||||
myPsiMethod = psiMethod;
|
||||
myModel = model;
|
||||
myType = type;
|
||||
|
||||
if (myPsiMethod != null) {
|
||||
setEnabled(true);
|
||||
myModel.addAll(getParameterWrappers());
|
||||
if (psiParameter != null) {
|
||||
for (Iterator iterator = myModel.iterator(); iterator.hasNext(); ) {
|
||||
ParameterWrapper wrapper = (ParameterWrapper) iterator.next();
|
||||
if (wrapper.getPsiParameter().equals(psiParameter))
|
||||
setSelectedItem(wrapper);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
List<ParameterWrapper> getParameterWrappers() {
|
||||
List<ParameterWrapper> wrappers = new ArrayList<ParameterWrapper>();
|
||||
if (myPsiMethod != null) {
|
||||
PsiParameterList parameterList = myPsiMethod.getParameterList();
|
||||
for (int i = 0; i < parameterList.getParameters().length; i++) {
|
||||
PsiParameter psiParameter = parameterList.getParameters()[i];
|
||||
if (myType == ASSERT_TRUE_METHOD || myType == ASSERT_FALSE_METHOD) {
|
||||
PsiType type = psiParameter.getType();
|
||||
if (type.equals(PsiType.BOOLEAN) || type.getCanonicalText().equals(Boolean.class.toString()))
|
||||
wrappers.add(new ParameterWrapper(psiParameter, i));
|
||||
} else {
|
||||
wrappers.add(new ParameterWrapper(psiParameter, i));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return wrappers;
|
||||
}
|
||||
|
||||
public static SortedComboBoxModel<ParameterWrapper> buildModel() {
|
||||
return new SortedComboBoxModel<ParameterWrapper>(new Comparator<ParameterWrapper>() {
|
||||
@Override
|
||||
public int compare(ParameterWrapper o1, ParameterWrapper o2) {
|
||||
return o1.compareTo(o2);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void itemStateChanged(ItemEvent e) {
|
||||
if (e.getSource() instanceof MethodDropDown) { // The MethodDropDown has changed.
|
||||
MethodDropDown methodDropDown = (MethodDropDown) e.getSource();
|
||||
if (methodDropDown.getSelectedPsiMethod() != null) {
|
||||
setEnabled(true);
|
||||
if (myPsiMethod == null || !myPsiMethod.equals(methodDropDown.getSelectedPsiMethod())) {
|
||||
myPsiMethod = methodDropDown.getSelectedPsiMethod();
|
||||
myModel.clear();
|
||||
myModel.addAll(getParameterWrappers());
|
||||
myModel.setSelectedItem(null);
|
||||
}
|
||||
} else {
|
||||
myPsiMethod = null;
|
||||
myModel.clear();
|
||||
myModel.setSelectedItem(null);
|
||||
setEnabled(false);
|
||||
}
|
||||
} else {
|
||||
throw new RuntimeException("Unexpected Configuration ParameterDropDown is only expected to receive events from MethodDropDown.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void propertyChange(PropertyChangeEvent evt) {
|
||||
if (evt.getPropertyName().equals(ClassField.PROPERTY_PSICLASS)) {
|
||||
if (evt.getNewValue() == null) {
|
||||
setEnabled(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public PsiParameter getSelectedPsiParameter() {
|
||||
ParameterWrapper parameterWrapper = myModel.getSelectedItem();
|
||||
if (parameterWrapper == null)
|
||||
return null;
|
||||
|
||||
return parameterWrapper.getPsiParameter();
|
||||
}
|
||||
|
||||
class ParameterWrapper implements Comparable<ParameterWrapper> {
|
||||
private final @NotNull String id;
|
||||
private final @NotNull PsiParameter psiParameter;
|
||||
private final int index;
|
||||
|
||||
ParameterWrapper(@NotNull PsiParameter psiParameter, int index) {
|
||||
this.psiParameter = psiParameter;
|
||||
this.index = index;
|
||||
String typeName;
|
||||
PsiTypeElement typeElement = psiParameter.getTypeElement();
|
||||
if (typeElement == null) {
|
||||
typeName = "";
|
||||
} else {
|
||||
if (typeElement.getType() instanceof PsiPrimitiveType) {
|
||||
typeName = ((PsiPrimitiveType) typeElement.getType()).getBoxedTypeName();
|
||||
} else {
|
||||
typeName = typeElement.getType().getCanonicalText();
|
||||
}
|
||||
}
|
||||
|
||||
id = typeName + " " + psiParameter.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(ParameterWrapper o) {
|
||||
return index - o.index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public PsiParameter getPsiParameter() {
|
||||
return psiParameter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class MethodWrapper implements Comparable<MethodWrapper> {
|
||||
private final @NotNull PsiMethod myPsiMethod;
|
||||
private final @NotNull String myId;
|
||||
|
||||
MethodWrapper(@NotNull PsiMethod psiMethod) {
|
||||
this.myPsiMethod = psiMethod;
|
||||
|
||||
List<String> parameters = new ArrayList<String>();
|
||||
for (int i = 0; i < psiMethod.getParameterList().getParameters().length; i++) {
|
||||
PsiParameter psiParameter = psiMethod.getParameterList().getParameters()[i];
|
||||
parameters.add(getParameterQualifiedName(psiParameter));
|
||||
}
|
||||
|
||||
myId = initId(psiMethod.getName(), parameters);
|
||||
}
|
||||
|
||||
private String getParameterQualifiedName(PsiParameter psiParameter) {
|
||||
PsiTypeElement typeElement = psiParameter.getTypeElement();
|
||||
if (typeElement == null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
if (typeElement.getType() instanceof PsiPrimitiveType) {
|
||||
return ((PsiPrimitiveType) typeElement.getType()).getBoxedTypeName();
|
||||
}
|
||||
|
||||
return typeElement.getType().getCanonicalText();
|
||||
}
|
||||
|
||||
private String initId(String methodName, List<String> parameterNames) {
|
||||
String shortName = methodName + "(";
|
||||
for (String parameterName : parameterNames) {
|
||||
if (parameterNames.lastIndexOf("") > -1)
|
||||
shortName += parameterName.substring(parameterName.lastIndexOf("") + 1) + ", ";
|
||||
else
|
||||
shortName += parameterName + ", ";
|
||||
}
|
||||
|
||||
if (parameterNames.size() > 0)
|
||||
shortName = shortName.substring(0, shortName.lastIndexOf(", "));
|
||||
|
||||
shortName += ")";
|
||||
return shortName;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public PsiMethod getPsiMethod() {
|
||||
return myPsiMethod;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getId() {
|
||||
return myId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return myId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(MethodWrapper o) {
|
||||
return myId.compareTo(o.myId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
public class AssertFalse {
|
||||
void bar() {
|
||||
final boolean b = call();
|
||||
if (Assertions.assertFalse(b)) {
|
||||
if(<warning descr="Condition 'b' is always 'false'">b</warning>) {}
|
||||
}
|
||||
}
|
||||
boolean call() {return true;}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import java.lang.*;
|
||||
|
||||
public class AssertIsNotNull {
|
||||
void bar() {
|
||||
final Object o = call();
|
||||
Assertions.assertIsNotNull(o);
|
||||
if(<warning descr="Condition 'o == null' is always 'false'">o == null</warning>) {}
|
||||
}
|
||||
Object call() {return new Object();}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import java.lang.*;
|
||||
|
||||
public class AssertIsNull {
|
||||
void bar() {
|
||||
final Object o = call();
|
||||
Assertions.assertIsNull(o);
|
||||
if(<warning descr="Condition 'o == null' is always 'true'">o == null</warning>) {}
|
||||
}
|
||||
Object call() {return new Object();}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
public class AssertTrue {
|
||||
void bar() {
|
||||
final boolean b = call();
|
||||
if (Assertions.assertTrue(b)) {
|
||||
if(<warning descr="Condition 'b' is always 'true'">b</warning>) {}
|
||||
}
|
||||
}
|
||||
boolean call() {return true;}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
public class IsNotNullCheck {
|
||||
void bar() {
|
||||
final Value v = call();
|
||||
if (Value.isNotNull(v)) {
|
||||
if(<warning descr="Condition 'v == null' is always 'false'">v == null</warning>) {}
|
||||
}
|
||||
}
|
||||
Value call() {return new Value();}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
public class IsNullCheck {
|
||||
void bar() {
|
||||
final Value v = call();
|
||||
if (Value.isNull(v)) {
|
||||
if(<warning descr="Condition 'v == null' is always 'true'">v == null</warning>) {}
|
||||
}
|
||||
}
|
||||
Value call() {return new Value();}
|
||||
}
|
||||
@@ -16,12 +16,19 @@
|
||||
package com.intellij.codeInspection;
|
||||
|
||||
import com.intellij.JavaTestUtil;
|
||||
import com.intellij.codeInsight.ConditionCheckManager;
|
||||
import com.intellij.codeInsight.ConditionChecker;
|
||||
import com.intellij.codeInsight.MethodConditionCheck;
|
||||
import com.intellij.codeInsight.NullableNotNullManager;
|
||||
import com.intellij.codeInspection.dataFlow.DataFlowInspection;
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.util.Disposer;
|
||||
import com.intellij.psi.PsiClass;
|
||||
import com.intellij.psi.PsiMethod;
|
||||
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* @author peter
|
||||
*/
|
||||
@@ -158,4 +165,60 @@ public class DataFlowInspectionTest extends LightCodeInsightFixtureTestCase {
|
||||
|
||||
public void testEqualsHasNoSideEffects() { doTest(); }
|
||||
|
||||
public void testIsNullCheck() throws Exception {
|
||||
ConditionCheckManager.getInstance(myModule.getProject()).getIsNullCheckMethods().add(
|
||||
buildMethodConditionCheck("Value", "isNull", ConditionChecker.Type.IS_NULL_METHOD,
|
||||
"public class Value { public static boolean isNull(Value o) {if (o == null) return true; else return false;} }"));
|
||||
doTest();
|
||||
}
|
||||
|
||||
public void testIsNotNullCheck() throws Exception {
|
||||
ConditionCheckManager.getInstance(myModule.getProject()).getIsNotNullCheckMethods().add(
|
||||
buildMethodConditionCheck("Value", "isNotNull", ConditionChecker.Type.IS_NOT_NULL_METHOD,
|
||||
"public class Value { public static boolean isNotNull(Value o) {if (o == null) return false; else return true;} }"));
|
||||
doTest();
|
||||
}
|
||||
|
||||
public void testAssertTrue() throws Exception {
|
||||
ConditionCheckManager.getInstance(myModule.getProject()).getAssertTrueMethods().add(
|
||||
buildMethodConditionCheck("Assertions", "assertTrue", ConditionChecker.Type.ASSERT_TRUE_METHOD,
|
||||
"public class Assertions { public static boolean assertTrue(boolean b) {if(!b) throw new Exception();} }"));
|
||||
doTest();
|
||||
}
|
||||
|
||||
public void testAssertFalse() throws Exception {
|
||||
ConditionCheckManager.getInstance(myModule.getProject()).getAssertFalseMethods().add(
|
||||
buildMethodConditionCheck("Assertions", "assertFalse", ConditionChecker.Type.ASSERT_FALSE_METHOD,
|
||||
"public class Assertions { public static boolean assertFalse(boolean b) {if(b) throw new Exception();} }"));
|
||||
doTest();
|
||||
}
|
||||
|
||||
public void testAssertIsNull() throws Exception {
|
||||
ConditionCheckManager.getInstance(myModule.getProject()).getAssertIsNullMethods().add(
|
||||
buildMethodConditionCheck("Assertions", "assertIsNull", ConditionChecker.Type.ASSERT_IS_NULL_METHOD,
|
||||
"public class Assertions { public static boolean assertIsNull(Object o) {if(o != null) throw new Exception();} }"));
|
||||
doTest();
|
||||
}
|
||||
|
||||
public void testAssertIsNotNull() throws Exception {
|
||||
ConditionCheckManager.getInstance(myModule.getProject()).getAssertIsNotNullMethods().add(
|
||||
buildMethodConditionCheck("Assertions", "assertIsNotNull", ConditionChecker.Type.ASSERT_IS_NOT_NULL_METHOD,
|
||||
"public class Assertions { public static boolean assertIsNotNull(Object o) {if(o == null) throw new Exception();} }"));
|
||||
doTest();
|
||||
}
|
||||
|
||||
private MethodConditionCheck buildMethodConditionCheck(String className, String methodName, ConditionChecker.Type type, String classText)
|
||||
throws IOException {
|
||||
myFixture.addClass(classText);
|
||||
PsiClass psiClass = myFixture.findClass(className);
|
||||
PsiMethod psiMethod = null;
|
||||
PsiMethod[] methods = psiClass.getMethods();
|
||||
for (PsiMethod tempPsiMethod : methods) {
|
||||
if (tempPsiMethod.getName().equals(methodName)) {
|
||||
psiMethod = tempPsiMethod;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return new MethodConditionCheck(psiMethod, psiMethod.getParameterList().getParameters()[0], type);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,6 +51,22 @@ inspection.data.flow.redundant.instanceof.quickfix=Replace with != null
|
||||
inspection.data.flow.simplify.boolean.expression.quickfix=Simplify Boolean Expression
|
||||
inspection.data.flow.simplify.to.assignment.quickfix.name=Simplify to normal assignment
|
||||
configure.annotations.option=Configure annotations
|
||||
configure.checker.option.button=Configure IsNull/IsNotNull/True/False Check/Assert Methods
|
||||
configure.checker.option.main.dialog.title=IsNull/IsNotNull Configuration
|
||||
configure.checker.option.overlap.error.title=Overlapping Check
|
||||
configure.checker.option.overlap.error.msg=Configuration conflicts with
|
||||
configure.checker.option.isNull.add.method.checker.dialog.title=Add IsNull Check Method
|
||||
configure.checker.option.isNotNull.add.method.checker.dialog.title=Add IsNotNull Check Method
|
||||
configure.checker.option.assert.isNull.add.method.checker.dialog.title=Add Assert IsNull Method
|
||||
configure.checker.option.assert.isNotNull.add.method.checker.dialog.title=Add Assert IsNotNull Method
|
||||
configure.checker.option.assert.true.add.method.checker.dialog.title=Add Assert True Method
|
||||
configure.checker.option.assert.false.add.method.checker.dialog.title=Add Assert False Method
|
||||
configure.checker.option.assert.isNull.method.panel.title=Assert IsNull Methods
|
||||
configure.checker.option.assert.isNotNull.method.panel.title=Assert IsNotNull Methods
|
||||
configure.checker.option.isNull.method.panel.title=IsNull Check Methods
|
||||
configure.checker.option.isNotNull.method.panel.title=IsNotNull Check Methods
|
||||
configure.checker.option.assert.true.method.panel.title=Assert True Methods
|
||||
configure.checker.option.assert.false.method.panel.title=Assert False Methods
|
||||
|
||||
#messages from dataflow inspection
|
||||
dataflow.message.npe.method.invocation=Method invocation <code>#ref</code> #loc may produce <code>java.lang.NullPointerException</code>
|
||||
|
||||
@@ -433,6 +433,9 @@
|
||||
<projectService serviceInterface="com.intellij.codeInsight.NullableNotNullManager"
|
||||
serviceImplementation="com.intellij.codeInsight.NullableNotNullManagerImpl"/>
|
||||
|
||||
<projectService serviceInterface="com.intellij.codeInsight.ConditionCheckManager"
|
||||
serviceImplementation="com.intellij.codeInsight.ConditionCheckManager"/>
|
||||
|
||||
<projectService serviceInterface="com.intellij.psi.search.PsiShortNamesCache"
|
||||
serviceImplementation="com.intellij.psi.impl.CompositeShortNamesCache"/>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user