Collection<PsiClass> (@NotNull PsiElement variableOrMethod, boolean useRighthandTypeAsWeakestTypeInAssignments) { final PsiType variableOrMethodType; if (variableOrMethod instanceof PsiVariable variable) { variableOrMethodType = variable.getType(); } else if (variableOrMethod instanceof PsiMethod method) { variableOrMethodType = method.getReturnType(); if (PsiTypes.voidType().equals(variableOrMethodType)) { return Collections.emptyList(); } } else { throw new IllegalArgumentException("PsiMethod or PsiVariable expected: " + variableOrMethod); } final PsiClass variableOrMethodClass = PsiUtil.resolveClassInClassTypeOnly(variableOrMethodType); if (variableOrMethodClass == null || variableOrMethodClass instanceof PsiTypeParameter) { return Collections.emptyList(); } final Set<PsiClass> weakestTypeClasses = new HashSet<>(); final GlobalSearchScope scope = variableOrMethod.getResolveScope(); final JavaPsiFacade facade = JavaPsiFacade.getInstance(variableOrMethod.getProject()); final PsiClass lowerBoundClass; if (variableOrMethod instanceof PsiResourceVariable resourceVariable) { lowerBoundClass = facade.findClass(CommonClassNames.JAVA_LANG_AUTO_CLOSEABLE, scope); if (lowerBoundClass == null || variableOrMethodClass.equals(lowerBoundClass)) { return Collections.emptyList(); } weakestTypeClasses.add(lowerBoundClass); @NonNls final String methodCallText = resourceVariable.getName() + ".close()"; final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)facade.getElementFactory().createExpressionFromText(methodCallText, resourceVariable.getParent()); if (!findWeakestType(methodCallExpression, weakestTypeClasses)) { return Collections.emptyList(); } if (weakestTypeClasses.isEmpty()) { weakestTypeClasses.add(lowerBoundClass); } } else { lowerBoundClass = facade.findClass(CommonClassNames.JAVA_LANG_OBJECT, scope); if (lowerBoundClass == null || variableOrMethodClass.equals(lowerBoundClass)) { return Collections.emptyList(); } weakestTypeClasses.add(lowerBoundClass); } final Query<PsiReference> query = ReferencesSearch.search(variableOrMethod, variableOrMethod.getUseScope()); boolean hasUsages = false; for (PsiReference reference : query) { ProgressManager.checkCanceled(); hasUsages = true; PsiElement referenceElement = reference.getElement(); PsiElement referenceParent = PsiUtil.skipParenthesizedExprUp(referenceElement.getParent()); if (referenceParent instanceof PsiMethodCallExpression) { referenceElement = referenceParent; referenceParent = PsiUtil.skipParenthesizedExprUp(referenceElement.getParent()); } final PsiElement referenceGrandParent = referenceParent.getParent(); if (referenceElement instanceof PsiMethodReferenceExpression methodReferenceExpression) { final PsiType type = methodReferenceExpression.getFunctionalInterfaceType(); final PsiType returnType = LambdaUtil.getFunctionalInterfaceReturnType(type); if (!PsiTypes.voidType().equals(returnType) && !checkType(returnType, weakestTypeClasses)) { return Collections.emptyList(); } } else if (referenceParent instanceof PsiExpressionList) { if (!(referenceGrandParent instanceof PsiMethodCallExpression methodCallExpression)) { return Collections.emptyList(); } if (!findWeakestType(referenceElement, methodCallExpression, weakestTypeClasses)) { return Collections.emptyList(); } } else if (referenceParent instanceof PsiLambdaExpression lambda) { final PsiClassType returnType = ObjectUtils.tryCast(LambdaUtil.getFunctionalInterfaceReturnType(lambda), PsiClassType.class); checkType(returnType, weakestTypeClasses); } else if (referenceGrandParent instanceof PsiMethodCallExpression methodCallExpression) { if (PsiUtil.skipParenthesizedExprUp(methodCallExpression.getParent()) instanceof PsiTypeCastExpression || !findWeakestType(methodCallExpression, weakestTypeClasses)) { return Collections.emptyList(); } } else if (referenceParent instanceof PsiResourceExpression) { final PsiClass closeable = facade.findClass(CommonClassNames.JAVA_LANG_AUTO_CLOSEABLE, scope); checkClass(closeable, weakestTypeClasses); } else if (referenceParent instanceof PsiAssignmentExpression assignmentExpression) { if (!findWeakestType(referenceElement, assignmentExpression, useRighthandTypeAsWeakestTypeInAssignments, weakestTypeClasses)) { return Collections.emptyList(); } } else if (referenceParent instanceof PsiNameValuePair) { final PsiType type = ExpectedTypeUtils.findExpectedType((PsiExpression)referenceElement, true); if (!checkType(type, weakestTypeClasses)) { return Collections.emptyList(); } } else if (referenceParent instanceof PsiVariable variable) { final PsiType type = variable.getType(); if (!type.isAssignableFrom(variableOrMethodType) || !checkType(type, weakestTypeClasses)) { return Collections.emptyList(); } } else if (referenceParent instanceof PsiForeachStatement foreachStatement) { if (!Comparing.equal(foreachStatement.getIteratedValue(), referenceElement)) { return Collections.emptyList(); } final PsiClass javaLangIterableClass = facade.findClass(CommonClassNames.JAVA_LANG_ITERABLE, scope); if (javaLangIterableClass == null) { return Collections.emptyList(); } checkClass(javaLangIterableClass, weakestTypeClasses); } else if (referenceParent instanceof PsiReturnStatement) { final PsiType type = PsiTypesUtil.getMethodReturnType(referenceParent); if (!checkType(type, weakestTypeClasses)) { return Collections.emptyList(); } } else if (referenceParent instanceof PsiReferenceExpression referenceExpression) { // field access, method call is handled above. final PsiElement target = referenceExpression.resolve(); if (!(target instanceof PsiField field)) { return Collections.emptyList(); } final PsiClass containingClass = field.getContainingClass(); checkClass(containingClass, weakestTypeClasses); } else if (referenceParent instanceof PsiArrayInitializerExpression arrayInitializerExpression) { if (!findWeakestType(arrayInitializerExpression, weakestTypeClasses)) { return Collections.emptyList(); } } else if (referenceParent instanceof PsiThrowStatement throwStatement) { if (!findWeakestType(throwStatement, variableOrMethodClass, weakestTypeClasses)) { return Collections.emptyList(); } } else if (referenceParent instanceof PsiConditionalExpression conditionalExpression) { final PsiExpression condition = conditionalExpression.getCondition(); if (referenceElement.equals(condition)) { return Collections.emptyList(); } final PsiType type = ExpectedTypeUtils.findExpectedType(conditionalExpression, true); if (!checkType(type, weakestTypeClasses)) { return Collections.emptyList(); } } else if (referenceParent instanceof PsiPolyadicExpression polyadicExpression) { final PsiType type = polyadicExpression.getType(); if (TypeUtils.isJavaLangString(type)) { if (ExpressionUtils.hasStringType((PsiExpression)referenceElement)) { final PsiExpression[] operands = polyadicExpression.getOperands(); boolean first = true; boolean stringSeen = false; for (PsiExpression operand : operands) { if (operand == reference) { break; } first = false; if (ExpressionUtils.hasStringType(operand)) { stringSeen = true; break; } } if (!stringSeen && (!first || !ExpressionUtils.hasStringType(operands[1]))) { if (!checkType(type, weakestTypeClasses)) { return Collections.emptyList(); } } } } else if (!ComparisonUtils.isEqualityComparison(polyadicExpression)) { return Collections.emptyList(); } } else if (referenceParent instanceof PsiSwitchBlock) { // only enums, Strings and primitives can be a switch expression // pattern matching not yet supported return Collections.emptyList(); } else if (referenceParent instanceof PsiUnaryExpression) { // only primitives and boxed types are the target of a unary // expression return Collections.emptyList(); } else if (referenceParent instanceof PsiIfStatement) { // only booleans and boxed Booleans are the condition of an if // statement return Collections.emptyList(); } else if (referenceParent instanceof PsiForStatement) { // only booleans and boxed Booleans are the condition of an // for statement return Collections.emptyList(); } else if (referenceParent instanceof PsiNewExpression newExpression) { final PsiExpression qualifier = newExpression.getQualifier(); if (qualifier != null) { final PsiClass innerClass = PsiUtil.resolveClassInClassTypeOnly(newExpression.getType()); if (innerClass == null) { return Collections.emptyList(); } final PsiClass outerClass = innerClass.getContainingClass(); if (outerClass != null) { checkClass(outerClass, weakestTypeClasses); } } } if (weakestTypeClasses.contains(variableOrMethodClass) || weakestTypeClasses.isEmpty()) { return Collections.emptyList(); } } if (!hasUsages) { return Collections.emptyList(); } return filterAccessibleClasses(weakestTypeClasses, variableOrMethodClass, variableOrMethod); }