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: *******************************************************************************/package org.eclipse.jdt.internal.compiler.flow;
011:
012: import org.eclipse.jdt.internal.compiler.ast.ASTNode;
013: import org.eclipse.jdt.internal.compiler.ast.Expression;
014: import org.eclipse.jdt.internal.compiler.ast.Reference;
015: import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
016: import org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
017: import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding;
018: import org.eclipse.jdt.internal.compiler.lookup.Scope;
019: import org.eclipse.jdt.internal.compiler.lookup.VariableBinding;
020:
021: /**
022: * Reflects the context of code analysis, keeping track of enclosing
023: * try statements, exception handlers, etc...
024: */
025: public class FinallyFlowContext extends FlowContext {
026:
027: Reference[] finalAssignments;
028: VariableBinding[] finalVariables;
029: int assignCount;
030:
031: LocalVariableBinding[] nullLocals;
032: Expression[] nullReferences;
033: int[] nullCheckTypes;
034: int nullCount;
035:
036: public FinallyFlowContext(FlowContext parent, ASTNode associatedNode) {
037: super (parent, associatedNode);
038: }
039:
040: /**
041: * Given some contextual initialization info (derived from a try block or a catch block), this
042: * code will check that the subroutine context does not also initialize a final variable potentially set
043: * redundantly.
044: */
045: public void complainOnDeferredChecks(FlowInfo flowInfo,
046: BlockScope scope) {
047:
048: // check redundant final assignments
049: for (int i = 0; i < this .assignCount; i++) {
050: VariableBinding variable = this .finalVariables[i];
051: if (variable == null)
052: continue;
053:
054: boolean complained = false; // remember if have complained on this final assignment
055: if (variable instanceof FieldBinding) {
056: // final field
057: if (flowInfo
058: .isPotentiallyAssigned((FieldBinding) variable)) {
059: complained = true;
060: scope.problemReporter()
061: .duplicateInitializationOfBlankFinalField(
062: (FieldBinding) variable,
063: finalAssignments[i]);
064: }
065: } else {
066: // final local variable
067: if (flowInfo
068: .isPotentiallyAssigned((LocalVariableBinding) variable)) {
069: complained = true;
070: scope.problemReporter()
071: .duplicateInitializationOfFinalLocal(
072: (LocalVariableBinding) variable,
073: this .finalAssignments[i]);
074: }
075: }
076: // any reference reported at this level is removed from the parent context
077: // where it could also be reported again
078: if (complained) {
079: FlowContext currentContext = this .parent;
080: while (currentContext != null) {
081: //if (currentContext.isSubRoutine()) {
082: currentContext
083: .removeFinalAssignmentIfAny(this .finalAssignments[i]);
084: //}
085: currentContext = currentContext.parent;
086: }
087: }
088: }
089:
090: // check inconsistent null checks
091: if (this .deferNullDiagnostic) { // within an enclosing loop, be conservative
092: for (int i = 0; i < this .nullCount; i++) {
093: this .parent.recordUsingNullReference(scope,
094: this .nullLocals[i], this .nullReferences[i],
095: this .nullCheckTypes[i], flowInfo);
096: }
097: } else { // no enclosing loop, be as precise as possible right now
098: for (int i = 0; i < this .nullCount; i++) {
099: Expression expression = this .nullReferences[i];
100: // final local variable
101: LocalVariableBinding local = this .nullLocals[i];
102: switch (this .nullCheckTypes[i]) {
103: case CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NULL:
104: case CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NON_NULL:
105: if (flowInfo.isDefinitelyNonNull(local)) {
106: if (this .nullCheckTypes[i] == (CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NON_NULL)) {
107: scope
108: .problemReporter()
109: .localVariableRedundantCheckOnNonNull(
110: local, expression);
111: } else {
112: scope
113: .problemReporter()
114: .localVariableNonNullComparedToNull(
115: local, expression);
116: }
117: continue;
118: }
119: case CAN_ONLY_NULL | IN_COMPARISON_NULL:
120: case CAN_ONLY_NULL | IN_COMPARISON_NON_NULL:
121: case CAN_ONLY_NULL | IN_ASSIGNMENT:
122: case CAN_ONLY_NULL | IN_INSTANCEOF:
123: if (flowInfo.isDefinitelyNull(local)) {
124: switch (this .nullCheckTypes[i] & CONTEXT_MASK) {
125: case FlowContext.IN_COMPARISON_NULL:
126: scope.problemReporter()
127: .localVariableRedundantCheckOnNull(
128: local, expression);
129: continue;
130: case FlowContext.IN_COMPARISON_NON_NULL:
131: scope
132: .problemReporter()
133: .localVariableNullComparedToNonNull(
134: local, expression);
135: continue;
136: case FlowContext.IN_ASSIGNMENT:
137: scope
138: .problemReporter()
139: .localVariableRedundantNullAssignment(
140: local, expression);
141: continue;
142: case FlowContext.IN_INSTANCEOF:
143: scope.problemReporter()
144: .localVariableNullInstanceof(local,
145: expression);
146: continue;
147: }
148: }
149: break;
150: case MAY_NULL:
151: if (flowInfo.isDefinitelyNull(local)) {
152: scope.problemReporter()
153: .localVariableNullReference(local,
154: expression);
155: continue;
156: }
157: if (flowInfo.isPotentiallyNull(local)) {
158: scope.problemReporter()
159: .localVariablePotentialNullReference(
160: local, expression);
161: }
162: break;
163: default:
164: // should not happen
165: }
166: }
167: }
168: }
169:
170: public String individualToString() {
171:
172: StringBuffer buffer = new StringBuffer("Finally flow context"); //$NON-NLS-1$
173: buffer
174: .append("[finalAssignments count - ").append(assignCount).append(']'); //$NON-NLS-1$
175: buffer
176: .append("[nullReferences count - ").append(nullCount).append(']'); //$NON-NLS-1$
177: return buffer.toString();
178: }
179:
180: public boolean isSubRoutine() {
181: return true;
182: }
183:
184: protected boolean recordFinalAssignment(VariableBinding binding,
185: Reference finalAssignment) {
186: if (assignCount == 0) {
187: finalAssignments = new Reference[5];
188: finalVariables = new VariableBinding[5];
189: } else {
190: if (assignCount == finalAssignments.length)
191: System
192: .arraycopy(
193: finalAssignments,
194: 0,
195: (finalAssignments = new Reference[assignCount * 2]),
196: 0, assignCount);
197: System
198: .arraycopy(
199: finalVariables,
200: 0,
201: (finalVariables = new VariableBinding[assignCount * 2]),
202: 0, assignCount);
203: }
204: finalAssignments[assignCount] = finalAssignment;
205: finalVariables[assignCount++] = binding;
206: return true;
207: }
208:
209: public void recordUsingNullReference(Scope scope,
210: LocalVariableBinding local, Expression reference,
211: int checkType, FlowInfo flowInfo) {
212: if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) == 0
213: && !flowInfo.isDefinitelyUnknown(local)) {
214: if (deferNullDiagnostic) { // within an enclosing loop, be conservative
215: switch (checkType) {
216: case CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NULL:
217: case CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NON_NULL:
218: case CAN_ONLY_NULL | IN_COMPARISON_NULL:
219: case CAN_ONLY_NULL | IN_COMPARISON_NON_NULL:
220: case CAN_ONLY_NULL | IN_ASSIGNMENT:
221: case CAN_ONLY_NULL | IN_INSTANCEOF:
222: if (flowInfo.cannotBeNull(local)) {
223: if (checkType == (CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NON_NULL)) {
224: scope
225: .problemReporter()
226: .localVariableRedundantCheckOnNonNull(
227: local, reference);
228: } else {
229: scope
230: .problemReporter()
231: .localVariableNonNullComparedToNull(
232: local, reference);
233: }
234: return;
235: }
236: if (flowInfo.canOnlyBeNull(local)) {
237: switch (checkType & CONTEXT_MASK) {
238: case FlowContext.IN_COMPARISON_NULL:
239: scope.problemReporter()
240: .localVariableRedundantCheckOnNull(
241: local, reference);
242: return;
243: case FlowContext.IN_COMPARISON_NON_NULL:
244: scope
245: .problemReporter()
246: .localVariableNullComparedToNonNull(
247: local, reference);
248: return;
249: case FlowContext.IN_ASSIGNMENT:
250: scope
251: .problemReporter()
252: .localVariableRedundantNullAssignment(
253: local, reference);
254: return;
255: case FlowContext.IN_INSTANCEOF:
256: scope.problemReporter()
257: .localVariableNullInstanceof(local,
258: reference);
259: return;
260: }
261: }
262: break;
263: case MAY_NULL:
264: if (flowInfo.cannotBeNull(local)) {
265: return;
266: }
267: if (flowInfo.canOnlyBeNull(local)) {
268: scope.problemReporter()
269: .localVariableNullReference(local,
270: reference);
271: return;
272: }
273: break;
274: default:
275: // never happens
276: }
277: } else { // no enclosing loop, be as precise as possible right now
278: switch (checkType) {
279: case CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NULL:
280: case CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NON_NULL:
281: if (flowInfo.isDefinitelyNonNull(local)) {
282: if (checkType == (CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NON_NULL)) {
283: scope
284: .problemReporter()
285: .localVariableRedundantCheckOnNonNull(
286: local, reference);
287: } else {
288: scope
289: .problemReporter()
290: .localVariableNonNullComparedToNull(
291: local, reference);
292: }
293: return;
294: }
295: case CAN_ONLY_NULL | IN_COMPARISON_NULL:
296: case CAN_ONLY_NULL | IN_COMPARISON_NON_NULL:
297: case CAN_ONLY_NULL | IN_ASSIGNMENT:
298: case CAN_ONLY_NULL | IN_INSTANCEOF:
299: if (flowInfo.isDefinitelyNull(local)) {
300: switch (checkType & CONTEXT_MASK) {
301: case FlowContext.IN_COMPARISON_NULL:
302: scope.problemReporter()
303: .localVariableRedundantCheckOnNull(
304: local, reference);
305: return;
306: case FlowContext.IN_COMPARISON_NON_NULL:
307: scope
308: .problemReporter()
309: .localVariableNullComparedToNonNull(
310: local, reference);
311: return;
312: case FlowContext.IN_ASSIGNMENT:
313: scope
314: .problemReporter()
315: .localVariableRedundantNullAssignment(
316: local, reference);
317: return;
318: case FlowContext.IN_INSTANCEOF:
319: scope.problemReporter()
320: .localVariableNullInstanceof(local,
321: reference);
322: return;
323: }
324: }
325: break;
326: case MAY_NULL:
327: if (flowInfo.isDefinitelyNull(local)) {
328: scope.problemReporter()
329: .localVariableNullReference(local,
330: reference);
331: return;
332: }
333: if (flowInfo.isPotentiallyNull(local)) {
334: scope.problemReporter()
335: .localVariablePotentialNullReference(
336: local, reference);
337: return;
338: }
339: if (flowInfo.isDefinitelyNonNull(local)) {
340: return; // shortcut: cannot be null
341: }
342: break;
343: default:
344: // never happens
345: }
346: }
347: recordNullReference(local, reference, checkType);
348: // prepare to re-check with try/catch flow info
349: }
350: }
351:
352: void removeFinalAssignmentIfAny(Reference reference) {
353: for (int i = 0; i < assignCount; i++) {
354: if (finalAssignments[i] == reference) {
355: finalAssignments[i] = null;
356: finalVariables[i] = null;
357: return;
358: }
359: }
360: }
361:
362: protected void recordNullReference(LocalVariableBinding local,
363: Expression expression, int status) {
364: if (this .nullCount == 0) {
365: this .nullLocals = new LocalVariableBinding[5];
366: this .nullReferences = new Expression[5];
367: this .nullCheckTypes = new int[5];
368: } else if (this .nullCount == this .nullLocals.length) {
369: int newLength = this .nullCount * 2;
370: System
371: .arraycopy(
372: this .nullLocals,
373: 0,
374: this .nullLocals = new LocalVariableBinding[newLength],
375: 0, this .nullCount);
376: System.arraycopy(this .nullReferences, 0,
377: this .nullReferences = new Expression[newLength], 0,
378: this .nullCount);
379: System.arraycopy(this .nullCheckTypes, 0,
380: this .nullCheckTypes = new int[newLength], 0,
381: this.nullCount);
382: }
383: this.nullLocals[this.nullCount] = local;
384: this.nullReferences[this.nullCount] = expression;
385: this.nullCheckTypes[this.nullCount++] = status;
386: }
387: }
|