public static boolean proveArrayTypeDistinct(Project project, PsiArrayType type, PsiType bound) { final JavaPsiFacade facade = JavaPsiFacade.getInstance(project); final GlobalSearchScope searchScope = GlobalSearchScope.allScope(project); final Set<PsiClass> possibleClasses = new HashSet<PsiClass>(); possibleClasses.add(facade.findClass(CommonClassNames.JAVA_IO_SERIALIZABLE, searchScope)); possibleClasses.add(facade.findClass(CommonClassNames.JAVA_LANG_CLONEABLE, searchScope)); possibleClasses.add(facade.findClass(CommonClassNames.JAVA_LANG_OBJECT, searchScope)); if (type.getArrayDimensions() == bound.getArrayDimensions()) { final PsiType componentType = type.getComponentType(); final PsiType boundComponentType = ((PsiArrayType) bound).getComponentType(); if (boundComponentType instanceof PsiClassType && componentType instanceof PsiClassType) { return proveExtendsBoundsDistinct( boundComponentType, componentType, ((PsiClassType) boundComponentType).resolve(), ((PsiClassType) componentType).resolve()); } else { return !bound.equals(type); } } else if (bound.getArrayDimensions() + 1 == type.getArrayDimensions() && bound.getDeepComponentType() instanceof PsiClassType) { return !possibleClasses.contains(((PsiClassType) bound.getDeepComponentType()).resolve()); } else if (bound.getArrayDimensions() == type.getArrayDimensions() + 1 && type.getDeepComponentType() instanceof PsiClassType) { return !possibleClasses.contains(((PsiClassType) type.getDeepComponentType()).resolve()); } else if (bound instanceof PsiClassType) { return !possibleClasses.contains(((PsiClassType) bound).resolve()); } else if (bound instanceof PsiWildcardType) { final PsiType boundBound = ((PsiWildcardType) bound).getBound(); return boundBound != null && !boundBound.equals(type); } return true; }
private void checkExpression(PsiExpression expression) { if (expression.getParent() instanceof PsiParenthesizedExpression) { return; } final PsiType expressionType = expression.getType(); if (expressionType == null) { return; } if (expressionType.getArrayDimensions() > 0) { // a horrible hack to get around what happens when you pass // an array to a vararg expression return; } if (TypeConversionUtil.isPrimitiveAndNotNull(expressionType)) { return; } final PsiPrimitiveType unboxedType = PsiPrimitiveType.getUnboxedType(expressionType); if (unboxedType == null) { return; } final PsiType expectedType = ExpectedTypeUtils.findExpectedType(expression, false); if (expectedType == null) { return; } if (!TypeConversionUtil.isPrimitiveAndNotNull(expectedType)) { return; } if (!expectedType.isAssignableFrom(unboxedType)) { return; } registerError(expression, expression); }
private static void generateNamesForArrayType( PsiType type, Set<String> possibleNames, NameValidator validator) { int arrayDim = type.getArrayDimensions(); if (arrayDim == 0) return; PsiType deepType = type.getDeepComponentType(); String candidateName = cleanTypeName(deepType.getPresentableText()); if (deepType instanceof PsiClassType) { PsiClass clazz = ((PsiClassType) deepType).resolve(); if (clazz == null) return; candidateName = GroovyNamesUtil.fromLowerLetter(clazz.getName()); } candidateName = StringUtil.pluralize(GroovyNamesUtil.fromLowerLetter(candidateName)); generateCamelNames(possibleNames, validator, candidateName); ArrayList<String> camelizedName = GroovyNamesUtil.camelizeString(candidateName); candidateName = camelizedName.get(camelizedName.size() - 1); candidateName = "arrayOf" + fromUpperLetter(candidateName); possibleNames.add(validator.validateName(candidateName, true)); }
@NotNull private DfaValue getMethodResultValue( MethodCallInstruction instruction, @Nullable DfaValue qualifierValue, DfaValueFactory factory) { DfaValue precalculated = instruction.getPrecalculatedReturnValue(); if (precalculated != null) { return precalculated; } final PsiType type = instruction.getResultType(); final MethodCallInstruction.MethodType methodType = instruction.getMethodType(); if (methodType == MethodCallInstruction.MethodType.UNBOXING) { return factory.getBoxedFactory().createUnboxed(qualifierValue); } if (methodType == MethodCallInstruction.MethodType.BOXING) { DfaValue boxed = factory.getBoxedFactory().createBoxed(qualifierValue); return boxed == null ? factory.createTypeValue(type, Nullness.NOT_NULL) : boxed; } if (methodType == MethodCallInstruction.MethodType.CAST) { assert qualifierValue != null; if (qualifierValue instanceof DfaConstValue) { Object casted = TypeConversionUtil.computeCastTo(((DfaConstValue) qualifierValue).getValue(), type); return factory .getConstFactory() .createFromValue(casted, type, ((DfaConstValue) qualifierValue).getConstant()); } return qualifierValue; } if (type != null && (type instanceof PsiClassType || type.getArrayDimensions() > 0)) { Nullness nullability = myReturnTypeNullability.get(instruction); if (nullability == Nullness.UNKNOWN && factory.isUnknownMembersAreNullable()) { nullability = Nullness.NULLABLE; } return factory.createTypeValue(type, nullability); } return DfaUnknownValue.getInstance(); }
@Override public void visitLiteralExpression(@NotNull PsiLiteralExpression value) { super.visitLiteralExpression(value); final String text = value.getText(); if (!PsiKeyword.NULL.equals(text)) { return; } PsiElement parent = value.getParent(); while (parent instanceof PsiParenthesizedExpression || parent instanceof PsiConditionalExpression || parent instanceof PsiTypeCastExpression) { parent = parent.getParent(); } if (parent == null || !(parent instanceof PsiReturnStatement)) { return; } final PsiMethod method = PsiTreeUtil.getParentOfType(value, PsiMethod.class); if (method == null) { return; } final PsiType returnType = method.getReturnType(); if (returnType == null) { return; } final boolean isArray = returnType.getArrayDimensions() > 0; if (AnnotationUtil.isAnnotated(method, AnnotationUtil.NULLABLE, false)) { return; } if (m_reportCollectionMethods && CollectionUtils.isCollectionClassOrInterface(returnType)) { registerError(value, value); } else if (m_reportArrayMethods && isArray) { registerError(value, value); } else if (m_reportObjectMethods && !isArray) { registerError(value, value); } }
private boolean checkCondition( @Nullable PsiExpression condition, @NotNull PsiStatement context, List<PsiExpression> notUpdated) { if (condition == null) { return false; } if (PsiUtil.isConstantExpression(condition) || PsiKeyword.NULL.equals(condition.getText())) { return true; } if (condition instanceof PsiInstanceOfExpression) { final PsiInstanceOfExpression instanceOfExpression = (PsiInstanceOfExpression) condition; final PsiExpression operand = instanceOfExpression.getOperand(); return checkCondition(operand, context, notUpdated); } else if (condition instanceof PsiParenthesizedExpression) { // catch stuff like "while ((x)) { ... }" final PsiExpression expression = ((PsiParenthesizedExpression) condition).getExpression(); return checkCondition(expression, context, notUpdated); } else if (condition instanceof PsiPolyadicExpression) { // while (value != x) { ... } // while (value != (x + y)) { ... } // while (b1 && b2) { ... } final PsiPolyadicExpression polyadicExpression = (PsiPolyadicExpression) condition; for (PsiExpression operand : polyadicExpression.getOperands()) { if (!checkCondition(operand, context, notUpdated)) { return false; } } return true; } else if (condition instanceof PsiReferenceExpression) { final PsiReferenceExpression referenceExpression = (PsiReferenceExpression) condition; final PsiElement element = referenceExpression.resolve(); if (element instanceof PsiField) { final PsiField field = (PsiField) element; final PsiType type = field.getType(); if (field.hasModifierProperty(PsiModifier.FINAL) && type.getArrayDimensions() == 0) { if (field.hasModifierProperty(PsiModifier.STATIC)) { return true; } final PsiExpression qualifier = referenceExpression.getQualifierExpression(); if (qualifier == null) { return true; } else if (checkCondition(qualifier, context, notUpdated)) { return true; } } } else if (element instanceof PsiVariable) { final PsiVariable variable = (PsiVariable) element; if (variable.hasModifierProperty(PsiModifier.FINAL)) { // final variables cannot be updated, don't bother to // flag them return true; } else if (element instanceof PsiLocalVariable || element instanceof PsiParameter) { if (!VariableAccessUtils.variableIsAssigned(variable, context)) { notUpdated.add(referenceExpression); return true; } } } } else if (condition instanceof PsiPrefixExpression) { final PsiPrefixExpression prefixExpression = (PsiPrefixExpression) condition; final IElementType tokenType = prefixExpression.getOperationTokenType(); if (JavaTokenType.EXCL.equals(tokenType) || JavaTokenType.PLUS.equals(tokenType) || JavaTokenType.MINUS.equals(tokenType)) { final PsiExpression operand = prefixExpression.getOperand(); return checkCondition(operand, context, notUpdated); } } else if (condition instanceof PsiArrayAccessExpression) { // Actually the contents of the array could change nevertheless // if it is accessed through a different reference like this: // int[] local_ints = new int[]{1, 2}; // int[] other_ints = local_ints; // while (local_ints[0] > 0) { other_ints[0]--; } // // Keep this check? final PsiArrayAccessExpression accessExpression = (PsiArrayAccessExpression) condition; final PsiExpression indexExpression = accessExpression.getIndexExpression(); return checkCondition(indexExpression, context, notUpdated) && checkCondition(accessExpression.getArrayExpression(), context, notUpdated); } else if (condition instanceof PsiConditionalExpression) { final PsiConditionalExpression conditionalExpression = (PsiConditionalExpression) condition; final PsiExpression thenExpression = conditionalExpression.getThenExpression(); final PsiExpression elseExpression = conditionalExpression.getElseExpression(); if (thenExpression == null || elseExpression == null) { return false; } return checkCondition(conditionalExpression.getCondition(), context, notUpdated) && checkCondition(thenExpression, context, notUpdated) && checkCondition(elseExpression, context, notUpdated); } else if (condition instanceof PsiThisExpression) { return true; } else if (condition instanceof PsiMethodCallExpression && !ignoreIterators) { final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression) condition; if (!IteratorUtils.isCallToHasNext(methodCallExpression)) { return false; } final PsiReferenceExpression methodExpression = methodCallExpression.getMethodExpression(); final PsiExpression qualifierExpression = methodExpression.getQualifierExpression(); if (qualifierExpression instanceof PsiReferenceExpression) { final PsiReferenceExpression referenceExpression = (PsiReferenceExpression) qualifierExpression; final PsiElement element = referenceExpression.resolve(); if (!(element instanceof PsiVariable)) { return false; } final PsiVariable variable = (PsiVariable) element; if (!IteratorUtils.containsCallToScannerNext(context, variable, true)) { notUpdated.add(qualifierExpression); return true; } } else { if (!IteratorUtils.containsCallToScannerNext(context, null, true)) { notUpdated.add(methodCallExpression); return true; } } } return false; }
@Override public void visitReferenceExpression(PsiReferenceExpression expression) { if (myWritten && myRead) { return; } super.visitReferenceExpression(expression); final PsiElement target = expression.resolve(); if (target != myVariable) { return; } if (PsiUtil.isAccessedForWriting(expression)) { final PsiElement parent = PsiTreeUtil.skipParentsOfType(expression, PsiParenthesizedExpression.class); if (parent instanceof PsiAssignmentExpression) { final PsiAssignmentExpression assignmentExpression = (PsiAssignmentExpression) parent; final PsiExpression rhs = assignmentExpression.getRExpression(); if (isComplexArrayExpression(rhs)) { myWritten = true; myRead = true; } else if (!isSimpleArrayExpression(rhs)) { myWritten = true; } } return; } myIsReferenced = true; PsiElement parent = getParent(expression); if (parent instanceof PsiArrayAccessExpression) { PsiArrayAccessExpression arrayAccessExpression = (PsiArrayAccessExpression) parent; parent = getParent(parent); while (parent instanceof PsiArrayAccessExpression) { arrayAccessExpression = (PsiArrayAccessExpression) parent; parent = getParent(parent); } final PsiType type = arrayAccessExpression.getType(); if (type != null) { final int dimensions = type.getArrayDimensions(); if (dimensions > 0 && dimensions != myVariable.getType().getArrayDimensions()) { myWritten = true; } } if (PsiUtil.isAccessedForWriting(arrayAccessExpression)) { myWritten = true; } if (PsiUtil.isAccessedForReading(arrayAccessExpression)) { myRead = true; } } else if (parent instanceof PsiReferenceExpression) { final PsiReferenceExpression referenceExpression = (PsiReferenceExpression) parent; final String name = referenceExpression.getReferenceName(); if ("length".equals(name) || ("clone".equals(name) && referenceExpression.getParent() instanceof PsiMethodCallExpression)) { myRead = true; } } else if (parent instanceof PsiForeachStatement) { final PsiForeachStatement foreachStatement = (PsiForeachStatement) parent; final PsiExpression iteratedValue = foreachStatement.getIteratedValue(); if (PsiTreeUtil.isAncestor(iteratedValue, expression, false)) { myRead = true; } } else if (parent instanceof PsiExpressionList) { final PsiExpressionList expressionList = (PsiExpressionList) parent; parent = parent.getParent(); if (parent instanceof PsiMethodCallExpression) { final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression) parent; final PsiMethod method = methodCallExpression.resolveMethod(); if (method != null) { final PsiClass aClass = method.getContainingClass(); if (aClass != null) { final String methodName = method.getName(); final String qualifiedName = aClass.getQualifiedName(); if ("java.lang.System".equals(qualifiedName)) { if ("arraycopy".equals(methodName)) { final PsiExpression[] expressions = expressionList.getExpressions(); if (expressions.length == 5) { if (PsiTreeUtil.isAncestor(expressions[0], expression, false)) { myRead = true; return; } else if (PsiTreeUtil.isAncestor(expressions[2], expression, false)) { myWritten = true; return; } } } } else if (CommonClassNames.JAVA_UTIL_ARRAYS.equals(qualifiedName)) { if ("fill".equals(methodName) || "parallelPrefix".equals(methodName) || "parallelSetAll".equals(methodName) || "parallelSort".equals(methodName) || "setAll".equals(methodName) || "sort".equals(methodName)) { myWritten = true; } else { myRead = true; } return; } } } } myRead = true; myWritten = true; } else { myWritten = true; myRead = true; } }
private static void changeNewOperatorType( PsiNewExpression originalExpression, PsiType toType, final Editor editor) throws IncorrectOperationException { PsiNewExpression newExpression; PsiElementFactory factory = JavaPsiFacade.getInstance(originalExpression.getProject()).getElementFactory(); int caretOffset; TextRange selection; if (toType instanceof PsiArrayType) { final PsiExpression[] originalExpressionArrayDimensions = originalExpression.getArrayDimensions(); caretOffset = 0; @NonNls String text = "new " + toType.getDeepComponentType().getCanonicalText() + "["; if (originalExpressionArrayDimensions.length > 0) { text += originalExpressionArrayDimensions[0].getText(); } else { text += "0"; caretOffset = -2; } text += "]"; for (int i = 1; i < toType.getArrayDimensions(); i++) { text += "["; String arrayDimension = ""; if (originalExpressionArrayDimensions.length > i) { arrayDimension = originalExpressionArrayDimensions[i].getText(); text += arrayDimension; } text += "]"; if (caretOffset < 0) { caretOffset -= arrayDimension.length() + 2; } } newExpression = (PsiNewExpression) factory.createExpressionFromText(text, originalExpression); if (caretOffset < 0) { selection = new TextRange(caretOffset, caretOffset + 1); } else { selection = null; } } else { final PsiAnonymousClass anonymousClass = originalExpression.getAnonymousClass(); newExpression = (PsiNewExpression) factory.createExpressionFromText( "new " + toType.getCanonicalText() + "()" + (anonymousClass != null ? "{}" : ""), originalExpression); PsiExpressionList argumentList = originalExpression.getArgumentList(); if (argumentList == null) return; newExpression.getArgumentList().replace(argumentList); if (anonymousClass == null) { // just to prevent useless inference if (PsiDiamondTypeUtil.canCollapseToDiamond(newExpression, originalExpression, toType)) { final PsiElement paramList = PsiDiamondTypeUtil.replaceExplicitWithDiamond( newExpression.getClassOrAnonymousClassReference().getParameterList()); newExpression = PsiTreeUtil.getParentOfType(paramList, PsiNewExpression.class); } } if (anonymousClass != null) { final PsiAnonymousClass newAnonymousClass = (PsiAnonymousClass) newExpression.getAnonymousClass().replace(anonymousClass); final PsiClass aClass = PsiUtil.resolveClassInType(toType); assert aClass != null; newAnonymousClass .getBaseClassReference() .replace(factory.createClassReferenceElement(aClass)); } selection = null; caretOffset = -1; } PsiElement element = originalExpression.replace(newExpression); editor.getCaretModel().moveToOffset(element.getTextRange().getEndOffset() + caretOffset); editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE); if (selection != null) { selection = selection.shiftRight(element.getTextRange().getEndOffset()); editor.getSelectionModel().setSelection(selection.getStartOffset(), selection.getEndOffset()); } }
public static void processSubTypes( PsiType psiType, final PsiElement context, boolean getRawSubtypes, @NotNull final PrefixMatcher matcher, Consumer<PsiType> consumer) { int arrayDim = psiType.getArrayDimensions(); psiType = psiType.getDeepComponentType(); if (!(psiType instanceof PsiClassType)) return; final Condition<String> shortNameCondition = new Condition<String>() { @Override public boolean value(String s) { return matcher.prefixMatches(s); } }; final PsiClassType baseType = (PsiClassType) psiType; final PsiClassType.ClassResolveResult baseResult = ApplicationManager.getApplication() .runReadAction( new Computable<PsiClassType.ClassResolveResult>() { @Override public PsiClassType.ClassResolveResult compute() { return JavaCompletionUtil.originalize(baseType).resolveGenerics(); } }); final PsiClass baseClass = baseResult.getElement(); final PsiSubstitutor baseSubstitutor = baseResult.getSubstitutor(); if (baseClass == null) return; final GlobalSearchScope scope = ApplicationManager.getApplication() .runReadAction( new Computable<GlobalSearchScope>() { @Override public GlobalSearchScope compute() { return context.getResolveScope(); } }); final Processor<PsiClass> inheritorsProcessor = createInheritorsProcessor( context, baseType, arrayDim, getRawSubtypes, consumer, baseClass, baseSubstitutor); if (matcher.getPrefix().length() > 2) { AllClassesGetter.processJavaClasses( matcher, context.getProject(), scope, new Processor<PsiClass>() { @Override public boolean process(PsiClass psiClass) { if (psiClass.isInheritor(baseClass, true)) { return inheritorsProcessor.process(psiClass); } return true; } }); } else { final Query<PsiClass> baseQuery = ClassInheritorsSearch.search( new ClassInheritorsSearch.SearchParameters( baseClass, scope, true, false, false, shortNameCondition)); final Query<PsiClass> query = new FilteredQuery<PsiClass>( baseQuery, new Condition<PsiClass>() { @Override public boolean value(final PsiClass psiClass) { return !(psiClass instanceof PsiTypeParameter); } }); query.forEach(inheritorsProcessor); } }
@Nullable public static PsiType getVariableTypeByExpressionType( @Nullable PsiType type, final boolean openCaptured) { if (type == null) return null; if (type instanceof PsiCapturedWildcardType) { type = ((PsiCapturedWildcardType) type).getWildcard(); } PsiType transformed = type.accept( new PsiTypeVisitor<PsiType>() { @Override public PsiType visitArrayType(PsiArrayType arrayType) { PsiType componentType = arrayType.getComponentType(); PsiType type = componentType.accept(this); if (type == componentType) return arrayType; return type.createArrayType(); } @Override public PsiType visitType(PsiType type) { return type; } @Override public PsiType visitWildcardType(final PsiWildcardType wildcardType) { final PsiType bound = wildcardType.getBound(); PsiManager manager = wildcardType.getManager(); if (bound != null) { final PsiType acceptedBound = bound.accept(this); if (acceptedBound instanceof PsiWildcardType) { if (((PsiWildcardType) acceptedBound).isExtends() != wildcardType.isExtends()) return PsiWildcardType.createUnbounded(manager); return acceptedBound; } if (wildcardType.isExtends() && acceptedBound.equalsToText(CommonClassNames.JAVA_LANG_OBJECT)) return PsiWildcardType.createUnbounded(manager); if (acceptedBound.equals(bound)) return wildcardType; return wildcardType.isExtends() ? PsiWildcardType.createExtends(manager, acceptedBound) : PsiWildcardType.createSuper(manager, acceptedBound); } return wildcardType; } @Override public PsiType visitCapturedWildcardType( PsiCapturedWildcardType capturedWildcardType) { return openCaptured ? capturedWildcardType.getWildcard().accept(this) : capturedWildcardType; } @Override public PsiType visitClassType(PsiClassType classType) { PsiClassType.ClassResolveResult resolveResult = classType.resolveGenerics(); PsiClass aClass = resolveResult.getElement(); if (aClass == null) return classType; boolean toExtend = false; PsiSubstitutor substitutor = PsiSubstitutor.EMPTY; for (PsiTypeParameter typeParameter : PsiUtil.typeParametersIterable(aClass)) { PsiType typeArgument = resolveResult.getSubstitutor().substitute(typeParameter); if (typeArgument instanceof PsiCapturedWildcardType) toExtend = true; if (typeArgument instanceof PsiWildcardType && ((PsiWildcardType) typeArgument).getBound() instanceof PsiIntersectionType) { toExtend = true; } PsiType toPut; if (typeArgument == null) { toPut = null; } else { final PsiType accepted = typeArgument.accept(this); if (typeArgument instanceof PsiIntersectionType) { toPut = PsiWildcardType.createExtends(typeParameter.getManager(), accepted); } else { toPut = accepted; } } LOG.assertTrue(toPut == null || toPut.isValid(), toPut); substitutor = substitutor.put(typeParameter, toPut); } final PsiAnnotation[] applicableAnnotations = classType.getApplicableAnnotations(); if (substitutor == PsiSubstitutor.EMPTY && !toExtend && applicableAnnotations.length == 0 && !(aClass instanceof PsiTypeParameter)) return classType; PsiManager manager = aClass.getManager(); PsiType result = JavaPsiFacade.getInstance(manager.getProject()) .getElementFactory() .createType( aClass, substitutor, PsiUtil.getLanguageLevel(aClass), applicableAnnotations); if (toExtend) result = PsiWildcardType.createExtends(manager, result); return result; } }); PsiType componentType = transformed != null ? transformed.getDeepComponentType() : null; if (componentType instanceof PsiWildcardType) { componentType = ((PsiWildcardType) componentType).getExtendsBound(); int dims = transformed.getArrayDimensions(); for (int i = 0; i < dims; i++) componentType = componentType.createArrayType(); return componentType; } return transformed; }