1 /*******************************************************************************
2 * Copyright (c) 2000, 2004 IBM Corporation and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Common Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/cpl-v10.html
9 * IBM Corporation - initial API and implementation
10 * Nick Teryaev - fix for bug (https://bugs.eclipse.org/bugs/show_bug.cgi?id=40752)
11 *******************************************************************************/
12 package org.eclipse.jdt.internal.compiler.ast;
14 import org.eclipse.jdt.internal.compiler.ASTVisitor;
15 import org.eclipse.jdt.internal.compiler.impl.*;
16 import org.eclipse.jdt.internal.compiler.codegen.*;
17 import org.eclipse.jdt.internal.compiler.flow.*;
18 import org.eclipse.jdt.internal.compiler.lookup.*;
19 import org.eclipse.jdt.internal.compiler.problem.ProblemSeverities;
21 public class CastExpression extends Expression {
23 public Expression expression;
24 public Expression type;
25 public TypeBinding expectedType; // when assignment conversion to a given expected type: String s = (String) t;
27 //expression.implicitConversion holds the cast for baseType casting
28 public CastExpression(Expression expression, Expression type) {
29 this.expression = expression;
32 //due to the fact an expression may start with ( and that a cast also start with (
33 //the field is an expression....it can be a TypeReference OR a NameReference Or
34 //an expression <--this last one is invalid.......
36 //if (type instanceof TypeReference )
37 // flag = IsTypeReference ;
39 // if (type instanceof NameReference)
40 // flag = IsNameReference ;
42 // flag = IsExpression ;
46 public FlowInfo analyseCode(
47 BlockScope currentScope,
48 FlowContext flowContext,
52 .analyseCode(currentScope, flowContext, flowInfo)
53 .unconditionalInits();
57 * Casting an enclosing instance will considered as useful if removing it would actually bind to a different type
59 public static void checkNeedForEnclosingInstanceCast(BlockScope scope, Expression enclosingInstance, TypeBinding enclosingInstanceType, TypeBinding memberType) {
61 if (scope.environment().options.getSeverity(CompilerOptions.UnnecessaryTypeCheck) == ProblemSeverities.Ignore) return;
63 TypeBinding castedExpressionType = ((CastExpression)enclosingInstance).expression.resolvedType;
64 if (castedExpressionType == null) return; // cannot do better
65 // obvious identity cast
66 if (castedExpressionType == enclosingInstanceType) {
67 scope.problemReporter().unnecessaryCast((CastExpression)enclosingInstance);
68 } else if (castedExpressionType == NullBinding){
69 return; // tolerate null enclosing instance cast
71 TypeBinding alternateEnclosingInstanceType = castedExpressionType;
72 if (castedExpressionType.isBaseType() || castedExpressionType.isArrayType()) return; // error case
73 if (memberType == scope.getMemberType(memberType.sourceName(), (ReferenceBinding) alternateEnclosingInstanceType)) {
74 scope.problemReporter().unnecessaryCast((CastExpression)enclosingInstance);
80 * Only complain for identity cast, since other type of casts may be useful: e.g. ~((~(long) 0) << 32) is different from: ~((~0) << 32)
82 public static void checkNeedForArgumentCast(BlockScope scope, int operator, int operatorSignature, Expression expression, int expressionTypeId) {
84 if (scope.environment().options.getSeverity(CompilerOptions.UnnecessaryTypeCheck) == ProblemSeverities.Ignore) return;
86 // check need for left operand cast
87 int alternateLeftTypeId = expressionTypeId;
88 if ((expression.bits & UnnecessaryCastMask) == 0 && expression.resolvedType.isBaseType()) {
89 // narrowing conversion on base type may change value, thus necessary
92 TypeBinding alternateLeftType = ((CastExpression)expression).expression.resolvedType;
93 if (alternateLeftType == null) return; // cannot do better
94 if ((alternateLeftTypeId = alternateLeftType.id) == expressionTypeId) { // obvious identity cast
95 scope.problemReporter().unnecessaryCast((CastExpression)expression);
97 } else if (alternateLeftTypeId == T_null) {
98 alternateLeftTypeId = expressionTypeId; // tolerate null argument cast
102 /* tolerate widening cast in unary expressions, as may be used when combined in binary expressions (41680)
103 int alternateOperatorSignature = OperatorExpression.OperatorSignatures[operator][(alternateLeftTypeId << 4) + alternateLeftTypeId];
104 // (cast) left Op (cast) right --> result
105 // 1111 0000 1111 0000 1111
106 // <<16 <<12 <<8 <<4 <<0
107 final int CompareMASK = (0xF<<16) + (0xF<<8) + 0xF; // mask hiding compile-time types
108 if ((operatorSignature & CompareMASK) == (alternateOperatorSignature & CompareMASK)) { // same promotions and result
109 scope.problemReporter().unnecessaryCastForArgument((CastExpression)expression, TypeBinding.wellKnownType(scope, expression.implicitConversion >> 4));
115 * Cast expressions will considered as useful if removing them all would actually bind to a different method
116 * (no fine grain analysis on per casted argument basis, simply separate widening cast from narrowing ones)
118 public static void checkNeedForArgumentCasts(BlockScope scope, Expression receiver, TypeBinding receiverType, MethodBinding binding, Expression[] arguments, TypeBinding[] argumentTypes, final InvocationSite invocationSite) {
120 if (scope.environment().options.getSeverity(CompilerOptions.UnnecessaryTypeCheck) == ProblemSeverities.Ignore) return;
122 int length = argumentTypes.length;
124 // iterate over arguments, and retrieve original argument types (before cast)
125 TypeBinding[] rawArgumentTypes = argumentTypes;
126 for (int i = 0; i < length; i++) {
127 Expression argument = arguments[i];
128 if (argument instanceof CastExpression) {
129 // narrowing conversion on base type may change value, thus necessary
130 if ((argument.bits & UnnecessaryCastMask) == 0 && argument.resolvedType.isBaseType()) {
133 TypeBinding castedExpressionType = ((CastExpression)argument).expression.resolvedType;
134 if (castedExpressionType == null) return; // cannot do better
135 // obvious identity cast
136 if (castedExpressionType == argumentTypes[i]) {
137 scope.problemReporter().unnecessaryCast((CastExpression)argument);
138 } else if (castedExpressionType == NullBinding){
139 continue; // tolerate null argument cast
141 if (rawArgumentTypes == argumentTypes) {
142 System.arraycopy(rawArgumentTypes, 0, rawArgumentTypes = new TypeBinding[length], 0, length);
144 // retain original argument type
145 rawArgumentTypes[i] = castedExpressionType;
149 // perform alternate lookup with original types
150 if (rawArgumentTypes != argumentTypes) {
151 checkAlternateBinding(scope, receiver, receiverType, binding, arguments, argumentTypes, rawArgumentTypes, invocationSite);
156 * Check binary operator casted arguments
158 public static void checkNeedForArgumentCasts(BlockScope scope, int operator, int operatorSignature, Expression left, int leftTypeId, boolean leftIsCast, Expression right, int rightTypeId, boolean rightIsCast) {
160 if (scope.environment().options.getSeverity(CompilerOptions.UnnecessaryTypeCheck) == ProblemSeverities.Ignore) return;
162 // check need for left operand cast
163 int alternateLeftTypeId = leftTypeId;
165 if ((left.bits & UnnecessaryCastMask) == 0 && left.resolvedType.isBaseType()) {
166 // narrowing conversion on base type may change value, thus necessary
169 TypeBinding alternateLeftType = ((CastExpression)left).expression.resolvedType;
170 if (alternateLeftType == null) return; // cannot do better
171 if ((alternateLeftTypeId = alternateLeftType.id) == leftTypeId) { // obvious identity cast
172 scope.problemReporter().unnecessaryCast((CastExpression)left);
174 } else if (alternateLeftTypeId == T_null) {
175 alternateLeftTypeId = leftTypeId; // tolerate null argument cast
180 // check need for right operand cast
181 int alternateRightTypeId = rightTypeId;
183 if ((right.bits & UnnecessaryCastMask) == 0 && right.resolvedType.isBaseType()) {
184 // narrowing conversion on base type may change value, thus necessary
187 TypeBinding alternateRightType = ((CastExpression)right).expression.resolvedType;
188 if (alternateRightType == null) return; // cannot do better
189 if ((alternateRightTypeId = alternateRightType.id) == rightTypeId) { // obvious identity cast
190 scope.problemReporter().unnecessaryCast((CastExpression)right);
192 } else if (alternateRightTypeId == T_null) {
193 alternateRightTypeId = rightTypeId; // tolerate null argument cast
198 if (leftIsCast || rightIsCast) {
199 if (alternateLeftTypeId > 15 || alternateRightTypeId > 15) { // must convert String + Object || Object + String
200 if (alternateLeftTypeId == T_JavaLangString) {
201 alternateRightTypeId = T_JavaLangObject;
202 } else if (alternateRightTypeId == T_JavaLangString) {
203 alternateLeftTypeId = T_JavaLangObject;
205 return; // invalid operator
208 int alternateOperatorSignature = OperatorExpression.OperatorSignatures[operator][(alternateLeftTypeId << 4) + alternateRightTypeId];
209 // (cast) left Op (cast) right --> result
210 // 1111 0000 1111 0000 1111
211 // <<16 <<12 <<8 <<4 <<0
212 final int CompareMASK = (0xF<<16) + (0xF<<8) + 0xF; // mask hiding compile-time types
213 if ((operatorSignature & CompareMASK) == (alternateOperatorSignature & CompareMASK)) { // same promotions and result
214 if (leftIsCast) scope.problemReporter().unnecessaryCastForArgument((CastExpression)left, TypeBinding.wellKnownType(scope, (left.implicitConversion & IMPLICIT_CONVERSION_MASK) >> 4));
215 if (rightIsCast) scope.problemReporter().unnecessaryCastForArgument((CastExpression)right, TypeBinding.wellKnownType(scope, (right.implicitConversion & IMPLICIT_CONVERSION_MASK) >> 4));
220 private static void checkAlternateBinding(BlockScope scope, Expression receiver, TypeBinding receiverType, MethodBinding binding, Expression[] arguments, TypeBinding[] originalArgumentTypes, TypeBinding[] alternateArgumentTypes, final InvocationSite invocationSite) {
222 InvocationSite fakeInvocationSite = new InvocationSite(){
223 public TypeBinding[] genericTypeArguments() { return null; }
224 public boolean isSuperAccess(){ return invocationSite.isSuperAccess(); }
225 public boolean isTypeAccess() { return invocationSite.isTypeAccess(); }
226 public void setActualReceiverType(ReferenceBinding actualReceiverType) { /* ignore */}
227 public void setDepth(int depth) { /* ignore */}
228 public void setFieldIndex(int depth){ /* ignore */}
229 public int sourceStart() { return 0; }
230 public int sourceEnd() { return 0; }
232 MethodBinding bindingIfNoCast;
233 if (binding.isConstructor()) {
234 bindingIfNoCast = scope.getConstructor((ReferenceBinding)receiverType, alternateArgumentTypes, fakeInvocationSite);
236 bindingIfNoCast = receiver.isImplicitThis()
237 ? scope.getImplicitMethod(binding.selector, alternateArgumentTypes, fakeInvocationSite)
238 : scope.getMethod(receiverType, binding.selector, alternateArgumentTypes, fakeInvocationSite);
240 if (bindingIfNoCast == binding) {
241 for (int i = 0, length = originalArgumentTypes.length; i < length; i++) {
242 if (originalArgumentTypes[i] != alternateArgumentTypes[i]) {
243 scope.problemReporter().unnecessaryCastForArgument((CastExpression)arguments[i], binding.parameters[i]);
249 public boolean checkUnsafeCast(Scope scope, TypeBinding castType, TypeBinding expressionType, TypeBinding match, boolean isNarrowing) {
250 if (match == castType) {
251 if (!isNarrowing) tagAsUnnecessaryCast(scope, castType);
254 if (castType.isBoundParameterizedType() || castType.isGenericType()) {
255 if (match.isProvablyDistinctFrom(isNarrowing ? expressionType : castType, 0)) {
256 reportIllegalCast(scope, castType, expressionType);
259 if (isNarrowing ? !expressionType.isEquivalentTo(match) : !match.isEquivalentTo(castType)) {
260 scope.problemReporter().unsafeCast(this);
263 if ((castType.tagBits & TagBits.HasDirectWildcard) == 0) {
264 if ((!match.isParameterizedType() && !match.isGenericType())
265 || expressionType.isRawType()) {
266 scope.problemReporter().unsafeCast(this);
271 if (!isNarrowing) tagAsUnnecessaryCast(scope, castType);
276 * Cast expression code generation
278 * @param currentScope org.eclipse.jdt.internal.compiler.lookup.BlockScope
279 * @param codeStream org.eclipse.jdt.internal.compiler.codegen.CodeStream
280 * @param valueRequired boolean
282 public void generateCode(
283 BlockScope currentScope,
284 CodeStream codeStream,
285 boolean valueRequired) {
287 int pc = codeStream.position;
288 boolean needRuntimeCheckcast = (this.bits & NeedRuntimeCheckCastMASK) != 0;
289 if (constant != NotAConstant) {
290 if (valueRequired || needRuntimeCheckcast) { // Added for: 1F1W9IG: IVJCOM:WINNT - Compiler omits casting check
291 codeStream.generateConstant(constant, implicitConversion);
292 if (needRuntimeCheckcast) {
293 codeStream.checkcast(this.resolvedType);
295 codeStream.generateImplicitConversion(this.implicitConversion);
301 codeStream.recordPositionsFrom(pc, this.sourceStart);
304 expression.generateCode(
307 valueRequired || needRuntimeCheckcast);
308 if (needRuntimeCheckcast) {
309 codeStream.checkcast(this.resolvedType);
311 codeStream.generateImplicitConversion(implicitConversion);
317 codeStream.generateImplicitConversion(implicitConversion);
319 codeStream.recordPositionsFrom(pc, this.sourceStart);
322 public Expression innermostCastedExpression(){
323 Expression current = this.expression;
324 while (current instanceof CastExpression) {
325 current = ((CastExpression) current).expression;
331 * @see org.eclipse.jdt.internal.compiler.ast.Expression#localVariableBinding()
333 public LocalVariableBinding localVariableBinding() {
334 return this.expression.localVariableBinding();
337 public int nullStatus(FlowInfo flowInfo) {
338 return this.expression.nullStatus(flowInfo);
341 public StringBuffer printExpression(int indent, StringBuffer output) {
344 type.print(0, output).append(") "); //$NON-NLS-1$
345 return expression.printExpression(0, output);
348 public void reportIllegalCast(Scope scope, TypeBinding castType, TypeBinding expressionType) {
349 scope.problemReporter().typeCastError(this, castType, expressionType);
352 public TypeBinding resolveType(BlockScope scope) {
353 // compute a new constant if the cast is effective
355 // due to the fact an expression may start with ( and that a cast can also start with (
356 // the field is an expression....it can be a TypeReference OR a NameReference Or
357 // any kind of Expression <-- this last one is invalid.......
359 constant = Constant.NotAConstant;
360 implicitConversion = T_undefined;
362 if ((type instanceof TypeReference) || (type instanceof NameReference)
363 && ((type.bits & ASTNode.ParenthesizedMASK) >> ASTNode.ParenthesizedSHIFT) == 0) { // no extra parenthesis around type: ((A))exp
365 this.resolvedType = type.resolveType(scope);
366 expression.setExpectedType(this.resolvedType); // needed in case of generic method invocation
367 TypeBinding expressionType = expression.resolveType(scope);
368 if (this.resolvedType != null && expressionType != null) {
369 checkCastTypesCompatibility(scope, this.resolvedType, expressionType, this.expression);
370 this.expression.computeConversion(scope, this.resolvedType, expressionType);
371 if ((this.bits & UnnecessaryCastMask) != 0) {
372 if ((this.bits & IgnoreNeedForCastCheckMASK) == 0) {
373 if (!usedForGenericMethodReturnTypeInference()) // used for generic type inference ?
374 scope.problemReporter().unnecessaryCast(this);
378 return this.resolvedType;
379 } else { // expression as a cast
380 TypeBinding expressionType = expression.resolveType(scope);
381 if (expressionType == null) return null;
382 scope.problemReporter().invalidTypeReference(type);
388 * @see org.eclipse.jdt.internal.compiler.ast.Expression#setExpectedType(org.eclipse.jdt.internal.compiler.lookup.TypeBinding)
390 public void setExpectedType(TypeBinding expectedType) {
391 this.expectedType = expectedType;
395 * Determines whether apparent unnecessary cast wasn't actually used to
396 * perform return type inference of generic method invocation.
398 private boolean usedForGenericMethodReturnTypeInference() {
399 if (this.expression instanceof MessageSend) {
400 MethodBinding method = ((MessageSend)this.expression).binding;
401 if (method instanceof ParameterizedGenericMethodBinding
402 && ((ParameterizedGenericMethodBinding)method).inferredReturnType) {
403 if (this.expectedType == null)
405 if (this.resolvedType != this.expectedType)
413 * @see org.eclipse.jdt.internal.compiler.ast.Expression#tagAsNeedCheckCast()
415 public void tagAsNeedCheckCast() {
416 this.bits |= NeedRuntimeCheckCastMASK;
420 * @see org.eclipse.jdt.internal.compiler.ast.Expression#tagAsUnnecessaryCast(Scope, TypeBinding)
422 public void tagAsUnnecessaryCast(Scope scope, TypeBinding castType) {
423 if (this.expression.resolvedType == null) return; // cannot do better if expression is not bound
424 this.bits |= UnnecessaryCastMask;
427 public void traverse(
429 BlockScope blockScope) {
431 if (visitor.visit(this, blockScope)) {
432 type.traverse(visitor, blockScope);
433 expression.traverse(visitor, blockScope);
435 visitor.endVisit(this, blockScope);