Merge branch 'customDfaAssert'

This commit is contained in:
peter
2013-02-27 16:56:29 +01:00
15 changed files with 1900 additions and 1 deletions
@@ -0,0 +1,239 @@
/*
* Copyright 2000-2013 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 = "ConditionCheckManager",
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<ConditionChecker> myIsNullCheckMethods = new ArrayList<ConditionChecker>();
private List<ConditionChecker> myIsNotNullCheckMethods = new ArrayList<ConditionChecker>();
private List<ConditionChecker> myAssertIsNullMethods = new ArrayList<ConditionChecker>();
private List<ConditionChecker> myAssertIsNotNullMethods = new ArrayList<ConditionChecker>();
private List<ConditionChecker> myAssertTrueMethods = new ArrayList<ConditionChecker>();
private List<ConditionChecker> myAssertFalseMethods = new ArrayList<ConditionChecker>();
public static ConditionCheckManager getInstance(Project project) {
return ServiceManager.getService(project, ConditionCheckManager.class);
}
public void setIsNullCheckMethods(List<ConditionChecker> methodConditionChecks) {
myIsNullCheckMethods.clear();
myIsNullCheckMethods.addAll(methodConditionChecks);
}
public void setIsNotNullCheckMethods(List<ConditionChecker> methodConditionChecks) {
myIsNotNullCheckMethods.clear();
myIsNotNullCheckMethods.addAll(methodConditionChecks);
}
public void setAssertIsNullMethods(List<ConditionChecker> methodConditionChecks) {
myAssertIsNullMethods.clear();
myAssertIsNullMethods.addAll(methodConditionChecks);
}
public void setAssertIsNotNullMethods(List<ConditionChecker> methodConditionChecks) {
myAssertIsNotNullMethods.clear();
myAssertIsNotNullMethods.addAll(methodConditionChecks);
}
public void setAssertTrueMethods(List<ConditionChecker> psiMethodWrappers) {
myAssertTrueMethods.clear();
myAssertTrueMethods.addAll(psiMethodWrappers);
}
public void setAssertFalseMethods(List<ConditionChecker> psiMethodWrappers) {
myAssertFalseMethods.clear();
myAssertFalseMethods.addAll(psiMethodWrappers);
}
public List<ConditionChecker> getIsNullCheckMethods() {
return myIsNullCheckMethods;
}
public List<ConditionChecker> getIsNotNullCheckMethods() {
return myIsNotNullCheckMethods;
}
public List<ConditionChecker> getAssertIsNullMethods() {
return myAssertIsNullMethods;
}
public List<ConditionChecker> getAssertIsNotNullMethods() {
return myAssertIsNotNullMethods;
}
public List<ConditionChecker> getAssertFalseMethods() {
return myAssertFalseMethods;
}
public List<ConditionChecker> 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<ConditionChecker> listToLoadFrom) {
for (ConditionChecker 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<ConditionChecker> listToLoadTo, List<String> listToLoadFrom, ConditionChecker.Type type){
listToLoadTo.clear();
for (String setting : listToLoadFrom) {
try {
listToLoadTo.add(new ConditionChecker.FromConfigBuilder(setting, type).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<ConditionChecker> checkers) {
for (ConditionChecker checker : checkers) {
if (checker.matchesPsiMethod(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<ConditionChecker> checkers) {
for (ConditionChecker checker : checkers) {
if (checker.matchesPsiMethod(psiMethod))
return true;
}
return false;
}
public static boolean methodMatches(PsiMethod psiMethod, int paramIndex, List<ConditionChecker> checkers) {
for (ConditionChecker checker : checkers) {
if (checker.matchesPsiMethod(psiMethod, paramIndex))
return true;
}
return false;
}
}
@@ -0,0 +1,431 @@
/*
* Copyright 2000-2013 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.diagnostic.*;
import com.intellij.psi.*;
import org.jetbrains.annotations.*;
import java.io.*;
import java.util.*;
/**
* 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 ConditionCheck would be <p/>
* myConditionCheckType=Type.IS_NOT_NULL_METHOD
* myClassName=Foo
* myMethodName=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 ConditionChecker implements Serializable {
private final @NotNull Type myConditionCheckType;
public 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;
}
}
private final @NotNull String myClassName;
private final @NotNull String myMethodName;
private final @NotNull List<String> myParameterClassList;
private final int myCheckedParameterIndex;
private final String myFullName;
private ConditionChecker(@NotNull String className,
@NotNull String methodName,
@NotNull List<String> parameterClassList,
int checkedParameterIndex,
@NotNull Type type,
@NotNull String fullName) {
checkState(!className.isEmpty(), "Class Name is blank");
checkState(!methodName.isEmpty(), "Method Name is blank");
checkState(!parameterClassList.isEmpty(), "Parameter Class List is empty");
checkState(checkedParameterIndex >= 0, "CheckedParameterIndex must be greater than or equal to zero");
checkState(parameterClassList.size() >= checkedParameterIndex, "CheckedParameterIndex is greater than Parameter Class List's size");
checkState(!fullName.isEmpty(), "Method Name is blank");
myConditionCheckType = type;
myClassName = className;
myMethodName = methodName;
myParameterClassList = parameterClassList;
myCheckedParameterIndex = checkedParameterIndex;
myFullName = fullName;
}
private static void checkState(boolean condition, String errorMsg) {
if (!condition) throw new IllegalArgumentException(errorMsg);
}
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();
}
public boolean matchesPsiMethod(PsiMethod psiMethod) {
if (!myMethodName.equals(psiMethod.getName())) return false;
PsiClass containingClass = psiMethod.getContainingClass();
if (containingClass == null) return false;
String qualifiedName = containingClass.getQualifiedName();
if (qualifiedName == null) return false;
if (!myClassName.equals(qualifiedName)) return false;
PsiParameterList psiParameterList = psiMethod.getParameterList();
if (myParameterClassList.size() != psiParameterList.getParameters().length) return false;
for (int i = 0; i < psiParameterList.getParameters().length; i++) {
PsiParameter psiParameter = psiParameterList.getParameters()[i];
PsiTypeElement psiTypeElement = psiParameter.getTypeElement();
if (psiTypeElement == null) return false;
PsiType psiType = psiTypeElement.getType();
String parameterCanonicalText = psiType.getCanonicalText();
String myParameterCanonicalText = myParameterClassList.get(i);
if (!myParameterCanonicalText.equals(parameterCanonicalText)) return false;
}
return true;
}
public boolean matchesPsiMethod(PsiMethod psiMethod, int paramIndex) {
if (matchesPsiMethod(psiMethod) && paramIndex == myCheckedParameterIndex) return true;
return false;
}
public boolean overlaps(ConditionChecker otherChecker) {
if (myClassName.equals(otherChecker.myClassName) &&
myMethodName.equals(otherChecker.myMethodName) &&
myParameterClassList.equals(otherChecker.myParameterClassList) &&
myCheckedParameterIndex == otherChecker.myCheckedParameterIndex) {
return true;
}
return false;
}
@NotNull
public Type getConditionCheckType() {
return myConditionCheckType;
}
@NotNull
public String getClassName() {
return myClassName;
}
@NotNull
public String getMethodName() {
return myMethodName;
}
public int getCheckedParameterIndex() {
return myCheckedParameterIndex;
}
public String getFullName() {
return myFullName;
}
/**
* In addition to normal duties, this controls the manner in which the ConditionCheck appears in the ConditionCheckDialog.MethodsPanel
*/
@Override
public String toString() {
return myFullName;
}
private static class Builder {
static String initFullName(String className,
String methodName,
List<String> parameterClasses,
List<String> parameterNames,
int checkedParameterIndex) {
String s = className + "." + methodName + "(";
int index = 0;
for (String parameterClass : parameterClasses) {
String parameterClassAndName = parameterClass + " " + parameterNames.get(index);
if (index == checkedParameterIndex) parameterClassAndName = "*" + parameterClassAndName + "*";
s += parameterClassAndName + ", ";
index++;
}
s = s.substring(0, s.length() - 2);
s += ")";
return s;
}
}
static class FromConfigBuilder extends Builder {
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.ConditionCheck.FromConfigBuilder");
private final @NotNull String serializedRepresentation;
private final @NotNull Type type;
FromConfigBuilder(@NotNull String serializedRepresentation, @NotNull Type type) {
this.serializedRepresentation = serializedRepresentation;
this.type = type;
}
private String parseClassAndMethodName() {
if (!serializedRepresentation.contains("(")) {
throw new IllegalArgumentException("Name should contain a opening parenthesis. " + serializedRepresentation);
}
else if (!serializedRepresentation.contains(")")) {
throw new IllegalArgumentException("Name should contain a closing parenthesis. " + serializedRepresentation);
}
else if (serializedRepresentation.indexOf("(", serializedRepresentation.indexOf("(") + 1) > -1) {
throw new IllegalArgumentException("Name should only contain one opening parenthesis. " + serializedRepresentation);
}
else if (serializedRepresentation.indexOf(")", serializedRepresentation.indexOf(")") + 1) > -1) {
throw new IllegalArgumentException("Name should only contain one closing parenthesis. " + serializedRepresentation);
}
else if (serializedRepresentation.indexOf(")") < serializedRepresentation.indexOf("(")) {
throw new IllegalArgumentException("Opening parenthesis should precede closing parenthesis. " + serializedRepresentation);
}
String classAndMethodName = serializedRepresentation.substring(0, serializedRepresentation.indexOf("("));
if (!classAndMethodName.contains(".")) {
throw new IllegalArgumentException(
"Name should contain a dot between the class name and method name (before the opening parenthesis). " +
serializedRepresentation);
}
return classAndMethodName;
}
@Nullable
public ConditionChecker build() {
try {
String classAndMethodName = parseClassAndMethodName();
String className = classAndMethodName.substring(0, classAndMethodName.lastIndexOf("."));
String methodName = classAndMethodName.substring(classAndMethodName.lastIndexOf(".") + 1);
String allParametersSubString =
serializedRepresentation.substring(serializedRepresentation.indexOf("(") + 1, serializedRepresentation.lastIndexOf(")")).trim();
if (allParametersSubString.isEmpty()) {
throw new IllegalArgumentException(
"Name should contain 1+ parameter (between opening and closing parenthesis). " + serializedRepresentation);
}
else if (allParametersSubString.contains("*") && allParametersSubString.indexOf("*") == allParametersSubString.lastIndexOf("*")) {
throw new IllegalArgumentException("Selected Parameter should be surrounded by asterisks. " + serializedRepresentation);
}
List<String> parameterClasses = new ArrayList<String>();
List<String> parameterNames = new ArrayList<String>();
int checkParameterIndex = -1;
int index = 0;
for (String parameterClassAndName : allParametersSubString.split(",")) {
parameterClassAndName = parameterClassAndName.trim();
if (parameterClassAndName.startsWith("*") && parameterClassAndName.endsWith("*")) {
checkParameterIndex = index;
parameterClassAndName = parameterClassAndName.substring(1, parameterClassAndName.length() - 1);
}
String[] parameterClassAndNameSplit = parameterClassAndName.split(" ");
String parameterClass = parameterClassAndNameSplit[0];
String parameterName = parameterClassAndNameSplit[1];
parameterClasses.add(parameterClass);
parameterNames.add(parameterName);
index++;
}
String fullName = initFullName(className, methodName, parameterClasses, parameterNames, checkParameterIndex);
return new ConditionChecker(className, methodName, parameterClasses, checkParameterIndex, type, fullName);
}
catch (Exception e) {
LOG.error("An Exception occurred while attempting to build ConditionCheck for Serialized String '" +
serializedRepresentation +
"' and Type '" +
type +
"'", e);
return null;
}
}
}
public static class FromPsiBuilder extends Builder {
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.ConditionCheck.FromPsiBuilder");
private final @NotNull PsiMethod psiMethod;
private final @NotNull PsiParameter psiParameter;
private final @NotNull Type type;
public FromPsiBuilder(@NotNull PsiMethod psiMethod, @NotNull PsiParameter psiParameter, @NotNull Type type) {
this.psiMethod = psiMethod;
this.psiParameter = psiParameter;
this.type = type;
}
private static void validatePsiMethodHasContainingClass(PsiMethod psiMethod) {
PsiElement psiElement = psiMethod.getContainingClass();
if (!(psiElement instanceof PsiClass)) {
throw new IllegalArgumentException("PsiMethod " + psiMethod + " can not have a null containing class.");
}
}
private static void validatePsiMethodReturnTypeForNonAsserts(PsiMethod psiMethod, Type type) {
PsiType returnType = psiMethod.getReturnType();
if (isAssert(type)) return;
if (returnType == null) throw new IllegalArgumentException("PsiMethod " + psiMethod + " has a null return type PsiType.");
if (returnType != PsiType.BOOLEAN && !returnType.getCanonicalText().equals(Boolean.class.toString())) {
throw new IllegalArgumentException("PsiMethod " + psiMethod + " must have a null return type PsiType of boolean or Boolean.");
}
}
private static void validatePsiParameterExistsInPsiMethod(PsiMethod psiMethod, PsiParameter psiParameter) {
boolean parameterFound = false;
PsiParameter[] parameters = psiMethod.getParameterList().getParameters();
for (PsiParameter parameter : parameters) {
if (psiParameter.equals(parameter)) {
parameterFound = true;
break;
}
}
if (!parameterFound) {
throw new IllegalArgumentException("PsiMethod " + psiMethod + " must have parameter " + getFullyQualifiedName(psiParameter));
}
}
private static boolean isAssert(Type type) {
return type == Type.ASSERT_IS_NULL_METHOD ||
type == Type.ASSERT_IS_NOT_NULL_METHOD ||
type == Type.ASSERT_TRUE_METHOD ||
type == Type.ASSERT_FALSE_METHOD;
}
private static String initClassNameFromPsiMethod(PsiMethod psiMethod) {
PsiElement psiElement = psiMethod.getContainingClass();
PsiClass psiClass = (PsiClass)psiElement;
if (psiClass == null) throw new IllegalStateException("PsiClass is null");
String qualifiedName = psiClass.getQualifiedName();
if (qualifiedName == null || qualifiedName.isEmpty()) throw new IllegalStateException("Qualified Name is Blank");
return qualifiedName;
}
private static String initMethodNameFromPsiMethod(PsiMethod psiMethod) {
return psiMethod.getName();
}
private static List<String> initParameterClassListFromPsiMethod(PsiMethod psiMethod) {
List<String> parameterClasses = new ArrayList<String>();
PsiParameter[] parameters = psiMethod.getParameterList().getParameters();
for (PsiParameter param : parameters) {
PsiTypeElement typeElement = param.getTypeElement();
if (typeElement == null) throw new RuntimeException("Parameter has null typeElement " + param.getName());
PsiType psiType = typeElement.getType();
parameterClasses.add(psiType.getCanonicalText());
}
return parameterClasses;
}
private static List<String> initParameterNameListFromPsiMethod(PsiMethod psiMethod) {
List<String> parameterNames = new ArrayList<String>();
PsiParameter[] parameters = psiMethod.getParameterList().getParameters();
for (PsiParameter param : parameters) {
parameterNames.add(param.getName());
}
return parameterNames;
}
private static int initCheckedParameterIndex(PsiMethod psiMethod, PsiParameter psiParameterToFind) {
PsiParameter[] parameters = psiMethod.getParameterList().getParameters();
for (int i = 0; i < parameters.length; i++) {
PsiParameter param = parameters[i];
if (param.equals(psiParameterToFind)) return i;
}
throw new IllegalStateException();
}
private void validateConstructorArgs(PsiMethod psiMethod, PsiParameter psiParameter) {
validatePsiMethodHasContainingClass(psiMethod);
validatePsiMethodReturnTypeForNonAsserts(psiMethod, type);
validatePsiParameterExistsInPsiMethod(psiMethod, psiParameter);
}
@Nullable
public ConditionChecker build() {
try {
validateConstructorArgs(psiMethod, psiParameter);
String className = initClassNameFromPsiMethod(psiMethod);
String methodName = initMethodNameFromPsiMethod(psiMethod);
List<String> parameterClassList = initParameterClassListFromPsiMethod(psiMethod);
List<String> parameterNameList = initParameterNameListFromPsiMethod(psiMethod);
int checkedParameterIndex = initCheckedParameterIndex(psiMethod, psiParameter);
String fullName = initFullName(className, methodName, parameterClassList, parameterNameList, checkedParameterIndex);
return new ConditionChecker(className, methodName, parameterClassList, checkedParameterIndex, type, fullName);
}
catch (Exception e) {
LOG.error("An Exception occurred while attempting to build ConditionCheck for PsiMethod '" + psiMethod +
"' PsiParameter='" + psiParameter + "' " +
"' and Type '" +
type +
"'", e);
return null;
}
}
}
}
@@ -0,0 +1,288 @@
/*
* Copyright 2000-2013 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<ConditionChecker> isNullCheckMethods = new ArrayList<ConditionChecker>(manager.getIsNullCheckMethods());
List<ConditionChecker> isNotNullCheckMethods = new ArrayList<ConditionChecker>(manager.getIsNotNullCheckMethods());
List<ConditionChecker> assertIsNullMethods = new ArrayList<ConditionChecker>(manager.getAssertIsNullMethods());
List<ConditionChecker> assertIsNotNullMethods = new ArrayList<ConditionChecker>(manager.getAssertIsNotNullMethods());
List<ConditionChecker> assertTrueMethods = new ArrayList<ConditionChecker>(manager.getAssertTrueMethods());
List<ConditionChecker> assertFalseMethods = new ArrayList<ConditionChecker>(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, 150));
bottomTwoThirdsSplitter.setPreferredSize(new Dimension(600, 300));
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.getConditionChecker());
manager.setIsNullCheckMethods(myIsNullCheckMethodPanel.getConditionChecker());
manager.setAssertIsNotNullMethods(myAssertIsNotNullMethodPanel.getConditionChecker());
manager.setAssertIsNullMethods(myAssertIsNullMethodPanel.getConditionChecker());
manager.setAssertTrueMethods(myAssertTrueMethodPanel.getConditionChecker());
manager.setAssertFalseMethods(myAssertFalseMethodPanel.getConditionChecker());
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<ConditionChecker> checkers, final ConditionChecker.Type type, final @NotNull Project myProject) {
this.myProject = myProject;
myList = new JBList(new CollectionListModel<ConditionChecker>(checkers));
myPanel = new JPanel(new BorderLayout());
myPanel.setBorder(IdeBorderFactory.createTitledBorder(initTitle(type), false, new Insets(10, 0, 0, 0)));
myPanel.setPreferredSize(new Dimension(400, 150));
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<ConditionChecker> 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 ConditionChecker checker, ConditionChecker.Type type, int index) {
MethodCheckerDetailsDialog pickMethodPanel =
new MethodCheckerDetailsDialog(checker, type, myProject, myPanel, getConditionCheckers(), getOtherCheckers());
pickMethodPanel.show();
ConditionChecker chk = pickMethodPanel.getConditionChecker();
if (chk != null) {
CollectionListModel<ConditionChecker> model = getCollectionListModel();
if (model.getSize() <= index) {
model.add(chk);
}
else {
model.setElementAt(chk, index);
}
}
}
private CollectionListModel<ConditionChecker> getCollectionListModel() {
//noinspection unchecked
return (CollectionListModel<ConditionChecker>)myList.getModel();
}
@NotNull
public JPanel getComponent() {
return myPanel;
}
public List<ConditionChecker> getConditionChecker() {
CollectionListModel<ConditionChecker> model = getCollectionListModel();
return new ArrayList<ConditionChecker>(model.getItems());
}
public Set<ConditionChecker> getConditionCheckers() {
Set<ConditionChecker> set = new HashSet<ConditionChecker>();
set.addAll(getConditionChecker());
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.*;
@@ -1343,6 +1344,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;
@@ -0,0 +1,708 @@
/*
* Copyright 2000-2013 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 ConditionChecker myPreviouslySelectedChecker;
/**
* Set by the OK and/or Cancel actions so that the caller can retrieve it via a call to getMethodIsNullIsNotNullChecker
*/
private @Nullable ConditionChecker mySelectedChecker;
MethodCheckerDetailsDialog(@Nullable ConditionChecker 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) {
psiClass =
JavaPsiFacade.getInstance(myProject).findClass(previouslySelectedChecker.getClassName(), GlobalSearchScope.allScope(myProject));
if (psiClass != null) {
for (PsiMethod method : psiClass.findMethodsByName(previouslySelectedChecker.getMethodName(), true)) {
if (previouslySelectedChecker.equals(buildParameterClassListFromPsiMethod(method))) {
psiMethod = method;
break;
}
}
}
if (psiMethod != null) {
PsiParameter[] parameters = psiMethod.getParameterList().getParameters();
if (parameters.length - 1 >= previouslySelectedChecker.getCheckedParameterIndex()) {
psiParameter = parameters[previouslySelectedChecker.getCheckedParameterIndex()];
}
}
}
if (psiClass == null || psiMethod == null || psiParameter == null) {
psiClass = null;
psiMethod = null;
psiParameter = null;
}
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 static List<String> buildParameterClassListFromPsiMethod(PsiMethod psiMethod) {
List<String> parameterClasses = new ArrayList<String>();
PsiParameter[] parameters = psiMethod.getParameterList().getParameters();
for (PsiParameter param : parameters) {
PsiTypeElement typeElement = param.getTypeElement();
if (typeElement == null) return new ArrayList<String>();
PsiType psiType = typeElement.getType();
parameterClasses.add(psiType.getCanonicalText());
}
return parameterClasses;
}
private static 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;
}
@Nullable
ConditionChecker getConditionChecker() {
return mySelectedChecker;
}
@Nullable
private ConditionChecker buildConditionChecker() {
PsiClass psiClass = classField.getPsiClass();
PsiMethod psiMethod = methodDropDown.getSelectedPsiMethod();
PsiParameter psiParameter = parameterDropDown.getSelectedPsiParameter();
if (psiClass != null && psiMethod != null && psiParameter != null) {
return new ConditionChecker.FromPsiBuilder(psiMethod, psiParameter, myType).build();
}
else {
return null;
}
}
private boolean overlaps(ConditionChecker thisChecker) {
for (ConditionChecker overlappingChecker : myOtherCheckers) {
if (thisChecker.overlaps(overlappingChecker)) {
Messages.showMessageDialog(myProject, InspectionsBundle.message("configure.checker.option.overlap.error.msg") +
" " +
overlappingChecker.getConditionCheckType() + " " + overlappingChecker.toString(),
InspectionsBundle.message("configure.checker.option.overlap.error.title"), Messages.getErrorIcon());
return true;
}
}
return false;
}
@Override
public void propertyChange(PropertyChangeEvent evt) {
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() {
ConditionChecker checker = buildConditionChecker();
if (checker != null && !overlaps(checker)) {
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 {
public static final String PROPERTY_PSICLASS = "ClassField.myPsiClass";
private final @NotNull Project myProject;
private @Nullable PsiClass 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) { //noinspection ConstantConditions
setText(myPsiClass.getQualifiedName());
}
addActionListener(this);
getChildComponent().addDocumentListener(this);
}
private static JavaCodeFragment.VisibilityChecker buildVisibilityChecker() {
return new JavaCodeFragment.VisibilityChecker() {
@Override
public Visibility isDeclarationVisible(PsiElement declaration, PsiElement place) {
return Visibility.VISIBLE;
}
};
}
@Override
public void actionPerformed(ActionEvent e) {
final TreeClassChooser chooser = TreeClassChooserFactory.getInstance(myProject)
.createNoInnerClassesScopeChooser("Choose Class", GlobalSearchScope.allScope(myProject), new ClassFilter() {
@Override
public boolean isAccepted(PsiClass aClass) {
return !aClass.isAnnotationType();
}
}, null);
chooser.showDialog();
PsiClass psiClass = chooser.getSelected();
if (psiClass != null) { //noinspection ConstantConditions
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, GlobalSearchScope.allScope(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;
}
}
}
/**
* Drop Down for picking Method Name
*/
static class MethodDropDown extends JComboBox implements PropertyChangeListener {
private final @NotNull ConditionChecker.Type myType;
private final @NotNull SortedComboBoxModel<MethodWrapper> myModel;
private @Nullable PsiClass myPsiClass;
MethodDropDown(@Nullable PsiClass psiClass,
@Nullable PsiMethod psiMethod,
@NotNull ConditionChecker.Type type,
@NotNull 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 static boolean isMethodFromJavaLangObject(PsiMethod method) {
if (method == null) return false;
PsiClass containingClass = method.getContainingClass();
if (containingClass == null) return false;
String name = containingClass.getQualifiedName();
if (name == null) return false;
if (CommonClassNames.JAVA_LANG_OBJECT.equals(name)) return true;
return false;
}
@NotNull
public static SortedComboBoxModel<MethodWrapper> buildModel() {
return new SortedComboBoxModel<MethodWrapper>(new Comparator<MethodWrapper>() {
@Override
public int compare(MethodWrapper o1, MethodWrapper o2) {
return o1.compareTo(o2);
}
});
}
private void initValues() {
if (myPsiClass != null) {
myModel.clear();
myModel.setSelectedItem(null);
PsiMethod[] allMethods = myPsiClass.getAllMethods();
for (PsiMethod method : allMethods) {
MethodWrapper methodWrapper = new MethodWrapper(method);
if (qualifies(method) && !myModel.getItems().contains(methodWrapper)) myModel.add(methodWrapper);
}
}
}
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 (PsiParameter psiParameter : parameters) {
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;
}
/**
* Called when ClassField is set and when user selects entry in the MethodDropDown
*/
@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;
}
@Nullable
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 final @NotNull SortedComboBoxModel<ParameterWrapper> myModel;
private final @NotNull ConditionChecker.Type myType;
private @Nullable PsiMethod myPsiMethod;
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);
}
}
public static SortedComboBoxModel<ParameterWrapper> buildModel() {
return new SortedComboBoxModel<ParameterWrapper>(new Comparator<ParameterWrapper>() {
@Override
public int compare(ParameterWrapper o1, ParameterWrapper o2) {
return o1.compareTo(o2);
}
});
}
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;
}
@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);
}
}
}
@Nullable
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> parameterClassNames = new ArrayList<String>();
PsiParameter[] parameters = psiMethod.getParameterList().getParameters();
for (PsiParameter psiParameter : parameters) {
parameterClassNames.add(getParameterQualifiedName(psiParameter));
}
myId = initId(psiMethod.getName(), parameterClassNames);
}
private static 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 static 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 boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MethodWrapper that = (MethodWrapper)o;
if (!myId.equals(that.myId)) return false;
return true;
}
@Override
public int hashCode() {
return myId.hashCode();
}
@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,11 +16,16 @@
package com.intellij.codeInspection;
import com.intellij.JavaTestUtil;
import com.intellij.codeInsight.NullableNotNullManager;
import com.intellij.codeInsight.*;
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 org.jetbrains.annotations.*;
import java.io.IOException;
/**
* @author peter
@@ -159,4 +164,62 @@ public class DataFlowInspectionTest extends LightCodeInsightFixtureTestCase {
public void testEqualsHasNoSideEffects() { doTest(); }
public void testIsNullCheck() throws Exception {
ConditionCheckManager.getInstance(myModule.getProject()).getIsNullCheckMethods().add(
buildConditionChecker("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(
buildConditionChecker("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(
buildConditionChecker("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(
buildConditionChecker("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(
buildConditionChecker("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(
buildConditionChecker("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();
}
@Nullable
private ConditionChecker buildConditionChecker(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;
}
}
assert psiMethod != null;
return new ConditionChecker.FromPsiBuilder(psiMethod, psiMethod.getParameterList().getParameters()[0], type).build();
}
}
@@ -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>
+3
View File
@@ -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"/>