1 /*******************************************************************************
2  * Copyright (c) 2000, 2003 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
7  *
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  *******************************************************************************/
11 package net.sourceforge.phpdt.internal.compiler.lookup;
12 
13 import net.sourceforge.phpdt.core.compiler.CharOperation;
14 import net.sourceforge.phpdt.internal.compiler.ast.MethodDeclaration;
15 import net.sourceforge.phpdt.internal.compiler.ast.TypeDeclaration;
16 import net.sourceforge.phpdt.internal.compiler.problem.ProblemReporter;
17 import net.sourceforge.phpdt.internal.compiler.util.HashtableOfObject;
18 
19 public final class MethodVerifier implements TagBits, TypeConstants {
20 	SourceTypeBinding type;
21 
22 	HashtableOfObject inheritedMethods;
23 
24 	HashtableOfObject currentMethods;
25 
26 	ReferenceBinding runtimeException;
27 
28 	ReferenceBinding errorException;
29 
30 	LookupEnvironment environment;
31 
32 	/*
33 	 * Binding creation is responsible for reporting all problems with types: -
34 	 * all modifier problems (duplicates & multiple visibility modifiers +
35 	 * incompatible combinations - abstract/final) - plus invalid modifiers
36 	 * given the context (the verifier did not do this before) - qualified name
37 	 * collisions between a type and a package (types in default packages are
38 	 * excluded) - all type hierarchy problems: - cycles in the superclass or
39 	 * superinterface hierarchy - an ambiguous, invisible or missing superclass
40 	 * or superinterface - extending a final class - extending an interface
41 	 * instead of a class - implementing a class instead of an interface -
42 	 * implementing the same interface more than once (ie. duplicate interfaces) -
43 	 * with nested types: - shadowing an enclosing type's source name - defining
44 	 * a static class or interface inside a non-static nested class - defining
45 	 * an interface as a local type (local types can only be classes)
46 	 */
MethodVerifier(LookupEnvironment environment)47 	public MethodVerifier(LookupEnvironment environment) {
48 		this.type = null; // Initialized with the public method
49 							// verify(SourceTypeBinding)
50 		this.inheritedMethods = null;
51 		this.currentMethods = null;
52 		this.runtimeException = null;
53 		this.errorException = null;
54 		this.environment = environment;
55 	}
56 
checkAgainstInheritedMethods(MethodBinding currentMethod, MethodBinding[] methods, int length)57 	private void checkAgainstInheritedMethods(MethodBinding currentMethod,
58 			MethodBinding[] methods, int length) {
59 		currentMethod.modifiers |= CompilerModifiers.AccOverriding;
60 		for (int i = length; --i >= 0;) {
61 			MethodBinding inheritedMethod = methods[i];
62 			if (!currentMethod.isAbstract() && inheritedMethod.isAbstract())
63 				currentMethod.modifiers |= CompilerModifiers.AccImplementing;
64 
65 			if (currentMethod.returnType != inheritedMethod.returnType) {
66 				this.problemReporter(currentMethod).incompatibleReturnType(
67 						currentMethod, inheritedMethod);
68 			} else if (currentMethod.isStatic() != inheritedMethod.isStatic()) { // Cannot
69 																					// override
70 																					// a
71 																					// static
72 																					// method
73 																					// or
74 																					// hide
75 																					// an
76 																					// instance
77 																					// method
78 				this.problemReporter(currentMethod).staticAndInstanceConflict(
79 						currentMethod, inheritedMethod);
80 			} else {
81 				if (currentMethod.thrownExceptions != NoExceptions)
82 					this.checkExceptions(currentMethod, inheritedMethod);
83 				if (inheritedMethod.isFinal())
84 					this.problemReporter(currentMethod)
85 							.finalMethodCannotBeOverridden(currentMethod,
86 									inheritedMethod);
87 				if (!this.isAsVisible(currentMethod, inheritedMethod))
88 					this.problemReporter(currentMethod).visibilityConflict(
89 							currentMethod, inheritedMethod);
90 				// if (inheritedMethod.isViewedAsDeprecated())
91 				// if (!currentMethod.isViewedAsDeprecated() ||
92 				// environment.options.reportDeprecationInsideDeprecatedCode)
93 				// this.problemReporter(currentMethod).overridesDeprecatedMethod(currentMethod,
94 				// inheritedMethod);
95 			}
96 		}
97 	}
98 
99 	/*
100 	 * "8.4.4" Verify that newExceptions are all included in
101 	 * inheritedExceptions. Assumes all exceptions are valid and throwable.
102 	 * Unchecked exceptions (compatible with runtime & error) are ignored (see
103 	 * the spec on pg. 203).
104 	 */
checkExceptions(MethodBinding newMethod, MethodBinding inheritedMethod)105 	private void checkExceptions(MethodBinding newMethod,
106 			MethodBinding inheritedMethod) {
107 		ReferenceBinding[] newExceptions = newMethod.thrownExceptions;
108 		ReferenceBinding[] inheritedExceptions = inheritedMethod.thrownExceptions;
109 		for (int i = newExceptions.length; --i >= 0;) {
110 			ReferenceBinding newException = newExceptions[i];
111 			int j = inheritedExceptions.length;
112 			while (--j > -1
113 					&& !this.isSameClassOrSubclassOf(newException,
114 							inheritedExceptions[j]))
115 				;
116 			if (j == -1)
117 				if (!(newException.isCompatibleWith(this.runtimeException()) || newException
118 						.isCompatibleWith(this.errorException())))
119 					this.problemReporter(newMethod)
120 							.incompatibleExceptionInThrowsClause(this.type,
121 									newMethod, inheritedMethod, newException);
122 		}
123 	}
124 
checkInheritedMethods(MethodBinding[] methods, int length)125 	private void checkInheritedMethods(MethodBinding[] methods, int length) {
126 		TypeBinding returnType = methods[0].returnType;
127 		int index = length;
128 		while (--index > 0 && returnType == methods[index].returnType)
129 			;
130 		if (index > 0) { // All inherited methods do NOT have the same
131 							// vmSignature
132 			this.problemReporter().inheritedMethodsHaveIncompatibleReturnTypes(
133 					this.type, methods, length);
134 			return;
135 		}
136 
137 		MethodBinding concreteMethod = null;
138 		if (!type.isInterface()) { // ignore concrete methods for interfaces
139 			for (int i = length; --i >= 0;) { // Remember that only one of the
140 												// methods can be non-abstract
141 				if (!methods[i].isAbstract()) {
142 					concreteMethod = methods[i];
143 					break;
144 				}
145 			}
146 		}
147 		if (concreteMethod == null) {
148 			if (this.type.isClass() && !this.type.isAbstract()) {
149 				for (int i = length; --i >= 0;)
150 					if (!mustImplementAbstractMethod(methods[i]))
151 						return; // have already reported problem against the
152 								// concrete superclass
153 
154 				TypeDeclaration typeDeclaration = this.type.scope.referenceContext;
155 				if (typeDeclaration != null) {
156 					MethodDeclaration missingAbstractMethod = typeDeclaration
157 							.addMissingAbstractMethodFor(methods[0]);
158 					missingAbstractMethod.scope.problemReporter()
159 							.abstractMethodMustBeImplemented(this.type,
160 									methods[0]);
161 				} else {
162 					this.problemReporter().abstractMethodMustBeImplemented(
163 							this.type, methods[0]);
164 				}
165 			}
166 			return;
167 		}
168 
169 		MethodBinding[] abstractMethods = new MethodBinding[length - 1];
170 		index = 0;
171 		for (int i = length; --i >= 0;)
172 			if (methods[i] != concreteMethod)
173 				abstractMethods[index++] = methods[i];
174 
175 		// Remember that interfaces can only define public instance methods
176 		if (concreteMethod.isStatic())
177 			// Cannot inherit a static method which is specified as an instance
178 			// method by an interface
179 			this.problemReporter().staticInheritedMethodConflicts(type,
180 					concreteMethod, abstractMethods);
181 		if (!concreteMethod.isPublic())
182 			// Cannot reduce visibility of a public method specified by an
183 			// interface
184 			this.problemReporter().inheritedMethodReducesVisibility(type,
185 					concreteMethod, abstractMethods);
186 		if (concreteMethod.thrownExceptions != NoExceptions)
187 			for (int i = abstractMethods.length; --i >= 0;)
188 				this.checkExceptions(concreteMethod, abstractMethods[i]);
189 	}
190 
191 	/*
192 	 * For each inherited method identifier (message pattern - vm signature
193 	 * minus the return type) if current method exists if current's vm signature
194 	 * does not match an inherited signature then complain else compare
195 	 * current's exceptions & visibility against each inherited method else if
196 	 * inherited methods = 1 if inherited is abstract && type is NOT an
197 	 * interface or abstract, complain else if vm signatures do not match
198 	 * complain else find the concrete implementation amongst the abstract
199 	 * methods (can only be 1) if one exists then it must be a public instance
200 	 * method compare concrete's exceptions against each abstract method else
201 	 * complain about missing implementation only if type is NOT an interface or
202 	 * abstract
203 	 */
checkMethods()204 	private void checkMethods() {
205 		boolean mustImplementAbstractMethods = this.type.isClass()
206 				&& !this.type.isAbstract();
207 		char[][] methodSelectors = this.inheritedMethods.keyTable;
208 		for (int s = methodSelectors.length; --s >= 0;) {
209 			if (methodSelectors[s] != null) {
210 				MethodBinding[] current = (MethodBinding[]) this.currentMethods
211 						.get(methodSelectors[s]);
212 				MethodBinding[] inherited = (MethodBinding[]) this.inheritedMethods.valueTable[s];
213 
214 				int index = -1;
215 				MethodBinding[] matchingInherited = new MethodBinding[inherited.length];
216 				if (current != null) {
217 					for (int i = 0, length1 = current.length; i < length1; i++) {
218 						while (index >= 0)
219 							matchingInherited[index--] = null; // clear the
220 																// previous
221 																// contents of
222 																// the matching
223 																// methods
224 						MethodBinding currentMethod = current[i];
225 						for (int j = 0, length2 = inherited.length; j < length2; j++) {
226 							if (inherited[j] != null
227 									&& currentMethod
228 											.areParametersEqual(inherited[j])) {
229 								matchingInherited[++index] = inherited[j];
230 								inherited[j] = null; // do not want to find
231 														// it again
232 							}
233 						}
234 						if (index >= 0)
235 							this.checkAgainstInheritedMethods(currentMethod,
236 									matchingInherited, index + 1); // pass in
237 																	// the
238 																	// length of
239 																	// matching
240 					}
241 				}
242 				for (int i = 0, length = inherited.length; i < length; i++) {
243 					while (index >= 0)
244 						matchingInherited[index--] = null; // clear the
245 															// previous contents
246 															// of the matching
247 															// methods
248 					if (inherited[i] != null) {
249 						matchingInherited[++index] = inherited[i];
250 						for (int j = i + 1; j < length; j++) {
251 							if (inherited[j] != null
252 									&& inherited[i]
253 											.areParametersEqual(inherited[j])) {
254 								matchingInherited[++index] = inherited[j];
255 								inherited[j] = null; // do not want to find
256 														// it again
257 							}
258 						}
259 					}
260 					if (index > 0) {
261 						this
262 								.checkInheritedMethods(matchingInherited,
263 										index + 1); // pass in the length of
264 													// matching
265 					} else if (mustImplementAbstractMethods && index == 0
266 							&& matchingInherited[0].isAbstract()) {
267 						if (mustImplementAbstractMethod(matchingInherited[0])) {
268 							TypeDeclaration typeDeclaration = this.type.scope.referenceContext;
269 							if (typeDeclaration != null) {
270 								MethodDeclaration missingAbstractMethod = typeDeclaration
271 										.addMissingAbstractMethodFor(matchingInherited[0]);
272 								missingAbstractMethod.scope
273 										.problemReporter()
274 										.abstractMethodMustBeImplemented(
275 												this.type, matchingInherited[0]);
276 							} else {
277 								this
278 										.problemReporter()
279 										.abstractMethodMustBeImplemented(
280 												this.type, matchingInherited[0]);
281 							}
282 						}
283 					}
284 				}
285 			}
286 		}
287 	}
288 
checkPackagePrivateAbstractMethod(MethodBinding abstractMethod)289 	private void checkPackagePrivateAbstractMethod(MethodBinding abstractMethod) {
290 		ReferenceBinding superType = this.type.superclass();
291 		char[] selector = abstractMethod.selector;
292 		do {
293 			if (!superType.isValidBinding())
294 				return;
295 			if (!superType.isAbstract())
296 				return; // closer non abstract super type will be flagged
297 						// instead
298 
299 			MethodBinding[] methods = superType.getMethods(selector);
300 			nextMethod: for (int m = methods.length; --m >= 0;) {
301 				MethodBinding method = methods[m];
302 				if (method.returnType != abstractMethod.returnType
303 						|| !method.areParametersEqual(abstractMethod))
304 					continue nextMethod;
305 				if (method.isPrivate() || method.isConstructor()
306 						|| method.isDefaultAbstract())
307 					continue nextMethod;
308 				if (superType.fPackage == abstractMethod.declaringClass.fPackage)
309 					return; // found concrete implementation of abstract method
310 							// in same package
311 			}
312 		} while ((superType = superType.superclass()) != abstractMethod.declaringClass);
313 
314 		// non visible abstract methods cannot be overridden so the type must be
315 		// defined abstract
316 		this.problemReporter().abstractMethodCannotBeOverridden(this.type,
317 				abstractMethod);
318 	}
319 
320 	/*
321 	 * Binding creation is responsible for reporting: - all modifier problems
322 	 * (duplicates & multiple visibility modifiers + incompatible combinations) -
323 	 * plus invalid modifiers given the context... examples: - interface methods
324 	 * can only be public - abstract methods can only be defined by abstract
325 	 * classes - collisions... 2 methods with identical vmSelectors - multiple
326 	 * methods with the same message pattern but different return types -
327 	 * ambiguous, invisible or missing return/argument/exception types - check
328 	 * the type of any array is not void - check that each exception type is
329 	 * Throwable or a subclass of it
330 	 */
computeInheritedMethods()331 	private void computeInheritedMethods() {
332 		this.inheritedMethods = new HashtableOfObject(51); // maps method
333 															// selectors to an
334 															// array of
335 															// methods... must
336 															// search to match
337 															// paramaters &
338 															// return type
339 		ReferenceBinding[][] interfacesToVisit = new ReferenceBinding[5][];
340 		int lastPosition = 0;
341 		interfacesToVisit[lastPosition] = type.superInterfaces();
342 
343 		ReferenceBinding superType = this.type.isClass() ? this.type
344 				.superclass() : this.type.scope.getJavaLangObject(); // check
345 																		// interface
346 																		// methods
347 																		// against
348 																		// Object
349 		MethodBinding[] nonVisibleDefaultMethods = null;
350 		int nonVisibleCount = 0;
351 
352 		while (superType != null) {
353 			if (superType.isValidBinding()) {
354 				ReferenceBinding[] itsInterfaces = superType.superInterfaces();
355 				if (itsInterfaces != NoSuperInterfaces) {
356 					if (++lastPosition == interfacesToVisit.length)
357 						System
358 								.arraycopy(
359 										interfacesToVisit,
360 										0,
361 										interfacesToVisit = new ReferenceBinding[lastPosition * 2][],
362 										0, lastPosition);
363 					interfacesToVisit[lastPosition] = itsInterfaces;
364 				}
365 
366 				MethodBinding[] methods = superType.methods();
367 				nextMethod: for (int m = methods.length; --m >= 0;) {
368 					MethodBinding method = methods[m];
369 					if (!(method.isPrivate() || method.isConstructor() || method
370 							.isDefaultAbstract())) { // look at all methods
371 														// which are NOT private
372 														// or constructors or
373 														// default abstract
374 						MethodBinding[] existingMethods = (MethodBinding[]) this.inheritedMethods
375 								.get(method.selector);
376 						if (existingMethods != null) {
377 							for (int i = 0, length = existingMethods.length; i < length; i++) {
378 								if (method.returnType == existingMethods[i].returnType
379 										&& method
380 												.areParametersEqual(existingMethods[i])) {
381 									if (method.isDefault()
382 											&& method.isAbstract()
383 											&& method.declaringClass.fPackage != type.fPackage)
384 										checkPackagePrivateAbstractMethod(method);
385 									continue nextMethod;
386 								}
387 							}
388 						}
389 						if (nonVisibleDefaultMethods != null)
390 							for (int i = 0; i < nonVisibleCount; i++)
391 								if (method.returnType == nonVisibleDefaultMethods[i].returnType
392 										&& CharOperation
393 												.equals(
394 														method.selector,
395 														nonVisibleDefaultMethods[i].selector)
396 										&& method
397 												.areParametersEqual(nonVisibleDefaultMethods[i]))
398 									continue nextMethod;
399 
400 						if (!(method.isDefault() && method.declaringClass.fPackage != type.fPackage)) { // ignore
401 																										// methods
402 																										// which
403 																										// have
404 																										// default
405 																										// visibility
406 																										// and
407 																										// are
408 																										// NOT
409 																										// defined
410 																										// in
411 																										// another
412 																										// package
413 							if (existingMethods == null)
414 								existingMethods = new MethodBinding[1];
415 							else
416 								System
417 										.arraycopy(
418 												existingMethods,
419 												0,
420 												(existingMethods = new MethodBinding[existingMethods.length + 1]),
421 												0, existingMethods.length - 1);
422 							existingMethods[existingMethods.length - 1] = method;
423 							this.inheritedMethods.put(method.selector,
424 									existingMethods);
425 						} else {
426 							if (nonVisibleDefaultMethods == null)
427 								nonVisibleDefaultMethods = new MethodBinding[10];
428 							else if (nonVisibleCount == nonVisibleDefaultMethods.length)
429 								System
430 										.arraycopy(
431 												nonVisibleDefaultMethods,
432 												0,
433 												(nonVisibleDefaultMethods = new MethodBinding[nonVisibleCount * 2]),
434 												0, nonVisibleCount);
435 							nonVisibleDefaultMethods[nonVisibleCount++] = method;
436 
437 							if (method.isAbstract() && !this.type.isAbstract()) // non
438 																				// visible
439 																				// abstract
440 																				// methods
441 																				// cannot
442 																				// be
443 																				// overridden
444 																				// so
445 																				// the
446 																				// type
447 																				// must
448 																				// be
449 																				// defined
450 																				// abstract
451 								this.problemReporter()
452 										.abstractMethodCannotBeOverridden(
453 												this.type, method);
454 
455 							MethodBinding[] current = (MethodBinding[]) this.currentMethods
456 									.get(method.selector);
457 							if (current != null) { // non visible methods
458 													// cannot be overridden so a
459 													// warning is issued
460 								foundMatch: for (int i = 0, length = current.length; i < length; i++) {
461 									if (method.returnType == current[i].returnType
462 											&& method
463 													.areParametersEqual(current[i])) {
464 										this.problemReporter()
465 												.overridesPackageDefaultMethod(
466 														current[i], method);
467 										break foundMatch;
468 									}
469 								}
470 							}
471 						}
472 					}
473 				}
474 				superType = superType.superclass();
475 			}
476 		}
477 
478 		for (int i = 0; i <= lastPosition; i++) {
479 			ReferenceBinding[] interfaces = interfacesToVisit[i];
480 			if (interfaces == null) {
481 				interfaces = new ReferenceBinding[0];
482 			}
483 			for (int j = 0, length = interfaces.length; j < length; j++) {
484 				superType = interfaces[j];
485 				if ((superType.tagBits & InterfaceVisited) == 0) {
486 					superType.tagBits |= InterfaceVisited;
487 					if (superType.isValidBinding()) {
488 						ReferenceBinding[] itsInterfaces = superType
489 								.superInterfaces();
490 						if (itsInterfaces != NoSuperInterfaces) {
491 							if (++lastPosition == interfacesToVisit.length)
492 								System
493 										.arraycopy(
494 												interfacesToVisit,
495 												0,
496 												interfacesToVisit = new ReferenceBinding[lastPosition * 2][],
497 												0, lastPosition);
498 							interfacesToVisit[lastPosition] = itsInterfaces;
499 						}
500 
501 						MethodBinding[] methods = superType.methods();
502 						for (int m = methods.length; --m >= 0;) { // Interface
503 																	// methods
504 																	// are all
505 																	// abstract
506 																	// public
507 							MethodBinding method = methods[m];
508 							MethodBinding[] existingMethods = (MethodBinding[]) this.inheritedMethods
509 									.get(method.selector);
510 							if (existingMethods == null)
511 								existingMethods = new MethodBinding[1];
512 							else
513 								System
514 										.arraycopy(
515 												existingMethods,
516 												0,
517 												(existingMethods = new MethodBinding[existingMethods.length + 1]),
518 												0, existingMethods.length - 1);
519 							existingMethods[existingMethods.length - 1] = method;
520 							this.inheritedMethods.put(method.selector,
521 									existingMethods);
522 						}
523 					}
524 				}
525 			}
526 		}
527 
528 		// bit reinitialization
529 		for (int i = 0; i <= lastPosition; i++) {
530 			ReferenceBinding[] interfaces = interfacesToVisit[i];
531 			if (interfaces == null) {
532 				interfaces = new ReferenceBinding[0];
533 			}
534 			for (int j = 0, length = interfaces.length; j < length; j++)
535 				interfaces[j].tagBits &= ~InterfaceVisited;
536 		}
537 	}
538 
computeMethods()539 	private void computeMethods() {
540 		MethodBinding[] methods = type.methods();
541 		if (methods == null) {
542 			methods = new MethodBinding[0];
543 		}
544 		int size = methods.length;
545 		this.currentMethods = new HashtableOfObject(size == 0 ? 1 : size); // maps
546 																			// method
547 																			// selectors
548 																			// to
549 																			// an
550 																			// array
551 																			// of
552 																			// methods...
553 																			// must
554 																			// search
555 																			// to
556 																			// match
557 																			// paramaters
558 																			// &
559 																			// return
560 																			// type
561 		for (int m = size; --m >= 0;) {
562 			MethodBinding method = methods[m];
563 			if (!(method.isConstructor() || method.isDefaultAbstract())) { // keep
564 																			// all
565 																			// methods
566 																			// which
567 																			// are
568 																			// NOT
569 																			// constructors
570 																			// or
571 																			// default
572 																			// abstract
573 				MethodBinding[] existingMethods = (MethodBinding[]) this.currentMethods
574 						.get(method.selector);
575 				if (existingMethods == null)
576 					existingMethods = new MethodBinding[1];
577 				else
578 					System
579 							.arraycopy(
580 									existingMethods,
581 									0,
582 									(existingMethods = new MethodBinding[existingMethods.length + 1]),
583 									0, existingMethods.length - 1);
584 				existingMethods[existingMethods.length - 1] = method;
585 				this.currentMethods.put(method.selector, existingMethods);
586 			}
587 		}
588 	}
589 
errorException()590 	private ReferenceBinding errorException() {
591 		if (errorException == null)
592 			this.errorException = this.type.scope.getJavaLangError();
593 		return errorException;
594 	}
595 
isAsVisible(MethodBinding newMethod, MethodBinding inheritedMethod)596 	private boolean isAsVisible(MethodBinding newMethod,
597 			MethodBinding inheritedMethod) {
598 		if (inheritedMethod.modifiers == newMethod.modifiers)
599 			return true;
600 
601 		if (newMethod.isPublic())
602 			return true; // Covers everything
603 		if (inheritedMethod.isPublic())
604 			return false;
605 
606 		if (newMethod.isProtected())
607 			return true;
608 		if (inheritedMethod.isProtected())
609 			return false;
610 
611 		return !newMethod.isPrivate(); // The inheritedMethod cannot be private
612 										// since it would not be visible
613 	}
614 
isSameClassOrSubclassOf(ReferenceBinding testClass, ReferenceBinding superclass)615 	private boolean isSameClassOrSubclassOf(ReferenceBinding testClass,
616 			ReferenceBinding superclass) {
617 		do {
618 			if (testClass == superclass)
619 				return true;
620 		} while ((testClass = testClass.superclass()) != null);
621 		return false;
622 	}
623 
mustImplementAbstractMethod(MethodBinding abstractMethod)624 	private boolean mustImplementAbstractMethod(MethodBinding abstractMethod) {
625 		// if the type's superclass is an abstract class, then all abstract
626 		// methods must be implemented
627 		// otherwise, skip it if the type's superclass must implement any of the
628 		// inherited methods
629 		ReferenceBinding superclass = this.type.superclass();
630 		ReferenceBinding declaringClass = abstractMethod.declaringClass;
631 		if (declaringClass.isClass()) {
632 			while (superclass.isAbstract() && superclass != declaringClass)
633 				superclass = superclass.superclass(); // find the first
634 														// concrete superclass
635 														// or the abstract
636 														// declaringClass
637 		} else {
638 			if (this.type.implementsInterface(declaringClass, false)) {
639 				if (this.type.isAbstract())
640 					return false; // leave it for the subclasses
641 				if (!superclass.implementsInterface(declaringClass, true)) // only
642 																			// if a
643 																			// superclass
644 																			// does
645 																			// not
646 																			// also
647 																			// implement
648 																			// the
649 																			// interface
650 					return true;
651 			}
652 			while (superclass.isAbstract()
653 					&& !superclass.implementsInterface(declaringClass, false))
654 				superclass = superclass.superclass(); // find the first
655 														// concrete superclass
656 														// or the superclass
657 														// which implements the
658 														// interface
659 		}
660 		return superclass.isAbstract(); // if it is a concrete class then we
661 										// have already reported problem against
662 										// it
663 	}
664 
problemReporter()665 	private ProblemReporter problemReporter() {
666 		return this.type.scope.problemReporter();
667 	}
668 
problemReporter(MethodBinding currentMethod)669 	private ProblemReporter problemReporter(MethodBinding currentMethod) {
670 		ProblemReporter reporter = problemReporter();
671 		if (currentMethod.declaringClass == type) // only report against the
672 													// currentMethod if its
673 													// implemented by the type
674 			reporter.referenceContext = currentMethod.sourceMethod();
675 		return reporter;
676 	}
677 
runtimeException()678 	private ReferenceBinding runtimeException() {
679 		if (runtimeException == null)
680 			this.runtimeException = this.type.scope
681 					.getJavaLangRuntimeException();
682 		return runtimeException;
683 	}
684 
verify(SourceTypeBinding type)685 	public void verify(SourceTypeBinding type) {
686 		this.type = type;
687 		this.computeMethods();
688 		this.computeInheritedMethods();
689 		this.checkMethods();
690 	}
691 
toString()692 	public String toString() {
693 		StringBuffer buffer = new StringBuffer(10);
694 		buffer.append("MethodVerifier for type: "); //$NON-NLS-1$
695 		buffer.append(type.readableName());
696 		buffer.append('\n');
697 		buffer.append("\t-inherited methods: "); //$NON-NLS-1$
698 		buffer.append(this.inheritedMethods);
699 		return buffer.toString();
700 	}
701 }
702