001: /*******************************************************************************
002: * Copyright (c) 2000, 2007 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: * Nick Teryaev - fix for bug (https://bugs.eclipse.org/bugs/show_bug.cgi?id=40752)
011: *******************************************************************************/package org.eclipse.jdt.internal.compiler.ast;
012:
013: import org.eclipse.jdt.internal.compiler.ASTVisitor;
014: import org.eclipse.jdt.internal.compiler.impl.*;
015: import org.eclipse.jdt.internal.compiler.codegen.*;
016: import org.eclipse.jdt.internal.compiler.flow.*;
017: import org.eclipse.jdt.internal.compiler.lookup.*;
018: import org.eclipse.jdt.internal.compiler.problem.ProblemSeverities;
019:
020: public class CastExpression extends Expression {
021:
022: public Expression expression;
023: public Expression type;
024: public TypeBinding expectedType; // when assignment conversion to a given expected type: String s = (String) t;
025:
026: //expression.implicitConversion holds the cast for baseType casting
027: public CastExpression(Expression expression, Expression type) {
028: this .expression = expression;
029: this .type = type;
030: type.bits |= IgnoreRawTypeCheck; // no need to worry about raw type usage
031: }
032:
033: public FlowInfo analyseCode(BlockScope currentScope,
034: FlowContext flowContext, FlowInfo flowInfo) {
035:
036: return expression.analyseCode(currentScope, flowContext,
037: flowInfo).unconditionalInits();
038: }
039:
040: /**
041: * Complain if assigned expression is cast, but not actually used as such, e.g. Object o = (List) object;
042: */
043: public static void checkNeedForAssignedCast(BlockScope scope,
044: TypeBinding expectedType, CastExpression rhs) {
045:
046: if (scope.compilerOptions().getSeverity(
047: CompilerOptions.UnnecessaryTypeCheck) == ProblemSeverities.Ignore)
048: return;
049:
050: TypeBinding castedExpressionType = rhs.expression.resolvedType;
051: // int i = (byte) n; // cast still had side effect
052: // double d = (float) n; // cast to float is unnecessary
053: if (castedExpressionType == null
054: || rhs.resolvedType.isBaseType())
055: return;
056: //if (castedExpressionType.id == T_null) return; // tolerate null expression cast
057: if (castedExpressionType.isCompatibleWith(expectedType)) {
058: scope.problemReporter().unnecessaryCast(rhs);
059: }
060: }
061:
062: /**
063: * Casting an enclosing instance will considered as useful if removing it would actually bind to a different type
064: */
065: public static void checkNeedForEnclosingInstanceCast(
066: BlockScope scope, Expression enclosingInstance,
067: TypeBinding enclosingInstanceType, TypeBinding memberType) {
068:
069: if (scope.compilerOptions().getSeverity(
070: CompilerOptions.UnnecessaryTypeCheck) == ProblemSeverities.Ignore)
071: return;
072:
073: TypeBinding castedExpressionType = ((CastExpression) enclosingInstance).expression.resolvedType;
074: if (castedExpressionType == null)
075: return; // cannot do better
076: // obvious identity cast
077: if (castedExpressionType == enclosingInstanceType) {
078: scope.problemReporter().unnecessaryCast(
079: (CastExpression) enclosingInstance);
080: } else if (castedExpressionType == TypeBinding.NULL) {
081: return; // tolerate null enclosing instance cast
082: } else {
083: TypeBinding alternateEnclosingInstanceType = castedExpressionType;
084: if (castedExpressionType.isBaseType()
085: || castedExpressionType.isArrayType())
086: return; // error case
087: if (memberType == scope.getMemberType(memberType
088: .sourceName(),
089: (ReferenceBinding) alternateEnclosingInstanceType)) {
090: scope.problemReporter().unnecessaryCast(
091: (CastExpression) enclosingInstance);
092: }
093: }
094: }
095:
096: /**
097: * Only complain for identity cast, since other type of casts may be useful: e.g. ~((~(long) 0) << 32) is different from: ~((~0) << 32)
098: */
099: public static void checkNeedForArgumentCast(BlockScope scope,
100: int operator, int operatorSignature, Expression expression,
101: int expressionTypeId) {
102:
103: if (scope.compilerOptions().getSeverity(
104: CompilerOptions.UnnecessaryTypeCheck) == ProblemSeverities.Ignore)
105: return;
106:
107: // check need for left operand cast
108: int alternateLeftTypeId = expressionTypeId;
109: if ((expression.bits & UnnecessaryCast) == 0
110: && expression.resolvedType.isBaseType()) {
111: // narrowing conversion on base type may change value, thus necessary
112: return;
113: } else {
114: TypeBinding alternateLeftType = ((CastExpression) expression).expression.resolvedType;
115: if (alternateLeftType == null)
116: return; // cannot do better
117: if ((alternateLeftTypeId = alternateLeftType.id) == expressionTypeId) { // obvious identity cast
118: scope.problemReporter().unnecessaryCast(
119: (CastExpression) expression);
120: return;
121: } else if (alternateLeftTypeId == T_null) {
122: alternateLeftTypeId = expressionTypeId; // tolerate null argument cast
123: return;
124: }
125: }
126: /* tolerate widening cast in unary expressions, as may be used when combined in binary expressions (41680)
127: int alternateOperatorSignature = OperatorExpression.OperatorSignatures[operator][(alternateLeftTypeId << 4) + alternateLeftTypeId];
128: // (cast) left Op (cast) right --> result
129: // 1111 0000 1111 0000 1111
130: // <<16 <<12 <<8 <<4 <<0
131: final int CompareMASK = (0xF<<16) + (0xF<<8) + 0xF; // mask hiding compile-time types
132: if ((operatorSignature & CompareMASK) == (alternateOperatorSignature & CompareMASK)) { // same promotions and result
133: scope.problemReporter().unnecessaryCastForArgument((CastExpression)expression, TypeBinding.wellKnownType(scope, expression.implicitConversion >> 4));
134: }
135: */
136: }
137:
138: /**
139: * Cast expressions will considered as useful if removing them all would actually bind to a different method
140: * (no fine grain analysis on per casted argument basis, simply separate widening cast from narrowing ones)
141: */
142: public static void checkNeedForArgumentCasts(BlockScope scope,
143: Expression receiver, TypeBinding receiverType,
144: MethodBinding binding, Expression[] arguments,
145: TypeBinding[] argumentTypes,
146: final InvocationSite invocationSite) {
147:
148: if (scope.compilerOptions().getSeverity(
149: CompilerOptions.UnnecessaryTypeCheck) == ProblemSeverities.Ignore)
150: return;
151:
152: int length = argumentTypes.length;
153:
154: // iterate over arguments, and retrieve original argument types (before cast)
155: TypeBinding[] rawArgumentTypes = argumentTypes;
156: for (int i = 0; i < length; i++) {
157: Expression argument = arguments[i];
158: if (argument instanceof CastExpression) {
159: // narrowing conversion on base type may change value, thus necessary
160: if ((argument.bits & UnnecessaryCast) == 0
161: && argument.resolvedType.isBaseType()) {
162: continue;
163: }
164: TypeBinding castedExpressionType = ((CastExpression) argument).expression.resolvedType;
165: if (castedExpressionType == null)
166: return; // cannot do better
167: // obvious identity cast
168: if (castedExpressionType == argumentTypes[i]) {
169: scope.problemReporter().unnecessaryCast(
170: (CastExpression) argument);
171: } else if (castedExpressionType == TypeBinding.NULL) {
172: continue; // tolerate null argument cast
173: } else if ((argument.implicitConversion & BOXING) != 0) {
174: continue; // boxing has a side effect: (int) char is not boxed as simple char
175: } else {
176: if (rawArgumentTypes == argumentTypes) {
177: System
178: .arraycopy(
179: rawArgumentTypes,
180: 0,
181: rawArgumentTypes = new TypeBinding[length],
182: 0, length);
183: }
184: // retain original argument type
185: rawArgumentTypes[i] = castedExpressionType;
186: }
187: }
188: }
189: // perform alternate lookup with original types
190: if (rawArgumentTypes != argumentTypes) {
191: checkAlternateBinding(scope, receiver, receiverType,
192: binding, arguments, argumentTypes,
193: rawArgumentTypes, invocationSite);
194: }
195: }
196:
197: /**
198: * Check binary operator casted arguments
199: */
200: public static void checkNeedForArgumentCasts(BlockScope scope,
201: int operator, int operatorSignature, Expression left,
202: int leftTypeId, boolean leftIsCast, Expression right,
203: int rightTypeId, boolean rightIsCast) {
204:
205: if (scope.compilerOptions().getSeverity(
206: CompilerOptions.UnnecessaryTypeCheck) == ProblemSeverities.Ignore)
207: return;
208:
209: // check need for left operand cast
210: int alternateLeftTypeId = leftTypeId;
211: if (leftIsCast) {
212: if ((left.bits & UnnecessaryCast) == 0
213: && left.resolvedType.isBaseType()) {
214: // narrowing conversion on base type may change value, thus necessary
215: leftIsCast = false;
216: } else {
217: TypeBinding alternateLeftType = ((CastExpression) left).expression.resolvedType;
218: if (alternateLeftType == null)
219: return; // cannot do better
220: if ((alternateLeftTypeId = alternateLeftType.id) == leftTypeId) { // obvious identity cast
221: scope.problemReporter().unnecessaryCast(
222: (CastExpression) left);
223: leftIsCast = false;
224: } else if (alternateLeftTypeId == T_null) {
225: alternateLeftTypeId = leftTypeId; // tolerate null argument cast
226: leftIsCast = false;
227: }
228: }
229: }
230: // check need for right operand cast
231: int alternateRightTypeId = rightTypeId;
232: if (rightIsCast) {
233: if ((right.bits & UnnecessaryCast) == 0
234: && right.resolvedType.isBaseType()) {
235: // narrowing conversion on base type may change value, thus necessary
236: rightIsCast = false;
237: } else {
238: TypeBinding alternateRightType = ((CastExpression) right).expression.resolvedType;
239: if (alternateRightType == null)
240: return; // cannot do better
241: if ((alternateRightTypeId = alternateRightType.id) == rightTypeId) { // obvious identity cast
242: scope.problemReporter().unnecessaryCast(
243: (CastExpression) right);
244: rightIsCast = false;
245: } else if (alternateRightTypeId == T_null) {
246: alternateRightTypeId = rightTypeId; // tolerate null argument cast
247: rightIsCast = false;
248: }
249: }
250: }
251: if (leftIsCast || rightIsCast) {
252: if (alternateLeftTypeId > 15 || alternateRightTypeId > 15) { // must convert String + Object || Object + String
253: if (alternateLeftTypeId == T_JavaLangString) {
254: alternateRightTypeId = T_JavaLangObject;
255: } else if (alternateRightTypeId == T_JavaLangString) {
256: alternateLeftTypeId = T_JavaLangObject;
257: } else {
258: return; // invalid operator
259: }
260: }
261: int alternateOperatorSignature = OperatorExpression.OperatorSignatures[operator][(alternateLeftTypeId << 4)
262: + alternateRightTypeId];
263: // (cast) left Op (cast) right --> result
264: // 1111 0000 1111 0000 1111
265: // <<16 <<12 <<8 <<4 <<0
266: final int CompareMASK = (0xF << 16) + (0xF << 8) + 0xF; // mask hiding compile-time types
267: if ((operatorSignature & CompareMASK) == (alternateOperatorSignature & CompareMASK)) { // same promotions and result
268: if (leftIsCast)
269: scope.problemReporter().unnecessaryCast(
270: (CastExpression) left);
271: if (rightIsCast)
272: scope.problemReporter().unnecessaryCast(
273: (CastExpression) right);
274: }
275: }
276: }
277:
278: private static void checkAlternateBinding(BlockScope scope,
279: Expression receiver, TypeBinding receiverType,
280: MethodBinding binding, Expression[] arguments,
281: TypeBinding[] originalArgumentTypes,
282: TypeBinding[] alternateArgumentTypes,
283: final InvocationSite invocationSite) {
284:
285: InvocationSite fakeInvocationSite = new InvocationSite() {
286: public TypeBinding[] genericTypeArguments() {
287: return null;
288: }
289:
290: public boolean isSuperAccess() {
291: return invocationSite.isSuperAccess();
292: }
293:
294: public boolean isTypeAccess() {
295: return invocationSite.isTypeAccess();
296: }
297:
298: public void setActualReceiverType(
299: ReferenceBinding actualReceiverType) { /* ignore */
300: }
301:
302: public void setDepth(int depth) { /* ignore */
303: }
304:
305: public void setFieldIndex(int depth) { /* ignore */
306: }
307:
308: public int sourceStart() {
309: return 0;
310: }
311:
312: public int sourceEnd() {
313: return 0;
314: }
315: };
316: MethodBinding bindingIfNoCast;
317: if (binding.isConstructor()) {
318: bindingIfNoCast = scope.getConstructor(
319: (ReferenceBinding) receiverType,
320: alternateArgumentTypes, fakeInvocationSite);
321: } else {
322: bindingIfNoCast = receiver.isImplicitThis() ? scope
323: .getImplicitMethod(binding.selector,
324: alternateArgumentTypes, fakeInvocationSite)
325: : scope.getMethod(receiverType, binding.selector,
326: alternateArgumentTypes, fakeInvocationSite);
327: }
328: if (bindingIfNoCast == binding) {
329: int argumentLength = originalArgumentTypes.length;
330: if (binding.isVarargs()) {
331: int paramLength = binding.parameters.length;
332: if (paramLength == argumentLength) {
333: int varargsIndex = paramLength - 1;
334: ArrayBinding varargsType = (ArrayBinding) binding.parameters[varargsIndex];
335: TypeBinding lastArgType = alternateArgumentTypes[varargsIndex];
336: // originalType may be compatible already, but cast mandated
337: // to clarify between varargs/non-varargs call
338: if (varargsType.dimensions != lastArgType
339: .dimensions()) {
340: return;
341: }
342: if (lastArgType.isCompatibleWith(varargsType
343: .elementsType())
344: && lastArgType
345: .isCompatibleWith(varargsType)) {
346: return;
347: }
348: }
349: }
350: for (int i = 0; i < argumentLength; i++) {
351: if (originalArgumentTypes[i] != alternateArgumentTypes[i]) {
352: scope.problemReporter().unnecessaryCast(
353: (CastExpression) arguments[i]);
354: }
355: }
356: }
357: }
358:
359: public boolean checkUnsafeCast(Scope scope, TypeBinding castType,
360: TypeBinding expressionType, TypeBinding match,
361: boolean isNarrowing) {
362: if (match == castType) {
363: if (!isNarrowing
364: && match == this .resolvedType.leafComponentType()) { // do not tag as unnecessary when recursing through upper bounds
365: tagAsUnnecessaryCast(scope, castType);
366: }
367: return true;
368: }
369: if (match != null
370: && match.isProvablyDistinctFrom(
371: isNarrowing ? expressionType : castType, 0)) {
372: return false;
373: }
374: switch (castType.kind()) {
375: case Binding.PARAMETERIZED_TYPE:
376: if (castType.isBoundParameterizedType()) {
377: if (match == null) { // unrelated types
378: this .bits |= UnsafeCast;
379: return true;
380: }
381: switch (match.kind()) {
382: case Binding.PARAMETERIZED_TYPE:
383: if (isNarrowing) {
384: // [JLS 5.5] T <: S
385: if (expressionType.isRawType()
386: || !expressionType
387: .isEquivalentTo(match)) {
388: this .bits |= UnsafeCast;
389: return true;
390: }
391: // [JLS 5.5] S has no subtype X != T, such that |X| == |T|
392: TypeBinding genericCastType = castType
393: .erasure(); // jump to generic type
394: TypeBinding genericMatch = genericCastType
395: .findSuperTypeWithSameErasure(expressionType);
396: if (genericMatch == match) {
397: this .bits |= UnsafeCast;
398: }
399: return true;
400: } else {
401: // [JLS 5.5] T >: S
402: if (!match.isEquivalentTo(castType)) {
403: this .bits |= UnsafeCast;
404: return true;
405: }
406: }
407: break;
408: case Binding.RAW_TYPE:
409: this .bits |= UnsafeCast; // upcast since castType is known to be bound paramType
410: return true;
411: default:
412: if (isNarrowing) {
413: // match is not parameterized or raw, then any other subtype of match will erase to |T|
414: this .bits |= UnsafeCast;
415: return true;
416: }
417: break;
418: }
419: }
420: break;
421: case Binding.ARRAY_TYPE:
422: TypeBinding leafType = castType.leafComponentType();
423: if (isNarrowing
424: && (leafType.isBoundParameterizedType() || leafType
425: .isTypeVariable())) {
426: this .bits |= UnsafeCast;
427: return true;
428: }
429: break;
430: case Binding.TYPE_PARAMETER:
431: this .bits |= UnsafeCast;
432: return true;
433: }
434: if (!isNarrowing
435: && match == this .resolvedType.leafComponentType()) { // do not tag as unnecessary when recursing through upper bounds
436: tagAsUnnecessaryCast(scope, castType);
437: }
438: return true;
439: }
440:
441: /**
442: * Cast expression code generation
443: *
444: * @param currentScope org.eclipse.jdt.internal.compiler.lookup.BlockScope
445: * @param codeStream org.eclipse.jdt.internal.compiler.codegen.CodeStream
446: * @param valueRequired boolean
447: */
448: public void generateCode(BlockScope currentScope,
449: CodeStream codeStream, boolean valueRequired) {
450:
451: int pc = codeStream.position;
452: boolean needRuntimeCheckcast = (this .bits & GenerateCheckcast) != 0;
453: if (constant != Constant.NotAConstant) {
454: if (valueRequired || needRuntimeCheckcast) { // Added for: 1F1W9IG: IVJCOM:WINNT - Compiler omits casting check
455: codeStream.generateConstant(constant,
456: implicitConversion);
457: if (needRuntimeCheckcast) {
458: codeStream.checkcast(this .resolvedType);
459: }
460: if (!valueRequired) {
461: // the resolveType cannot be double or long
462: codeStream.pop();
463: }
464: }
465: codeStream.recordPositionsFrom(pc, this .sourceStart);
466: return;
467: }
468: expression.generateCode(currentScope, codeStream, valueRequired
469: || needRuntimeCheckcast);
470: if (needRuntimeCheckcast
471: && this .expression.postConversionType(currentScope) != this .resolvedType
472: .erasure()) { // no need to issue a checkcast if already done as genericCast
473: codeStream.checkcast(this .resolvedType);
474: }
475: if (valueRequired) {
476: codeStream.generateImplicitConversion(implicitConversion);
477: } else if (needRuntimeCheckcast) {
478: codeStream.pop();
479: }
480: codeStream.recordPositionsFrom(pc, this .sourceStart);
481: }
482:
483: public Expression innermostCastedExpression() {
484: Expression current = this .expression;
485: while (current instanceof CastExpression) {
486: current = ((CastExpression) current).expression;
487: }
488: return current;
489: }
490:
491: /**
492: * @see org.eclipse.jdt.internal.compiler.ast.Expression#localVariableBinding()
493: */
494: public LocalVariableBinding localVariableBinding() {
495: return this .expression.localVariableBinding();
496: }
497:
498: public int nullStatus(FlowInfo flowInfo) {
499: return this .expression.nullStatus(flowInfo);
500: }
501:
502: /**
503: * @see org.eclipse.jdt.internal.compiler.ast.Expression#optimizedBooleanConstant()
504: */
505: public Constant optimizedBooleanConstant() {
506: switch (this .resolvedType.id) {
507: case T_boolean:
508: case T_JavaLangBoolean:
509: return this .expression.optimizedBooleanConstant();
510: }
511: return Constant.NotAConstant;
512: }
513:
514: public StringBuffer printExpression(int indent, StringBuffer output) {
515:
516: output.append('(');
517: type.print(0, output).append(") "); //$NON-NLS-1$
518: return expression.printExpression(0, output);
519: }
520:
521: public TypeBinding resolveType(BlockScope scope) {
522: // compute a new constant if the cast is effective
523:
524: // due to the fact an expression may start with ( and that a cast can also start with (
525: // the field is an expression....it can be a TypeReference OR a NameReference Or
526: // any kind of Expression <-- this last one is invalid.......
527:
528: constant = Constant.NotAConstant;
529: implicitConversion = T_undefined;
530:
531: if ((type instanceof TypeReference)
532: || (type instanceof NameReference)
533: && ((type.bits & ASTNode.ParenthesizedMASK) >> ASTNode.ParenthesizedSHIFT) == 0) { // no extra parenthesis around type: ((A))exp
534:
535: TypeBinding castType = this .resolvedType = type
536: .resolveType(scope);
537: //expression.setExpectedType(this.resolvedType); // needed in case of generic method invocation
538: TypeBinding expressionType = expression.resolveType(scope);
539: if (castType != null) {
540: if (expressionType != null) {
541: boolean isLegal = checkCastTypesCompatibility(
542: scope, castType, expressionType,
543: this .expression);
544: if (isLegal) {
545: this .expression.computeConversion(scope,
546: castType, expressionType);
547: if ((this .bits & UnsafeCast) != 0) { // unsafe cast
548: scope.problemReporter().unsafeCast(this ,
549: scope);
550: } else {
551: if (castType.isRawType()
552: && scope
553: .compilerOptions()
554: .getSeverity(
555: CompilerOptions.RawTypeReference) != ProblemSeverities.Ignore) {
556: scope.problemReporter()
557: .rawTypeReference(this .type,
558: castType);
559: }
560: if ((this .bits & (UnnecessaryCast | DisableUnnecessaryCastCheck)) == UnnecessaryCast) { // unnecessary cast
561: if (!isIndirectlyUsed()) // used for generic type inference or boxing ?
562: scope.problemReporter()
563: .unnecessaryCast(this );
564: }
565: }
566: } else { // illegal cast
567: scope.problemReporter().typeCastError(this ,
568: castType, expressionType);
569: this .bits |= DisableUnnecessaryCastCheck; // disable further secondary diagnosis
570: }
571: }
572: this .resolvedType = castType.capture(scope,
573: this .sourceEnd);
574: }
575: return this .resolvedType;
576: } else { // expression as a cast
577: TypeBinding expressionType = expression.resolveType(scope);
578: if (expressionType == null)
579: return null;
580: scope.problemReporter().invalidTypeReference(type);
581: return null;
582: }
583: }
584:
585: /**
586: * @see org.eclipse.jdt.internal.compiler.ast.Expression#setExpectedType(org.eclipse.jdt.internal.compiler.lookup.TypeBinding)
587: */
588: public void setExpectedType(TypeBinding expectedType) {
589: this .expectedType = expectedType;
590: }
591:
592: /**
593: * Determines whether apparent unnecessary cast wasn't actually used to
594: * perform return type inference of generic method invocation or boxing.
595: */
596: private boolean isIndirectlyUsed() {
597: if (this .expression instanceof MessageSend) {
598: MethodBinding method = ((MessageSend) this .expression).binding;
599: if (method instanceof ParameterizedGenericMethodBinding
600: && ((ParameterizedGenericMethodBinding) method).inferredReturnType) {
601: if (this .expectedType == null)
602: return true;
603: if (this .resolvedType != this .expectedType)
604: return true;
605: }
606: }
607: if (this .expectedType != null
608: && this .resolvedType.isBaseType()
609: && !this .resolvedType
610: .isCompatibleWith(this .expectedType)) {
611: // boxing: Short s = (short) _byte
612: return true;
613: }
614: return false;
615: }
616:
617: /**
618: * @see org.eclipse.jdt.internal.compiler.ast.Expression#tagAsNeedCheckCast()
619: */
620: public void tagAsNeedCheckCast() {
621: this .bits |= GenerateCheckcast;
622: }
623:
624: /**
625: * @see org.eclipse.jdt.internal.compiler.ast.Expression#tagAsUnnecessaryCast(Scope, TypeBinding)
626: */
627: public void tagAsUnnecessaryCast(Scope scope, TypeBinding castType) {
628: if (this .expression.resolvedType == null)
629: return; // cannot do better if expression is not bound
630: this .bits |= UnnecessaryCast;
631: }
632:
633: public void traverse(ASTVisitor visitor, BlockScope blockScope) {
634:
635: if (visitor.visit(this, blockScope)) {
636: type.traverse(visitor, blockScope);
637: expression.traverse(visitor, blockScope);
638: }
639: visitor.endVisit(this, blockScope);
640: }
641: }
|