/** * Casting an enclosing instance will considered as useful if removing it would actually bind to a * different type */ public static void checkNeedForEnclosingInstanceCast( BlockScope scope, Expression enclosingInstance, TypeBinding enclosingInstanceType, TypeBinding memberType) { if (scope.compilerOptions().getSeverity(CompilerOptions.UnnecessaryTypeCheck) == ProblemSeverities.Ignore) return; TypeBinding castedExpressionType = ((CastExpression) enclosingInstance).expression.resolvedType; if (castedExpressionType == null) return; // cannot do better // obvious identity cast if (castedExpressionType == enclosingInstanceType) { scope.problemReporter().unnecessaryCast((CastExpression) enclosingInstance); } else if (castedExpressionType == TypeBinding.NULL) { return; // tolerate null enclosing instance cast } else { TypeBinding alternateEnclosingInstanceType = castedExpressionType; if (castedExpressionType.isBaseType() || castedExpressionType.isArrayType()) return; // error case if (memberType == scope.getMemberType( memberType.sourceName(), (ReferenceBinding) alternateEnclosingInstanceType)) { scope.problemReporter().unnecessaryCast((CastExpression) enclosingInstance); } } }
/** * Cast expressions will considered as useful if removing them all would actually bind to a * different method (no fine grain analysis on per casted argument basis, simply separate widening * cast from narrowing ones) */ public static void checkNeedForArgumentCasts( BlockScope scope, Expression receiver, TypeBinding receiverType, MethodBinding binding, Expression[] arguments, TypeBinding[] argumentTypes, final InvocationSite invocationSite) { if (scope.compilerOptions().getSeverity(CompilerOptions.UnnecessaryTypeCheck) == ProblemSeverities.Ignore) return; int length = argumentTypes.length; // iterate over arguments, and retrieve original argument types (before cast) TypeBinding[] rawArgumentTypes = argumentTypes; for (int i = 0; i < length; i++) { Expression argument = arguments[i]; if (argument instanceof CastExpression) { // narrowing conversion on base type may change value, thus necessary if ((argument.bits & ASTNode.UnnecessaryCast) == 0 && argument.resolvedType.isBaseType()) { continue; } TypeBinding castedExpressionType = ((CastExpression) argument).expression.resolvedType; if (castedExpressionType == null) return; // cannot do better // obvious identity cast if (castedExpressionType == argumentTypes[i]) { scope.problemReporter().unnecessaryCast((CastExpression) argument); } else if (castedExpressionType == TypeBinding.NULL) { continue; // tolerate null argument cast } else if ((argument.implicitConversion & TypeIds.BOXING) != 0) { continue; // boxing has a side effect: (int) char is not boxed as simple char } else { if (rawArgumentTypes == argumentTypes) { System.arraycopy( rawArgumentTypes, 0, rawArgumentTypes = new TypeBinding[length], 0, length); } // retain original argument type rawArgumentTypes[i] = castedExpressionType; } } } // perform alternate lookup with original types if (rawArgumentTypes != argumentTypes) { checkAlternateBinding( scope, receiver, receiverType, binding, arguments, argumentTypes, rawArgumentTypes, invocationSite); } }
/** * Complain if assigned expression is cast, but not actually used as such, e.g. Object o = (List) * object; */ public static void checkNeedForAssignedCast( BlockScope scope, TypeBinding expectedType, CastExpression rhs) { if (scope.compilerOptions().getSeverity(CompilerOptions.UnnecessaryTypeCheck) == ProblemSeverities.Ignore) return; TypeBinding castedExpressionType = rhs.expression.resolvedType; // int i = (byte) n; // cast still had side effect // double d = (float) n; // cast to float is unnecessary if (castedExpressionType == null || rhs.resolvedType.isBaseType()) return; // if (castedExpressionType.id == T_null) return; // tolerate null expression cast if (castedExpressionType.isCompatibleWith(expectedType)) { scope.problemReporter().unnecessaryCast(rhs); } }
/** * Complain if cast expression is cast, but not actually needed, int i = (int)(Integer) 12; Note * that this (int) cast is however needed: Integer i = 0; char c = (char)((int) i); */ public static void checkNeedForCastCast(BlockScope scope, CastExpression enclosingCast) { if (scope.compilerOptions().getSeverity(CompilerOptions.UnnecessaryTypeCheck) == ProblemSeverities.Ignore) return; CastExpression nestedCast = (CastExpression) enclosingCast.expression; if ((nestedCast.bits & ASTNode.UnnecessaryCast) == 0) return; // check if could cast directly to enclosing cast type, without intermediate type cast CastExpression alternateCast = new CastExpression(null, enclosingCast.type); alternateCast.resolvedType = enclosingCast.resolvedType; if (!alternateCast.checkCastTypesCompatibility( scope, enclosingCast.resolvedType, nestedCast.expression.resolvedType, null /* no expr to avoid side-effects*/)) return; scope.problemReporter().unnecessaryCast(nestedCast); }
void checkAssignment(BlockScope scope, TypeBinding lhsType, TypeBinding rhsType) { FieldBinding leftField = getLastField(this.lhs); if (leftField != null && rhsType != TypeBinding.NULL && (lhsType.kind() == Binding.WILDCARD_TYPE) && ((WildcardBinding) lhsType).boundKind != Wildcard.SUPER) { scope.problemReporter().wildcardAssignment(lhsType, rhsType, this.expression); } else if (leftField != null && !leftField.isStatic() && leftField.declaringClass != null /*length pseudo field*/ && leftField.declaringClass.isRawType()) { scope.problemReporter().unsafeRawFieldAssignment(leftField, rhsType, this.lhs); } else if (rhsType.needsUncheckedConversion(lhsType)) { scope.problemReporter().unsafeTypeConversion(this.expression, rhsType, lhsType); } }
public TypeBinding resolveType(BlockScope scope) { // due to syntax lhs may be only a NameReference, a FieldReference or an ArrayReference this.constant = Constant.NotAConstant; if (!(this.lhs instanceof Reference) || this.lhs.isThis()) { scope.problemReporter().expressionShouldBeAVariable(this.lhs); return null; } TypeBinding lhsType = lhs.resolveType(scope); this.expression.setExpectedType(lhsType); // needed in case of generic method invocation if (lhsType != null) { this.resolvedType = lhsType.capture(scope, this.sourceEnd); } TypeBinding rhsType = this.expression.resolveType(scope); if (lhsType == null || rhsType == null) { return null; } // check for assignment with no effect Binding left = getDirectBinding(this.lhs); if (left != null && left == getDirectBinding(this.expression)) { scope.problemReporter().assignmentHasNoEffect(this, left.shortReadableName()); } // Compile-time conversion of base-types : implicit narrowing integer into byte/short/character // may require to widen the rhs expression at runtime if (lhsType != rhsType) { // must call before computeConversion() and typeMismatchError() scope.compilationUnitScope().recordTypeConversion(lhsType, rhsType); } if ((this.expression.isConstantValueOfTypeAssignableToType(rhsType, lhsType) || (lhsType.isBaseType() && BaseTypeBinding.isWidening(lhsType.id, rhsType.id))) || rhsType.isCompatibleWith(lhsType)) { this.expression.computeConversion(scope, lhsType, rhsType); checkAssignment(scope, lhsType, rhsType); if (this.expression instanceof CastExpression && (this.expression.bits & ASTNode.UnnecessaryCast) == 0) { CastExpression.checkNeedForAssignedCast(scope, lhsType, (CastExpression) this.expression); } return this.resolvedType; } else if (scope.isBoxingCompatibleWith(rhsType, lhsType) || (rhsType.isBaseType() // narrowing then boxing ? && scope.compilerOptions().sourceLevel >= ClassFileConstants.JDK1_5 // autoboxing && !lhsType.isBaseType() && this.expression.isConstantValueOfTypeAssignableToType( rhsType, scope.environment().computeBoxingType(lhsType)))) { this.expression.computeConversion(scope, lhsType, rhsType); if (this.expression instanceof CastExpression && (this.expression.bits & ASTNode.UnnecessaryCast) == 0) { CastExpression.checkNeedForAssignedCast(scope, lhsType, (CastExpression) this.expression); } return this.resolvedType; } scope.problemReporter().typeMismatchError(rhsType, lhsType, this.expression, this.lhs); return lhsType; }
/** * Only complain for identity cast, since other type of casts may be useful: e.g. ~((~(long) 0) << * 32) is different from: ~((~0) << 32) */ public static void checkNeedForArgumentCast( BlockScope scope, int operator, int operatorSignature, Expression expression, int expressionTypeId) { if (scope.compilerOptions().getSeverity(CompilerOptions.UnnecessaryTypeCheck) == ProblemSeverities.Ignore) return; // check need for left operand cast int alternateLeftTypeId = expressionTypeId; if ((expression.bits & ASTNode.UnnecessaryCast) == 0 && expression.resolvedType.isBaseType()) { // narrowing conversion on base type may change value, thus necessary return; } else { TypeBinding alternateLeftType = ((CastExpression) expression).expression.resolvedType; if (alternateLeftType == null) return; // cannot do better if ((alternateLeftTypeId = alternateLeftType.id) == expressionTypeId) { // obvious identity cast scope.problemReporter().unnecessaryCast((CastExpression) expression); return; } else if (alternateLeftTypeId == TypeIds.T_null) { alternateLeftTypeId = expressionTypeId; // tolerate null argument cast return; } } /* tolerate widening cast in unary expressions, as may be used when combined in binary expressions (41680) int alternateOperatorSignature = OperatorExpression.OperatorSignatures[operator][(alternateLeftTypeId << 4) + alternateLeftTypeId]; // (cast) left Op (cast) right --> result // 1111 0000 1111 0000 1111 // <<16 <<12 <<8 <<4 <<0 final int CompareMASK = (0xF<<16) + (0xF<<8) + 0xF; // mask hiding compile-time types if ((operatorSignature & CompareMASK) == (alternateOperatorSignature & CompareMASK)) { // same promotions and result scope.problemReporter().unnecessaryCastForArgument((CastExpression)expression, TypeBinding.wellKnownType(scope, expression.implicitConversion >> 4)); } */ }
/** * @see * xap.lui.compile.ca.jdt.internal.compiler.ast.Expression#resolveTypeExpecting(xap.lui.compile.ca.jdt.internal.compiler.lookup.BlockScope, * xap.lui.compile.ca.jdt.internal.compiler.lookup.TypeBinding) */ public TypeBinding resolveTypeExpecting(BlockScope scope, TypeBinding expectedType) { TypeBinding type = super.resolveTypeExpecting(scope, expectedType); if (type == null) return null; TypeBinding lhsType = this.resolvedType; TypeBinding rhsType = this.expression.resolvedType; // signal possible accidental boolean assignment (instead of using '==' operator) if (expectedType == TypeBinding.BOOLEAN && lhsType == TypeBinding.BOOLEAN && (this.lhs.bits & IsStrictlyAssigned) != 0) { scope.problemReporter().possibleAccidentalBooleanAssignment(this); } checkAssignment(scope, lhsType, rhsType); return type; }
public TypeBinding resolveType(BlockScope scope) { // compute a new constant if the cast is effective // due to the fact an expression may start with ( and that a cast can also start with ( // the field is an expression....it can be a TypeReference OR a NameReference Or // any kind of Expression <-- this last one is invalid....... this.constant = Constant.NotAConstant; this.implicitConversion = TypeIds.T_undefined; if ((this.type instanceof TypeReference) || (this.type instanceof NameReference) && ((this.type.bits & ASTNode.ParenthesizedMASK) >> ASTNode.ParenthesizedSHIFT) == 0) { // no extra parenthesis around type: ((A))exp boolean exprContainCast = false; TypeBinding castType = this.resolvedType = this.type.resolveType(scope); // expression.setExpectedType(this.resolvedType); // needed in case of generic method // invocation if (this.expression instanceof CastExpression) { this.expression.bits |= ASTNode.DisableUnnecessaryCastCheck; exprContainCast = true; } TypeBinding expressionType = this.expression.resolveType(scope); if (castType != null) { if (expressionType != null) { boolean isLegal = checkCastTypesCompatibility(scope, castType, expressionType, this.expression); if (isLegal) { this.expression.computeConversion(scope, castType, expressionType); if ((this.bits & ASTNode.UnsafeCast) != 0) { // unsafe cast scope.problemReporter().unsafeCast(this, scope); } else { if (castType.isRawType() && scope.compilerOptions().getSeverity(CompilerOptions.RawTypeReference) != ProblemSeverities.Ignore) { scope.problemReporter().rawTypeReference(this.type, castType); } if ((this.bits & (ASTNode.UnnecessaryCast | ASTNode.DisableUnnecessaryCastCheck)) == ASTNode.UnnecessaryCast) { // unnecessary cast if (!isIndirectlyUsed()) // used for generic type inference or boxing ? scope.problemReporter().unnecessaryCast(this); } } } else { // illegal cast if ((castType.tagBits & TagBits.HasMissingType) == 0) { // no complaint if secondary error scope.problemReporter().typeCastError(this, castType, expressionType); } this.bits |= ASTNode.DisableUnnecessaryCastCheck; // disable further secondary diagnosis } } this.resolvedType = castType.capture(scope, this.sourceEnd); if (exprContainCast) { checkNeedForCastCast(scope, this); } } return this.resolvedType; } else { // expression as a cast TypeBinding expressionType = this.expression.resolveType(scope); if (expressionType == null) return null; scope.problemReporter().invalidTypeReference(this.type); return null; } }
private static void checkAlternateBinding( BlockScope scope, Expression receiver, TypeBinding receiverType, MethodBinding binding, Expression[] arguments, TypeBinding[] originalArgumentTypes, TypeBinding[] alternateArgumentTypes, final InvocationSite invocationSite) { InvocationSite fakeInvocationSite = new InvocationSite() { public TypeBinding[] genericTypeArguments() { return null; } public boolean isSuperAccess() { return invocationSite.isSuperAccess(); } public boolean isTypeAccess() { return invocationSite.isTypeAccess(); } public void setActualReceiverType(ReferenceBinding actualReceiverType) { /* ignore */ } public void setDepth(int depth) { /* ignore */ } public void setFieldIndex(int depth) { /* ignore */ } public int sourceStart() { return 0; } public int sourceEnd() { return 0; } }; MethodBinding bindingIfNoCast; if (binding.isConstructor()) { bindingIfNoCast = scope.getConstructor( (ReferenceBinding) receiverType, alternateArgumentTypes, fakeInvocationSite); } else { bindingIfNoCast = receiver.isImplicitThis() ? scope.getImplicitMethod( binding.selector, alternateArgumentTypes, fakeInvocationSite) : scope.getMethod( receiverType, binding.selector, alternateArgumentTypes, fakeInvocationSite); } if (bindingIfNoCast == binding) { int argumentLength = originalArgumentTypes.length; if (binding.isVarargs()) { int paramLength = binding.parameters.length; if (paramLength == argumentLength) { int varargsIndex = paramLength - 1; ArrayBinding varargsType = (ArrayBinding) binding.parameters[varargsIndex]; TypeBinding lastArgType = alternateArgumentTypes[varargsIndex]; // originalType may be compatible already, but cast mandated // to clarify between varargs/non-varargs call if (varargsType.dimensions != lastArgType.dimensions()) { return; } if (lastArgType.isCompatibleWith(varargsType.elementsType()) && lastArgType.isCompatibleWith(varargsType)) { return; } } } for (int i = 0; i < argumentLength; i++) { if (originalArgumentTypes[i] != alternateArgumentTypes[i]) { scope.problemReporter().unnecessaryCast((CastExpression) arguments[i]); } } } }
/** Check binary operator casted arguments */ public static void checkNeedForArgumentCasts( BlockScope scope, int operator, int operatorSignature, Expression left, int leftTypeId, boolean leftIsCast, Expression right, int rightTypeId, boolean rightIsCast) { if (scope.compilerOptions().getSeverity(CompilerOptions.UnnecessaryTypeCheck) == ProblemSeverities.Ignore) return; // check need for left operand cast int alternateLeftTypeId = leftTypeId; if (leftIsCast) { if ((left.bits & ASTNode.UnnecessaryCast) == 0 && left.resolvedType.isBaseType()) { // narrowing conversion on base type may change value, thus necessary leftIsCast = false; } else { TypeBinding alternateLeftType = ((CastExpression) left).expression.resolvedType; if (alternateLeftType == null) return; // cannot do better if ((alternateLeftTypeId = alternateLeftType.id) == leftTypeId) { // obvious identity cast scope.problemReporter().unnecessaryCast((CastExpression) left); leftIsCast = false; } else if (alternateLeftTypeId == TypeIds.T_null) { alternateLeftTypeId = leftTypeId; // tolerate null argument cast leftIsCast = false; } } } // check need for right operand cast int alternateRightTypeId = rightTypeId; if (rightIsCast) { if ((right.bits & ASTNode.UnnecessaryCast) == 0 && right.resolvedType.isBaseType()) { // narrowing conversion on base type may change value, thus necessary rightIsCast = false; } else { TypeBinding alternateRightType = ((CastExpression) right).expression.resolvedType; if (alternateRightType == null) return; // cannot do better if ((alternateRightTypeId = alternateRightType.id) == rightTypeId) { // obvious identity cast scope.problemReporter().unnecessaryCast((CastExpression) right); rightIsCast = false; } else if (alternateRightTypeId == TypeIds.T_null) { alternateRightTypeId = rightTypeId; // tolerate null argument cast rightIsCast = false; } } } if (leftIsCast || rightIsCast) { if (alternateLeftTypeId > 15 || alternateRightTypeId > 15) { // must convert String + Object || Object + String if (alternateLeftTypeId == TypeIds.T_JavaLangString) { alternateRightTypeId = TypeIds.T_JavaLangObject; } else if (alternateRightTypeId == TypeIds.T_JavaLangString) { alternateLeftTypeId = TypeIds.T_JavaLangObject; } else { return; // invalid operator } } int alternateOperatorSignature = OperatorExpression.OperatorSignatures[operator][ (alternateLeftTypeId << 4) + alternateRightTypeId]; // (cast) left Op (cast) right --> result // 1111 0000 1111 0000 1111 // <<16 <<12 <<8 <<4 <<0 final int CompareMASK = (0xF << 16) + (0xF << 8) + 0xF; // mask hiding compile-time types if ((operatorSignature & CompareMASK) == (alternateOperatorSignature & CompareMASK)) { // same promotions and result if (leftIsCast) scope.problemReporter().unnecessaryCast((CastExpression) left); if (rightIsCast) scope.problemReporter().unnecessaryCast((CastExpression) right); } } }