From 0e12be8b663d58f7a9629ff0fced570e3524bfc7 Mon Sep 17 00:00:00 2001 From: peter Date: Thu, 21 Feb 2013 11:17:53 +0100 Subject: [PATCH 1/7] [Johnny Clasrk] IsNull IsNotNull True False Checks and Assertions (IDEA-35808) --- .../codeInsight/ConditionCheckManager.java | 252 +++++++ .../codeInsight/ConditionChecker.java | 55 ++ .../codeInsight/MethodConditionCheck.java | 396 +++++++++++ .../dataFlow/ConditionCheckDialog.java | 264 ++++++++ .../dataFlow/ControlFlowAnalyzer.java | 77 +++ .../dataFlow/DataFlowInspection.java | 18 + .../dataFlow/MethodCheckerDetailsDialog.java | 621 ++++++++++++++++++ .../dataFlow/fixture/AssertFalse.java | 9 + .../dataFlow/fixture/AssertIsNotNull.java | 10 + .../dataFlow/fixture/AssertIsNull.java | 10 + .../dataFlow/fixture/AssertTrue.java | 9 + .../dataFlow/fixture/IsNotNullCheck.java | 9 + .../dataFlow/fixture/IsNullCheck.java | 9 + .../DataFlowInspectionTest.java | 63 ++ .../src/messages/InspectionsBundle.properties | 16 + resources/src/META-INF/IdeaPlugin.xml | 3 + 16 files changed, 1821 insertions(+) create mode 100644 java/java-impl/src/com/intellij/codeInsight/ConditionCheckManager.java create mode 100644 java/java-impl/src/com/intellij/codeInsight/ConditionChecker.java create mode 100644 java/java-impl/src/com/intellij/codeInsight/MethodConditionCheck.java create mode 100644 java/java-impl/src/com/intellij/codeInspection/dataFlow/ConditionCheckDialog.java create mode 100644 java/java-impl/src/com/intellij/codeInspection/dataFlow/MethodCheckerDetailsDialog.java create mode 100644 java/java-tests/testData/inspection/dataFlow/fixture/AssertFalse.java create mode 100644 java/java-tests/testData/inspection/dataFlow/fixture/AssertIsNotNull.java create mode 100644 java/java-tests/testData/inspection/dataFlow/fixture/AssertIsNull.java create mode 100644 java/java-tests/testData/inspection/dataFlow/fixture/AssertTrue.java create mode 100644 java/java-tests/testData/inspection/dataFlow/fixture/IsNotNullCheck.java create mode 100644 java/java-tests/testData/inspection/dataFlow/fixture/IsNullCheck.java diff --git a/java/java-impl/src/com/intellij/codeInsight/ConditionCheckManager.java b/java/java-impl/src/com/intellij/codeInsight/ConditionCheckManager.java new file mode 100644 index 000000000000..539a3a1b231a --- /dev/null +++ b/java/java-impl/src/com/intellij/codeInsight/ConditionCheckManager.java @@ -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 Johnny Clark + * 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 { + @SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"}) private State state; + private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.ConditionCheckManager"); + + private List myIsNullCheckMethods = new ArrayList(); + private List myIsNotNullCheckMethods = new ArrayList(); + + private List myAssertIsNullMethods = new ArrayList(); + private List myAssertIsNotNullMethods = new ArrayList(); + + private List myAssertTrueMethods = new ArrayList(); + private List myAssertFalseMethods = new ArrayList(); + + private static Project myProject; + + public static ConditionCheckManager getInstance(Project project) { + myProject = project; + return ServiceManager.getService(project, ConditionCheckManager.class); + } + + public void setIsNullCheckMethods(List methodConditionChecks) { + myIsNullCheckMethods.clear(); + myIsNullCheckMethods.addAll(methodConditionChecks); + } + + public void setIsNotNullCheckMethods(List methodConditionChecks) { + myIsNotNullCheckMethods.clear(); + myIsNotNullCheckMethods.addAll(methodConditionChecks); + } + + public void setAssertNullMethods(List methodConditionChecks) { + myAssertIsNullMethods.clear(); + myAssertIsNullMethods.addAll(methodConditionChecks); + } + + public void setAssertNotNullMethods(List methodConditionChecks) { + myAssertIsNotNullMethods.clear(); + myAssertIsNotNullMethods.addAll(methodConditionChecks); + } + + public void setAssertTrueMethods(List psiMethodWrappers) { + myAssertTrueMethods.clear(); + myAssertTrueMethods.addAll(psiMethodWrappers); + } + + public void setAssertFalseMethods(List psiMethodWrappers) { + myAssertFalseMethods.clear(); + myAssertFalseMethods.addAll(psiMethodWrappers); + } + + public List getIsNullCheckMethods() { + return myIsNullCheckMethods; + } + + public List getIsNotNullCheckMethods() { + return myIsNotNullCheckMethods; + } + + public List getAssertIsNullMethods() { + return myAssertIsNullMethods; + } + + public List getAssertIsNotNullMethods() { + return myAssertIsNotNullMethods; + } + + public List getAssertFalseMethods() { + return myAssertFalseMethods; + } + + public List getAssertTrueMethods() { + return myAssertTrueMethods; + } + + public static class State { + public List myIsNullCheckMethods = new ArrayList(); + public List myIsNotNullCheckMethods = new ArrayList(); + public List myAssertIsNullMethods = new ArrayList(); + public List myAssertIsNotNullMethods = new ArrayList(); + public List myAssertTrueMethods = new ArrayList(); + public List myAssertFalseMethods = new ArrayList(); + } + + @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 listToLoadTo, List 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 listToLoadTo, List 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 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 checkers) { + for (MethodConditionCheck checker : checkers) { + if (checker.matches(psiMethod)) + return true; + } + return false; + } + + public static boolean methodMatches(PsiMethod psiMethod, int paramIndex, List 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()); + } +} diff --git a/java/java-impl/src/com/intellij/codeInsight/ConditionChecker.java b/java/java-impl/src/com/intellij/codeInsight/ConditionChecker.java new file mode 100644 index 000000000000..ffcbb3c57558 --- /dev/null +++ b/java/java-impl/src/com/intellij/codeInsight/ConditionChecker.java @@ -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 Johnny Clark + * 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(); +} diff --git a/java/java-impl/src/com/intellij/codeInsight/MethodConditionCheck.java b/java/java-impl/src/com/intellij/codeInsight/MethodConditionCheck.java new file mode 100644 index 000000000000..b8f1afbf9442 --- /dev/null +++ b/java/java-impl/src/com/intellij/codeInsight/MethodConditionCheck.java @@ -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 + *
+ * {@code
+ *  class Foo {
+ *     static boolean validateNotNull(Object o) {
+ *       if (o == null) return false;
+ *       else return true;
+ *     }
+ *   }
+ * }
+ *
+ * The corresponding MethodConditionCheck would be 

+ * 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 + * + *

+ * {@code
+ *   if (Value.isNotNull(o)) {
+ *     if(o != null) {}
+ *   }
+ * }
+ * 
+ * + * @author Johnny Clark + * Creation Date: 8/14/12 + */ +public class MethodConditionCheck implements ConditionChecker, Comparable { + 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 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 initParameterNamesFromPsiMethod(PsiParameter selectedParameter) { + List parameters = new ArrayList(); + 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 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 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 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 psiMethods, String allParametersSubString) { + String[] parameterClassAndNameArray = allParametersSubString.split(","); + List parameterClassToMatch = new ArrayList(); + for (String parameterClassAndName : parameterClassAndNameArray) { + parameterClassAndName = parameterClassAndName.replace("*", "").trim(); + parameterClassAndName = parameterClassAndName.substring(0, parameterClassAndName.indexOf(" ")).trim(); + parameterClassToMatch.add(parameterClassAndName); + } + + for (PsiMethod method : psiMethods) { + List parameterForCurrentMethod = new ArrayList(); + 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 findPsiMethodsInPsiClassWithMatchingMethodName(PsiClass psiClass, String methodName) { + List psiMethods = new ArrayList(); + 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); + } + } +} diff --git a/java/java-impl/src/com/intellij/codeInspection/dataFlow/ConditionCheckDialog.java b/java/java-impl/src/com/intellij/codeInspection/dataFlow/ConditionCheckDialog.java new file mode 100644 index 000000000000..d48698d2dfa4 --- /dev/null +++ b/java/java-impl/src/com/intellij/codeInspection/dataFlow/ConditionCheckDialog.java @@ -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 + *
    + *
  1. Is Null Check MethodsPanel
  2. + *
  3. Is Not Null Check MethodsPanel
  4. + *
  5. Assert Is Null MethodsPanel
  6. + *
  7. Assert Is Not Null MethodsPanel
  8. + *
  9. Assert True MethodsPanel
  10. + *
  11. Assert False MethodsPanel
  12. + *
+ * + * @author Johnny Clark + * 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 isNullCheckMethods = new ArrayList(manager.getIsNullCheckMethods()); + List isNotNullCheckMethods = new ArrayList(manager.getIsNotNullCheckMethods()); + List assertIsNullMethods = new ArrayList(manager.getAssertIsNullMethods()); + List assertIsNotNullMethods = new ArrayList(manager.getAssertIsNotNullMethods()); + List assertTrueMethods = new ArrayList(manager.getAssertTrueMethods()); + List assertFalseMethods = new ArrayList(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 otherPanels; + + public MethodsPanel(final List checkers, final ConditionChecker.Type type, final Project myProject) { + this.myProject = myProject; + myList = new JBList(new CollectionListModel(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 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 model = getCollectionListModel(); + if (model.getSize() <= index) + model.add(chk); + else + model.setElementAt(chk, index); + } + } + + private CollectionListModel getCollectionListModel() { + //noinspection unchecked + return (CollectionListModel) myList.getModel(); + } + + @NotNull + public JPanel getComponent() { + return myPanel; + } + + public List getMethodConditionChecker() { + CollectionListModel model = getCollectionListModel(); + return new ArrayList(model.getItems()); + } + + public Set getConditionCheckers() { + Set set = new HashSet(); + set.addAll(getMethodConditionChecker()); + return set; + } + + public void setOtherMethodsPanels(MethodsPanel p1, MethodsPanel p2, MethodsPanel p3, MethodsPanel p4, MethodsPanel p5) { + otherPanels = new HashSet(); + otherPanels.add(p1); + otherPanels.add(p2); + otherPanels.add(p3); + otherPanels.add(p4); + otherPanels.add(p5); + } + + public Set getOtherCheckers() { + Set otherCheckers = new HashSet(); + for (MethodsPanel otherPanel : otherPanels) { + otherCheckers.addAll(otherPanel.getConditionCheckers()); + } + return otherCheckers; + } + } +} diff --git a/java/java-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java b/java/java-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java index e3170217680f..2ec949a983bd 100644 --- a/java/java-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java +++ b/java/java-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java @@ -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")) { diff --git a/java/java-impl/src/com/intellij/codeInspection/dataFlow/DataFlowInspection.java b/java/java-impl/src/com/intellij/codeInspection/dataFlow/DataFlowInspection.java index 56b1d22041f4..2b7bd60bae48 100644 --- a/java/java-impl/src/com/intellij/codeInspection/dataFlow/DataFlowInspection.java +++ b/java/java-impl/src/com/intellij/codeInspection/dataFlow/DataFlowInspection.java @@ -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; diff --git a/java/java-impl/src/com/intellij/codeInspection/dataFlow/MethodCheckerDetailsDialog.java b/java/java-impl/src/com/intellij/codeInspection/dataFlow/MethodCheckerDetailsDialog.java new file mode 100644 index 000000000000..cd261e1c9214 --- /dev/null +++ b/java/java-impl/src/com/intellij/codeInspection/dataFlow/MethodCheckerDetailsDialog.java @@ -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 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 otherCheckersSameType, @NotNull Set otherCheckers) { + super(component, true); + if (!isSupported(type)) + throw new IllegalArgumentException("Type is invalid " + type); + + myProject = project; + myType = type; + myOtherCheckers = new HashSet(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 classComponent = new LabeledComponent(); + final LabeledComponent methodComponent = new LabeledComponent(); + final LabeledComponent parameterComponent = new LabeledComponent(); + + 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 myModel; + + MethodDropDown(@Nullable PsiClass psiClass, @Nullable PsiMethod psiMethod, @NotNull ConditionChecker.Type type, SortedComboBoxModel 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 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 buildModel() { + return new SortedComboBoxModel(new Comparator() { + @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 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 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 getParameterWrappers() { + List wrappers = new ArrayList(); + 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 buildModel() { + return new SortedComboBoxModel(new Comparator() { + @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 { + 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 { + private final @NotNull PsiMethod myPsiMethod; + private final @NotNull String myId; + + MethodWrapper(@NotNull PsiMethod psiMethod) { + this.myPsiMethod = psiMethod; + + List parameters = new ArrayList(); + 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 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); + } + } +} diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/AssertFalse.java b/java/java-tests/testData/inspection/dataFlow/fixture/AssertFalse.java new file mode 100644 index 000000000000..3aa73afcff5b --- /dev/null +++ b/java/java-tests/testData/inspection/dataFlow/fixture/AssertFalse.java @@ -0,0 +1,9 @@ +public class AssertFalse { + void bar() { + final boolean b = call(); + if (Assertions.assertFalse(b)) { + if(b) {} + } + } + boolean call() {return true;} +} diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/AssertIsNotNull.java b/java/java-tests/testData/inspection/dataFlow/fixture/AssertIsNotNull.java new file mode 100644 index 000000000000..5b16eb78ac7b --- /dev/null +++ b/java/java-tests/testData/inspection/dataFlow/fixture/AssertIsNotNull.java @@ -0,0 +1,10 @@ +import java.lang.*; + +public class AssertIsNotNull { + void bar() { + final Object o = call(); + Assertions.assertIsNotNull(o); + if(o == null) {} + } + Object call() {return new Object();} +} diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/AssertIsNull.java b/java/java-tests/testData/inspection/dataFlow/fixture/AssertIsNull.java new file mode 100644 index 000000000000..5915277cc273 --- /dev/null +++ b/java/java-tests/testData/inspection/dataFlow/fixture/AssertIsNull.java @@ -0,0 +1,10 @@ +import java.lang.*; + +public class AssertIsNull { + void bar() { + final Object o = call(); + Assertions.assertIsNull(o); + if(o == null) {} + } + Object call() {return new Object();} +} diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/AssertTrue.java b/java/java-tests/testData/inspection/dataFlow/fixture/AssertTrue.java new file mode 100644 index 000000000000..a71e4382ab44 --- /dev/null +++ b/java/java-tests/testData/inspection/dataFlow/fixture/AssertTrue.java @@ -0,0 +1,9 @@ +public class AssertTrue { + void bar() { + final boolean b = call(); + if (Assertions.assertTrue(b)) { + if(b) {} + } + } + boolean call() {return true;} +} diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/IsNotNullCheck.java b/java/java-tests/testData/inspection/dataFlow/fixture/IsNotNullCheck.java new file mode 100644 index 000000000000..85879eda40a7 --- /dev/null +++ b/java/java-tests/testData/inspection/dataFlow/fixture/IsNotNullCheck.java @@ -0,0 +1,9 @@ +public class IsNotNullCheck { + void bar() { + final Value v = call(); + if (Value.isNotNull(v)) { + if(v == null) {} + } + } + Value call() {return new Value();} +} diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/IsNullCheck.java b/java/java-tests/testData/inspection/dataFlow/fixture/IsNullCheck.java new file mode 100644 index 000000000000..1b2c48593a16 --- /dev/null +++ b/java/java-tests/testData/inspection/dataFlow/fixture/IsNullCheck.java @@ -0,0 +1,9 @@ +public class IsNullCheck { + void bar() { + final Value v = call(); + if (Value.isNull(v)) { + if(v == null) {} + } + } + Value call() {return new Value();} +} diff --git a/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTest.java b/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTest.java index 56fa8115d95f..fb8f28866961 100644 --- a/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTest.java @@ -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); + } } diff --git a/platform/platform-resources-en/src/messages/InspectionsBundle.properties b/platform/platform-resources-en/src/messages/InspectionsBundle.properties index 084f692e3dc8..663c46229631 100644 --- a/platform/platform-resources-en/src/messages/InspectionsBundle.properties +++ b/platform/platform-resources-en/src/messages/InspectionsBundle.properties @@ -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 #ref #loc may produce java.lang.NullPointerException diff --git a/resources/src/META-INF/IdeaPlugin.xml b/resources/src/META-INF/IdeaPlugin.xml index a9768a9a1cef..3c635fe04277 100644 --- a/resources/src/META-INF/IdeaPlugin.xml +++ b/resources/src/META-INF/IdeaPlugin.xml @@ -433,6 +433,9 @@ + + From b34abcdbbc48bdfe6d7919a7601078ad6bd9da25 Mon Sep 17 00:00:00 2001 From: peter Date: Thu, 21 Feb 2013 11:21:30 +0100 Subject: [PATCH 2/7] don't hold Project in statics --- .../com/intellij/codeInsight/ConditionCheckManager.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/java/java-impl/src/com/intellij/codeInsight/ConditionCheckManager.java b/java/java-impl/src/com/intellij/codeInsight/ConditionCheckManager.java index 539a3a1b231a..92676fc2ecef 100644 --- a/java/java-impl/src/com/intellij/codeInsight/ConditionCheckManager.java +++ b/java/java-impl/src/com/intellij/codeInsight/ConditionCheckManager.java @@ -45,10 +45,13 @@ public class ConditionCheckManager implements PersistentStateComponent myAssertTrueMethods = new ArrayList(); private List myAssertFalseMethods = new ArrayList(); - private static Project myProject; + private Project myProject; + + public ConditionCheckManager(Project project) { + myProject = project; + } public static ConditionCheckManager getInstance(Project project) { - myProject = project; return ServiceManager.getService(project, ConditionCheckManager.class); } From 7beb260d2331851eb98d1f6e853e6e20c5b8014d Mon Sep 17 00:00:00 2001 From: peter Date: Thu, 21 Feb 2013 11:42:59 +0100 Subject: [PATCH 3/7] @NotNull --- .../src/com/intellij/codeInsight/ConditionChecker.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/java/java-impl/src/com/intellij/codeInsight/ConditionChecker.java b/java/java-impl/src/com/intellij/codeInsight/ConditionChecker.java index ffcbb3c57558..3fb86a0d48b0 100644 --- a/java/java-impl/src/com/intellij/codeInsight/ConditionChecker.java +++ b/java/java-impl/src/com/intellij/codeInsight/ConditionChecker.java @@ -16,6 +16,7 @@ package com.intellij.codeInsight; import com.intellij.psi.*; +import org.jetbrains.annotations.NotNull; /** * Interface for IsNull, IsNotNull Method Checks and Assert True/False/IsNull/IsNotNull Method Checks to be performed by the Constant Condition Inspection. @@ -51,5 +52,6 @@ public interface ConditionChecker { boolean overlaps(ConditionChecker checker); + @NotNull Type getType(); } From 54ff2eb7a2951d36e7a025777e5f41ba11e30ea6 Mon Sep 17 00:00:00 2001 From: peter Date: Thu, 21 Feb 2013 11:43:48 +0100 Subject: [PATCH 4/7] fix some idea warnings --- .../codeInsight/MethodConditionCheck.java | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/java/java-impl/src/com/intellij/codeInsight/MethodConditionCheck.java b/java/java-impl/src/com/intellij/codeInsight/MethodConditionCheck.java index b8f1afbf9442..46c3121d6619 100644 --- a/java/java-impl/src/com/intellij/codeInsight/MethodConditionCheck.java +++ b/java/java-impl/src/com/intellij/codeInsight/MethodConditionCheck.java @@ -154,6 +154,7 @@ public class MethodConditionCheck implements ConditionChecker, Comparable parameters) { + private static String initFullName(String className, String methodName, List parameters) { String s = className + "." + methodName + "("; for (String parameterName : parameters) { s += parameterName + ", "; @@ -234,7 +233,7 @@ public class MethodConditionCheck implements ConditionChecker, Comparable parameterNames) { + private static String initShortName(String methodName, List parameterNames) { String shortName = methodName + "("; for (String parameterName : parameterNames) { if (parameterNames.lastIndexOf(".") > -1) @@ -330,7 +329,7 @@ public class MethodConditionCheck implements ConditionChecker, Comparable psiMethods, String allParametersSubString) { + private static PsiMethod findPsiMethodWithMatchingParameters(List psiMethods, String allParametersSubString) { String[] parameterClassAndNameArray = allParametersSubString.split(","); List parameterClassToMatch = new ArrayList(); for (String parameterClassAndName : parameterClassAndNameArray) { @@ -378,7 +377,7 @@ public class MethodConditionCheck implements ConditionChecker, Comparable findPsiMethodsInPsiClassWithMatchingMethodName(PsiClass psiClass, String methodName) { + private static List findPsiMethodsInPsiClassWithMatchingMethodName(PsiClass psiClass, String methodName) { List psiMethods = new ArrayList(); for (int i = 0; i < psiClass.getMethods().length; i++) { PsiMethod possibleMatchPsiMethod = psiClass.getMethods()[i]; From 149009eb41388a80b77d5d488041f1e81c2090b5 Mon Sep 17 00:00:00 2001 From: peter Date: Wed, 27 Feb 2013 14:02:14 +0100 Subject: [PATCH 5/7] IDEA-101843 Seems "Super Method" action can be dumb aware --- .../navigation/actions/GotoSuperAction.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/codeInsight/navigation/actions/GotoSuperAction.java b/platform/lang-impl/src/com/intellij/codeInsight/navigation/actions/GotoSuperAction.java index 13f75d480460..f74f9b7ea493 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/navigation/actions/GotoSuperAction.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/navigation/actions/GotoSuperAction.java @@ -22,6 +22,9 @@ import com.intellij.lang.CodeInsightActions; import com.intellij.lang.Language; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.project.DumbAware; +import com.intellij.openapi.project.DumbService; +import com.intellij.openapi.project.IndexNotReadyException; import com.intellij.openapi.project.Project; import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiFile; @@ -29,7 +32,7 @@ import com.intellij.psi.util.PsiUtilBase; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; -public class GotoSuperAction extends BaseCodeInsightAction implements CodeInsightActionHandler { +public class GotoSuperAction extends BaseCodeInsightAction implements CodeInsightActionHandler, DumbAware { @NonNls public static final String FEATURE_ID = "navigation.goto.super"; @@ -48,7 +51,12 @@ public class GotoSuperAction extends BaseCodeInsightAction implements CodeInsigh final CodeInsightActionHandler codeInsightActionHandler = CodeInsightActions.GOTO_SUPER.forLanguage(language); if (codeInsightActionHandler != null) { - codeInsightActionHandler.invoke(project, editor, file); + try { + codeInsightActionHandler.invoke(project, editor, file); + } + catch (IndexNotReadyException e) { + DumbService.getInstance(project).showDumbModeNotification("Goto Super action is not available during indexing"); + } } } From 3b8b7b84cb54cab6ff9c89ddcab90d771844409d Mon Sep 17 00:00:00 2001 From: peter Date: Wed, 27 Feb 2013 14:58:36 +0100 Subject: [PATCH 6/7] commitDocument exceptions shouldn't fail the entire save (IDEA-101980) --- .../src/com/intellij/psi/impl/PsiDocumentManagerImpl.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/platform/lang-impl/src/com/intellij/psi/impl/PsiDocumentManagerImpl.java b/platform/lang-impl/src/com/intellij/psi/impl/PsiDocumentManagerImpl.java index 05a59804171a..93a2277cc51d 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/PsiDocumentManagerImpl.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/PsiDocumentManagerImpl.java @@ -819,7 +819,12 @@ public class PsiDocumentManagerImpl extends PsiDocumentManager implements Projec // Ensure all documents are committed on save so file content dependent indices, that use PSI to build have consistent content. UIUtil.invokeLaterIfNeeded(new Runnable() { public void run() { - commitAllDocuments(); + try { + commitAllDocuments(); + } + catch (Exception e) { + LOG.error(e); + } } }); } From a70d203871df8f5f12cc1101b581037e725457af Mon Sep 17 00:00:00 2001 From: peter Date: Wed, 27 Feb 2013 15:49:50 +0100 Subject: [PATCH 7/7] [Johnny Clark] make condition checkers psi-independent, minor fixes (IDEA-35808) --- .../codeInsight/ConditionCheckManager.java | 82 ++-- .../codeInsight/ConditionChecker.java | 398 +++++++++++++++++- .../codeInsight/MethodConditionCheck.java | 395 ----------------- .../dataFlow/ConditionCheckDialog.java | 166 ++++---- .../dataFlow/MethodCheckerDetailsDialog.java | 371 +++++++++------- .../DataFlowInspectionTest.java | 36 +- 6 files changed, 761 insertions(+), 687 deletions(-) delete mode 100644 java/java-impl/src/com/intellij/codeInsight/MethodConditionCheck.java diff --git a/java/java-impl/src/com/intellij/codeInsight/ConditionCheckManager.java b/java/java-impl/src/com/intellij/codeInsight/ConditionCheckManager.java index 92676fc2ecef..b9be30108002 100644 --- a/java/java-impl/src/com/intellij/codeInsight/ConditionCheckManager.java +++ b/java/java-impl/src/com/intellij/codeInsight/ConditionCheckManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2012 JetBrains s.r.o. + * 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. @@ -29,83 +29,77 @@ import java.util.List; * Creation Date: 8/3/12 */ @State( - name = "IsNullIsNotNullCheckManager", + name = "ConditionCheckManager", storages = {@Storage(id = "dir", file = StoragePathMacros.PROJECT_CONFIG_DIR + "/checker.xml", scheme = StorageScheme.DIRECTORY_BASED)} ) public class ConditionCheckManager implements PersistentStateComponent { @SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"}) private State state; private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.ConditionCheckManager"); - private List myIsNullCheckMethods = new ArrayList(); - private List myIsNotNullCheckMethods = new ArrayList(); + private List myIsNullCheckMethods = new ArrayList(); + private List myIsNotNullCheckMethods = new ArrayList(); - private List myAssertIsNullMethods = new ArrayList(); - private List myAssertIsNotNullMethods = new ArrayList(); + private List myAssertIsNullMethods = new ArrayList(); + private List myAssertIsNotNullMethods = new ArrayList(); - private List myAssertTrueMethods = new ArrayList(); - private List myAssertFalseMethods = new ArrayList(); - - private Project myProject; - - public ConditionCheckManager(Project project) { - myProject = project; - } + private List myAssertTrueMethods = new ArrayList(); + private List myAssertFalseMethods = new ArrayList(); public static ConditionCheckManager getInstance(Project project) { return ServiceManager.getService(project, ConditionCheckManager.class); } - public void setIsNullCheckMethods(List methodConditionChecks) { + public void setIsNullCheckMethods(List methodConditionChecks) { myIsNullCheckMethods.clear(); myIsNullCheckMethods.addAll(methodConditionChecks); } - public void setIsNotNullCheckMethods(List methodConditionChecks) { + public void setIsNotNullCheckMethods(List methodConditionChecks) { myIsNotNullCheckMethods.clear(); myIsNotNullCheckMethods.addAll(methodConditionChecks); } - public void setAssertNullMethods(List methodConditionChecks) { + public void setAssertIsNullMethods(List methodConditionChecks) { myAssertIsNullMethods.clear(); myAssertIsNullMethods.addAll(methodConditionChecks); } - public void setAssertNotNullMethods(List methodConditionChecks) { + public void setAssertIsNotNullMethods(List methodConditionChecks) { myAssertIsNotNullMethods.clear(); myAssertIsNotNullMethods.addAll(methodConditionChecks); } - public void setAssertTrueMethods(List psiMethodWrappers) { + public void setAssertTrueMethods(List psiMethodWrappers) { myAssertTrueMethods.clear(); myAssertTrueMethods.addAll(psiMethodWrappers); } - public void setAssertFalseMethods(List psiMethodWrappers) { + public void setAssertFalseMethods(List psiMethodWrappers) { myAssertFalseMethods.clear(); myAssertFalseMethods.addAll(psiMethodWrappers); } - public List getIsNullCheckMethods() { + public List getIsNullCheckMethods() { return myIsNullCheckMethods; } - public List getIsNotNullCheckMethods() { + public List getIsNotNullCheckMethods() { return myIsNotNullCheckMethods; } - public List getAssertIsNullMethods() { + public List getAssertIsNullMethods() { return myAssertIsNullMethods; } - public List getAssertIsNotNullMethods() { + public List getAssertIsNotNullMethods() { return myAssertIsNotNullMethods; } - public List getAssertFalseMethods() { + public List getAssertFalseMethods() { return myAssertFalseMethods; } - public List getAssertTrueMethods() { + public List getAssertTrueMethods() { return myAssertTrueMethods; } @@ -132,8 +126,8 @@ public class ConditionCheckManager implements PersistentStateComponent listToLoadTo, List listToLoadFrom) { - for (MethodConditionCheck checker : listToLoadFrom) { + private static void loadMethodChecksToState(List listToLoadTo, List listToLoadFrom) { + for (ConditionChecker checker : listToLoadFrom) { listToLoadTo.add(checker.toString()); } } @@ -149,20 +143,20 @@ public class ConditionCheckManager implements PersistentStateComponent listToLoadTo, List listToLoadFrom, ConditionChecker.Type type){ + public void loadMethods(List listToLoadTo, List listToLoadFrom, ConditionChecker.Type type){ listToLoadTo.clear(); for (String setting : listToLoadFrom) { try { - listToLoadTo.add(new MethodConditionCheck.Builder(setting, type, myProject).build()); + 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 checkers) { - for (MethodConditionCheck checker : checkers) { - if (checker.matches(psiMethod)) { + public static boolean isMethod(@NotNull PsiMethod psiMethod, List checkers) { + for (ConditionChecker checker : checkers) { + if (checker.matchesPsiMethod(psiMethod)) { return true; } } @@ -227,29 +221,19 @@ public class ConditionCheckManager implements PersistentStateComponent checkers) { - for (MethodConditionCheck checker : checkers) { - if (checker.matches(psiMethod)) + public static boolean methodMatches(PsiMethod psiMethod, List checkers) { + for (ConditionChecker checker : checkers) { + if (checker.matchesPsiMethod(psiMethod)) return true; } return false; } - public static boolean methodMatches(PsiMethod psiMethod, int paramIndex, List checkers) { - for (MethodConditionCheck checker : checkers) { - if (checker.matches(psiMethod, paramIndex)) + public static boolean methodMatches(PsiMethod psiMethod, int paramIndex, List checkers) { + for (ConditionChecker checker : checkers) { + if (checker.matchesPsiMethod(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()); - } } diff --git a/java/java-impl/src/com/intellij/codeInsight/ConditionChecker.java b/java/java-impl/src/com/intellij/codeInsight/ConditionChecker.java index 3fb86a0d48b0..de3871d0e61b 100644 --- a/java/java-impl/src/com/intellij/codeInsight/ConditionChecker.java +++ b/java/java-impl/src/com/intellij/codeInsight/ConditionChecker.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2012 JetBrains s.r.o. + * 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. @@ -15,20 +15,50 @@ */ package com.intellij.codeInsight; +import com.intellij.openapi.diagnostic.*; import com.intellij.psi.*; -import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.*; + +import java.io.*; +import java.util.*; + /** - * 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. + * 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 + *
+ * {@code
+ *  class Foo {
+ *     static boolean validateNotNull(Object o) {
+ *       if (o == null) return false;
+ *       else return true;
+ *     }
+ *   }
+ * }
+ *
+ * The corresponding ConditionCheck would be 

+ * 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 + * + *

+ * {@code
+ *   if (Value.isNotNull(o)) {
+ *     if(o != null) {}
+ *   }
+ * }
+ * 
* * @author Johnny Clark * Creation Date: 8/14/12 */ -public interface ConditionChecker { - enum Type { +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"), @@ -47,11 +77,355 @@ public interface ConditionChecker { } } - boolean matches(PsiMethod psiMethod); - boolean matches(PsiMethod psiMethod, int paramIndex); + private final @NotNull String myClassName; + private final @NotNull String myMethodName; + private final @NotNull List myParameterClassList; + private final int myCheckedParameterIndex; + private final String myFullName; - boolean overlaps(ConditionChecker checker); + private ConditionChecker(@NotNull String className, + @NotNull String methodName, + @NotNull List 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 - Type getType(); + 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 parameterClasses, + List 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 parameterClasses = new ArrayList(); + List parameterNames = new ArrayList(); + 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 initParameterClassListFromPsiMethod(PsiMethod psiMethod) { + List parameterClasses = new ArrayList(); + 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 initParameterNameListFromPsiMethod(PsiMethod psiMethod) { + List parameterNames = new ArrayList(); + 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 parameterClassList = initParameterClassListFromPsiMethod(psiMethod); + List 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; + } + } + } } diff --git a/java/java-impl/src/com/intellij/codeInsight/MethodConditionCheck.java b/java/java-impl/src/com/intellij/codeInsight/MethodConditionCheck.java deleted file mode 100644 index 46c3121d6619..000000000000 --- a/java/java-impl/src/com/intellij/codeInsight/MethodConditionCheck.java +++ /dev/null @@ -1,395 +0,0 @@ -/* - * 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 - *
- * {@code
- *  class Foo {
- *     static boolean validateNotNull(Object o) {
- *       if (o == null) return false;
- *       else return true;
- *     }
- *   }
- * }
- *
- * The corresponding MethodConditionCheck would be 

- * 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 - * - *

- * {@code
- *   if (Value.isNotNull(o)) {
- *     if(o != null) {}
- *   }
- * }
- * 
- * - * @author Johnny Clark - * Creation Date: 8/14/12 - */ -public class MethodConditionCheck implements ConditionChecker, Comparable { - 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 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; - } - - @NotNull - @Override - public Type getType() { - return myType; - } - - private void validatePsiMethod() { - PsiElement psiElement = myPsiMethod.getContainingClass(); - if (psiElement == null) - 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() { - return myPsiMethod.getContainingClass().getQualifiedName(); - } - - private String initMethodNameFromPsiMethod() { - return myPsiMethod.getName(); - } - - private List initParameterNamesFromPsiMethod(PsiParameter selectedParameter) { - List parameters = new ArrayList(); - 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 static String initFullName(String className, String methodName, List parameters) { - String s = className + "." + methodName + "("; - for (String parameterName : parameters) { - s += parameterName + ", "; - } - s = s.substring(0, s.length() - 2); - s += ")"; - return s; - } - - private static String initShortName(String methodName, List 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 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 static 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 static PsiMethod findPsiMethodWithMatchingParameters(List psiMethods, String allParametersSubString) { - String[] parameterClassAndNameArray = allParametersSubString.split(","); - List parameterClassToMatch = new ArrayList(); - for (String parameterClassAndName : parameterClassAndNameArray) { - parameterClassAndName = parameterClassAndName.replace("*", "").trim(); - parameterClassAndName = parameterClassAndName.substring(0, parameterClassAndName.indexOf(" ")).trim(); - parameterClassToMatch.add(parameterClassAndName); - } - - for (PsiMethod method : psiMethods) { - List parameterForCurrentMethod = new ArrayList(); - 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 static List findPsiMethodsInPsiClassWithMatchingMethodName(PsiClass psiClass, String methodName) { - List psiMethods = new ArrayList(); - 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); - } - } -} diff --git a/java/java-impl/src/com/intellij/codeInspection/dataFlow/ConditionCheckDialog.java b/java/java-impl/src/com/intellij/codeInspection/dataFlow/ConditionCheckDialog.java index d48698d2dfa4..0f8a7257efdf 100644 --- a/java/java-impl/src/com/intellij/codeInspection/dataFlow/ConditionCheckDialog.java +++ b/java/java-impl/src/com/intellij/codeInspection/dataFlow/ConditionCheckDialog.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2012 JetBrains s.r.o. + * 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. @@ -33,12 +33,12 @@ 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 *
    - *
  1. Is Null Check MethodsPanel
  2. - *
  3. Is Not Null Check MethodsPanel
  4. - *
  5. Assert Is Null MethodsPanel
  6. - *
  7. Assert Is Not Null MethodsPanel
  8. - *
  9. Assert True MethodsPanel
  10. - *
  11. Assert False MethodsPanel
  12. + *
  13. Is Null Check MethodsPanel
  14. + *
  15. Is Not Null Check MethodsPanel
  16. + *
  17. Assert Is Null MethodsPanel
  18. + *
  19. Assert Is Not Null MethodsPanel
  20. + *
  21. Assert True MethodsPanel
  22. + *
  23. Assert False MethodsPanel
  24. *
* * @author Johnny Clark @@ -65,12 +65,12 @@ public class ConditionCheckDialog extends DialogWrapper { final Splitter isNullIsNotNullCheckMethodSplitter = new Splitter(false); final Splitter assertTrueFalseMethodSplitter = new Splitter(false); - List isNullCheckMethods = new ArrayList(manager.getIsNullCheckMethods()); - List isNotNullCheckMethods = new ArrayList(manager.getIsNotNullCheckMethods()); - List assertIsNullMethods = new ArrayList(manager.getAssertIsNullMethods()); - List assertIsNotNullMethods = new ArrayList(manager.getAssertIsNotNullMethods()); - List assertTrueMethods = new ArrayList(manager.getAssertTrueMethods()); - List assertFalseMethods = new ArrayList(manager.getAssertFalseMethods()); + List isNullCheckMethods = new ArrayList(manager.getIsNullCheckMethods()); + List isNotNullCheckMethods = new ArrayList(manager.getIsNotNullCheckMethods()); + List assertIsNullMethods = new ArrayList(manager.getAssertIsNullMethods()); + List assertIsNotNullMethods = new ArrayList(manager.getAssertIsNotNullMethods()); + List assertTrueMethods = new ArrayList(manager.getAssertTrueMethods()); + List assertFalseMethods = new ArrayList(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); @@ -92,15 +92,27 @@ public class ConditionCheckDialog extends DialogWrapper { mainSplitter.setFirstComponent(topThirdSplitter); mainSplitter.setSecondComponent(bottomTwoThirdsSplitter); - topThirdSplitter.setPreferredSize(new Dimension(600, 400)); - bottomTwoThirdsSplitter.setPreferredSize(new Dimension(600, 800)); + 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); + 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); @@ -114,12 +126,12 @@ public class ConditionCheckDialog extends DialogWrapper { @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()); + 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(); } @@ -133,12 +145,12 @@ public class ConditionCheckDialog extends DialogWrapper { private final @NotNull Project myProject; private Set otherPanels; - public MethodsPanel(final List checkers, final ConditionChecker.Type type, final Project myProject) { + public MethodsPanel(final List checkers, final ConditionChecker.Type type, final @NotNull Project myProject) { this.myProject = myProject; - myList = new JBList(new CollectionListModel(checkers)); + myList = new JBList(new CollectionListModel(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)); + myPanel.setPreferredSize(new Dimension(400, 150)); myList.setCellRenderer(new ColoredListCellRenderer() { @Override @@ -147,25 +159,29 @@ public class ConditionCheckDialog extends DialogWrapper { if (s.contains("*")) { int indexOfAsterix1 = s.indexOf("*"); int indexOfAsterix2 = s.lastIndexOf("*"); - if (indexOfAsterix1 >= 0 && indexOfAsterix1 < s.length() && indexOfAsterix2 >= 0 && indexOfAsterix2 < s.length() && indexOfAsterix1 < indexOfAsterix2) { + 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 { + } + else { append(s, SimpleTextAttributes.REGULAR_ATTRIBUTES); } } } }); - final ToolbarDecorator toolbarDecorator = ToolbarDecorator.createDecorator(myList).disableUpDownActions() - .setAddAction(new AnActionButtonRunnable() { + 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() { + }).setRemoveAction(new AnActionButtonRunnable() { @Override public void run(AnActionButton anActionButton) { CollectionListModel model = getCollectionListModel(); @@ -175,57 +191,65 @@ public class ConditionCheckDialog extends DialogWrapper { } }); - myList.addMouseListener( - new MouseAdapter() { - public void mouseClicked(MouseEvent e) { - if (e.getClickCount() == 2) { - int index = myList.locationToIndex(e.getPoint()); - CollectionListModel model = getCollectionListModel(); - if (index >= 0 && model.getSize() > index) { - chooseMethod(model.getElementAt(index), type, index); - } + myList.addMouseListener(new MouseAdapter() { + public void mouseClicked(MouseEvent e) { + if (e.getClickCount() == 2) { + int index = myList.locationToIndex(e.getPoint()); + CollectionListModel 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)) + 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)) + } + 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)) + } + 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)) + } + 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)) + } + 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)) + } + else if (type.equals(ConditionChecker.Type.ASSERT_FALSE_METHOD)) { return InspectionsBundle.message("configure.checker.option.assert.false.method.panel.title"); - else + } + 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 model = getCollectionListModel(); - if (model.getSize() <= index) - model.add(chk); - else - model.setElementAt(chk, index); } } - private CollectionListModel getCollectionListModel() { + 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 model = getCollectionListModel(); + if (model.getSize() <= index) { + model.add(chk); + } + else { + model.setElementAt(chk, index); + } + } + } + + private CollectionListModel getCollectionListModel() { //noinspection unchecked - return (CollectionListModel) myList.getModel(); + return (CollectionListModel)myList.getModel(); } @NotNull @@ -233,14 +257,14 @@ public class ConditionCheckDialog extends DialogWrapper { return myPanel; } - public List getMethodConditionChecker() { - CollectionListModel model = getCollectionListModel(); - return new ArrayList(model.getItems()); + public List getConditionChecker() { + CollectionListModel model = getCollectionListModel(); + return new ArrayList(model.getItems()); } public Set getConditionCheckers() { Set set = new HashSet(); - set.addAll(getMethodConditionChecker()); + set.addAll(getConditionChecker()); return set; } diff --git a/java/java-impl/src/com/intellij/codeInspection/dataFlow/MethodCheckerDetailsDialog.java b/java/java-impl/src/com/intellij/codeInspection/dataFlow/MethodCheckerDetailsDialog.java index cd261e1c9214..5d34364e6452 100644 --- a/java/java-impl/src/com/intellij/codeInspection/dataFlow/MethodCheckerDetailsDialog.java +++ b/java/java-impl/src/com/intellij/codeInspection/dataFlow/MethodCheckerDetailsDialog.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2012 JetBrains s.r.o. + * 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. @@ -45,33 +45,55 @@ class MethodCheckerDetailsDialog extends DialogWrapper implements PropertyChange private final @NotNull MethodDropDown methodDropDown; private final @NotNull ClassField classField; private final @NotNull Set myOtherCheckers; - private final @Nullable MethodConditionCheck myPreviouslySelectedChecker; - + 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 MethodConditionCheck mySelectedChecker; + private @Nullable ConditionChecker mySelectedChecker; - MethodCheckerDetailsDialog(@Nullable MethodConditionCheck previouslySelectedChecker, @NotNull ConditionChecker.Type type, @NotNull Project project, @NotNull Component component, @NotNull Set otherCheckersSameType, @NotNull Set otherCheckers) { + MethodCheckerDetailsDialog(@Nullable ConditionChecker previouslySelectedChecker, + @NotNull ConditionChecker.Type type, + @NotNull Project project, + @NotNull Component component, + @NotNull Set otherCheckersSameType, + @NotNull Set otherCheckers) { super(component, true); - if (!isSupported(type)) - throw new IllegalArgumentException("Type is invalid " + type); + if (!isSupported(type)) throw new IllegalArgumentException("Type is invalid " + type); myProject = project; myType = type; myOtherCheckers = new HashSet(otherCheckersSameType); myOtherCheckers.addAll(otherCheckers); myPreviouslySelectedChecker = previouslySelectedChecker; - if (myPreviouslySelectedChecker != null) - myOtherCheckers.remove(myPreviouslySelectedChecker); + 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(); + 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); @@ -94,21 +116,42 @@ class MethodCheckerDetailsDialog extends DialogWrapper implements PropertyChange type == ASSERT_TRUE_METHOD || type == ASSERT_FALSE_METHOD; } - private String initTitle(@NotNull ConditionChecker.Type type) { - if (type.equals(IS_NULL_METHOD)) + private static List buildParameterClassListFromPsiMethod(PsiMethod psiMethod) { + List parameterClasses = new ArrayList(); + PsiParameter[] parameters = psiMethod.getParameterList().getParameters(); + for (PsiParameter param : parameters) { + PsiTypeElement typeElement = param.getTypeElement(); + if (typeElement == null) return new ArrayList(); + + 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)) + } + 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)) + } + 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)) + } + 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)) + } + 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)) + } + else if (type.equals(ASSERT_FALSE_METHOD)) { return InspectionsBundle.message("configure.checker.option.assert.false.add.method.checker.dialog.title"); - else + } + else { throw new IllegalArgumentException("MethodCheckerDetailsDialog does not support type " + type); + } } @Override @@ -135,29 +178,31 @@ class MethodCheckerDetailsDialog extends DialogWrapper implements PropertyChange return panel; } - MethodConditionCheck getMethodConditionChecker() { + @Nullable + ConditionChecker getConditionChecker() { return mySelectedChecker; } - private MethodConditionCheck buildMethodConditionChecker() { + @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 MethodConditionCheck(psiMethod, psiParameter, myType); - } else { + return new ConditionChecker.FromPsiBuilder(psiMethod, psiParameter, myType).build(); + } + else { return null; } } - private boolean overlaps() { - MethodConditionCheck thisChecker = buildMethodConditionChecker(); + 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.toString(), - InspectionsBundle.message("configure.checker.option.overlap.error.title"), - Messages.getErrorIcon()); + 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; } } @@ -166,9 +211,7 @@ class MethodCheckerDetailsDialog extends DialogWrapper implements PropertyChange @Override public void propertyChange(PropertyChangeEvent evt) { -// if (evt.getPropertyName().equals(ClassField.PROPERTY_PSICLASS)) { checkOkActionEnable(); -// } } @Override @@ -177,23 +220,25 @@ class MethodCheckerDetailsDialog extends DialogWrapper implements PropertyChange } private void checkOkActionEnable() { - if (classField.getPsiClass() == null || methodDropDown.getSelectedPsiMethod() == null || parameterDropDown.getSelectedPsiParameter() == null) { + if (classField.getPsiClass() == null || + methodDropDown.getSelectedPsiMethod() == null || + parameterDropDown.getSelectedPsiParameter() == null) { setOKActionEnabled(false); - } else { + } + else { setOKActionEnabled(true); } } @Override protected void doOKAction() { - if (!overlaps()) { - MethodConditionCheck checker = buildMethodConditionChecker(); - if (checker != null) { - if (checker.equals(myPreviouslySelectedChecker)) { - mySelectedChecker = myPreviouslySelectedChecker; - } else { - mySelectedChecker = checker; - } + ConditionChecker checker = buildConditionChecker(); + if (checker != null && !overlaps(checker)) { + if (checker.equals(myPreviouslySelectedChecker)) { + mySelectedChecker = myPreviouslySelectedChecker; + } + else { + mySelectedChecker = checker; } super.doOKAction(); } @@ -204,35 +249,47 @@ class MethodCheckerDetailsDialog extends DialogWrapper implements PropertyChange PsiClass psiClass = classField.getPsiClass(); PsiMethod psiMethod = methodDropDown.getSelectedPsiMethod(); PsiParameter psiParameter = parameterDropDown.getSelectedPsiParameter(); - if (psiClass == null || psiMethod == null || psiParameter == null) + if (psiClass == null || psiMethod == null || psiParameter == null) { return false; - else + } + 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 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) + 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", new EverythingGlobalScope(myProject), new ClassFilter() { + .createNoInnerClassesScopeChooser("Choose Class", GlobalSearchScope.allScope(myProject), new ClassFilter() { @Override public boolean isAccepted(PsiClass aClass) { return !aClass.isAnnotationType(); @@ -240,8 +297,9 @@ class MethodCheckerDetailsDialog extends DialogWrapper implements PropertyChange }, null); chooser.showDialog(); PsiClass psiClass = chooser.getSelected(); - if (psiClass != null) + if (psiClass != null) { //noinspection ConstantConditions setText(chooser.getSelected().getQualifiedName()); + } } @Nullable @@ -258,7 +316,7 @@ class MethodCheckerDetailsDialog extends DialogWrapper implements PropertyChange String className = event.getDocument().getText(); PsiClass psiClass = null; if (className != null) { - psiClass = JavaPsiFacade.getInstance(myProject).findClass(className, new EverythingGlobalScope(myProject)); + psiClass = JavaPsiFacade.getInstance(myProject).findClass(className, GlobalSearchScope.allScope(myProject)); } if (psiClass != null && myPsiClass != null) { @@ -266,38 +324,33 @@ class MethodCheckerDetailsDialog extends DialogWrapper implements PropertyChange firePropertyChange(PROPERTY_PSICLASS, myPsiClass, psiClass); myPsiClass = psiClass; } - } else if (psiClass != null) { + } + else if (psiClass != null) { firePropertyChange(PROPERTY_PSICLASS, myPsiClass, psiClass); myPsiClass = psiClass; - } else if (myPsiClass != null) { + } + 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 myModel; + private @Nullable PsiClass myPsiClass; - MethodDropDown(@Nullable PsiClass psiClass, @Nullable PsiMethod psiMethod, @NotNull ConditionChecker.Type type, SortedComboBoxModel model) { + MethodDropDown(@Nullable PsiClass psiClass, + @Nullable PsiMethod psiMethod, + @NotNull ConditionChecker.Type type, + @NotNull SortedComboBoxModel model) { super(model); - if (!isSupported(type)) - throw new IllegalArgumentException("Type is invalid " + type); + if (!isSupported(type)) throw new IllegalArgumentException("Type is invalid " + type); myPsiClass = psiClass; myType = type; @@ -314,14 +367,37 @@ class MethodCheckerDetailsDialog extends DialogWrapper implements PropertyChange } } + 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 buildModel() { + return new SortedComboBoxModel(new Comparator() { + @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 allMethod : allMethods) { - if (qualifies(allMethod)) - myModel.add(new MethodWrapper(allMethod)); + for (PsiMethod method : allMethods) { + MethodWrapper methodWrapper = new MethodWrapper(method); + if (qualifies(method) && !myModel.getItems().contains(methodWrapper)) myModel.add(methodWrapper); } } } @@ -341,10 +417,10 @@ class MethodCheckerDetailsDialog extends DialogWrapper implements PropertyChange if (returnType != PsiType.BOOLEAN && (returnType == null || !returnType.getCanonicalText().equals(Boolean.class.toString()))) { return false; } - } else if (myType == ASSERT_TRUE_METHOD || myType == ASSERT_FALSE_METHOD) { + } + 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]; + for (PsiParameter psiParameter : parameters) { PsiType type = psiParameter.getType(); if (type.equals(PsiType.BOOLEAN) || type.getCanonicalText().equals(Boolean.class.toString())) { booleanParamExists = true; @@ -362,24 +438,19 @@ class MethodCheckerDetailsDialog extends DialogWrapper implements PropertyChange 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; - } - + /** + * 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 { + } + else { setEnabled(true); if (myPsiClass == null || !myPsiClass.equals(evt.getNewValue())) { // ClassChanged so refresh list - myPsiClass = (PsiClass) evt.getNewValue(); + myPsiClass = (PsiClass)evt.getNewValue(); initValues(); } } @@ -393,19 +464,10 @@ class MethodCheckerDetailsDialog extends DialogWrapper implements PropertyChange myPsiClass = null; } - public static SortedComboBoxModel buildModel() { - return new SortedComboBoxModel(new Comparator() { - @Override - public int compare(MethodWrapper o1, MethodWrapper o2) { - return o1.compareTo(o2); - } - }); - } - + @Nullable public PsiMethod getSelectedPsiMethod() { MethodWrapper methodWrapper = myModel.getSelectedItem(); - if (methodWrapper == null) - return null; + if (methodWrapper == null) return null; return methodWrapper.getPsiMethod(); } @@ -415,16 +477,17 @@ class MethodCheckerDetailsDialog extends DialogWrapper implements PropertyChange * Drop Down for picking Parameter Name */ static class ParameterDropDown extends JComboBox implements PropertyChangeListener, ItemListener { - private @Nullable PsiMethod myPsiMethod; private final @NotNull SortedComboBoxModel myModel; private final @NotNull ConditionChecker.Type myType; - public static final String PROPERTY_PARAMETERDROPDOWN = "ParameterDropDown.myModel"; + private @Nullable PsiMethod myPsiMethod; - public ParameterDropDown(@Nullable PsiMethod psiMethod, @Nullable PsiParameter psiParameter, @NotNull SortedComboBoxModel model, @NotNull ConditionChecker.Type type) { + public ParameterDropDown(@Nullable PsiMethod psiMethod, + @Nullable PsiParameter psiParameter, + @NotNull SortedComboBoxModel model, + @NotNull ConditionChecker.Type type) { super(model); - if (!isSupported(type)) - throw new IllegalArgumentException("Type is invalid " + type); + if (!isSupported(type)) throw new IllegalArgumentException("Type is invalid " + type); myPsiMethod = psiMethod; myModel = model; @@ -435,35 +498,16 @@ class MethodCheckerDetailsDialog extends DialogWrapper implements PropertyChange 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); + ParameterWrapper wrapper = (ParameterWrapper)iterator.next(); + if (wrapper.getPsiParameter().equals(psiParameter)) setSelectedItem(wrapper); } } - } else { + } + else { setEnabled(false); } } - List getParameterWrappers() { - List wrappers = new ArrayList(); - 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 buildModel() { return new SortedComboBoxModel(new Comparator() { @Override @@ -473,10 +517,31 @@ class MethodCheckerDetailsDialog extends DialogWrapper implements PropertyChange }); } + List getParameterWrappers() { + List wrappers = new ArrayList(); + 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(); + MethodDropDown methodDropDown = (MethodDropDown)e.getSource(); if (methodDropDown.getSelectedPsiMethod() != null) { setEnabled(true); if (myPsiMethod == null || !myPsiMethod.equals(methodDropDown.getSelectedPsiMethod())) { @@ -485,13 +550,15 @@ class MethodCheckerDetailsDialog extends DialogWrapper implements PropertyChange myModel.addAll(getParameterWrappers()); myModel.setSelectedItem(null); } - } else { + } + else { myPsiMethod = null; myModel.clear(); myModel.setSelectedItem(null); setEnabled(false); } - } else { + } + else { throw new RuntimeException("Unexpected Configuration ParameterDropDown is only expected to receive events from MethodDropDown."); } } @@ -505,10 +572,10 @@ class MethodCheckerDetailsDialog extends DialogWrapper implements PropertyChange } } + @Nullable public PsiParameter getSelectedPsiParameter() { ParameterWrapper parameterWrapper = myModel.getSelectedItem(); - if (parameterWrapper == null) - return null; + if (parameterWrapper == null) return null; return parameterWrapper.getPsiParameter(); } @@ -525,10 +592,12 @@ class MethodCheckerDetailsDialog extends DialogWrapper implements PropertyChange PsiTypeElement typeElement = psiParameter.getTypeElement(); if (typeElement == null) { typeName = ""; - } else { + } + else { if (typeElement.getType() instanceof PsiPrimitiveType) { - typeName = ((PsiPrimitiveType) typeElement.getType()).getBoxedTypeName(); - } else { + typeName = ((PsiPrimitiveType)typeElement.getType()).getBoxedTypeName(); + } + else { typeName = typeElement.getType().getCanonicalText(); } } @@ -560,39 +629,40 @@ class MethodCheckerDetailsDialog extends DialogWrapper implements PropertyChange MethodWrapper(@NotNull PsiMethod psiMethod) { this.myPsiMethod = psiMethod; - List parameters = new ArrayList(); - for (int i = 0; i < psiMethod.getParameterList().getParameters().length; i++) { - PsiParameter psiParameter = psiMethod.getParameterList().getParameters()[i]; - parameters.add(getParameterQualifiedName(psiParameter)); + List parameterClassNames = new ArrayList(); + PsiParameter[] parameters = psiMethod.getParameterList().getParameters(); + for (PsiParameter psiParameter : parameters) { + parameterClassNames.add(getParameterQualifiedName(psiParameter)); } - myId = initId(psiMethod.getName(), parameters); + myId = initId(psiMethod.getName(), parameterClassNames); } - private String getParameterQualifiedName(PsiParameter psiParameter) { + 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 ((PsiPrimitiveType)typeElement.getType()).getBoxedTypeName(); } return typeElement.getType().getCanonicalText(); } - private String initId(String methodName, List parameterNames) { + private static String initId(String methodName, List parameterNames) { String shortName = methodName + "("; for (String parameterName : parameterNames) { - if (parameterNames.lastIndexOf("") > -1) - shortName += parameterName.substring(parameterName.lastIndexOf("") + 1) + ", "; - else + if (parameterNames.lastIndexOf(".") > -1) { + shortName += parameterName.substring(parameterName.lastIndexOf(".") + 1) + ", "; + } + else { shortName += parameterName + ", "; + } } - if (parameterNames.size() > 0) - shortName = shortName.substring(0, shortName.lastIndexOf(", ")); + if (parameterNames.size() > 0) shortName = shortName.substring(0, shortName.lastIndexOf(", ")); shortName += ")"; return shortName; @@ -613,6 +683,23 @@ class MethodCheckerDetailsDialog extends DialogWrapper implements PropertyChange 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); diff --git a/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTest.java b/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTest.java index fb8f28866961..82cfd3eec562 100644 --- a/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTest.java @@ -16,16 +16,14 @@ 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.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; @@ -167,47 +165,48 @@ public class DataFlowInspectionTest extends LightCodeInsightFixtureTestCase { 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;} }")); + 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( - 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;} }")); + 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( - buildMethodConditionCheck("Assertions", "assertTrue", ConditionChecker.Type.ASSERT_TRUE_METHOD, - "public class Assertions { public static boolean assertTrue(boolean b) {if(!b) throw new Exception();} }")); + 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( - buildMethodConditionCheck("Assertions", "assertFalse", ConditionChecker.Type.ASSERT_FALSE_METHOD, - "public class Assertions { public static boolean assertFalse(boolean b) {if(b) throw new Exception();} }")); + 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( - buildMethodConditionCheck("Assertions", "assertIsNull", ConditionChecker.Type.ASSERT_IS_NULL_METHOD, - "public class Assertions { public static boolean assertIsNull(Object o) {if(o != null) throw new Exception();} }")); + 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( - 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();} }")); + 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(); } - private MethodConditionCheck buildMethodConditionCheck(String className, String methodName, ConditionChecker.Type type, String classText) + @Nullable + private ConditionChecker buildConditionChecker(String className, String methodName, ConditionChecker.Type type, String classText) throws IOException { myFixture.addClass(classText); PsiClass psiClass = myFixture.findClass(className); @@ -219,6 +218,7 @@ public class DataFlowInspectionTest extends LightCodeInsightFixtureTestCase { break; } } - return new MethodConditionCheck(psiMethod, psiMethod.getParameterList().getParameters()[0], type); + assert psiMethod != null; + return new ConditionChecker.FromPsiBuilder(psiMethod, psiMethod.getParameterList().getParameters()[0], type).build(); } }