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 *******************************************************************************/
11 package org.eclipse.jdt.internal.compiler.parser;
13 import org.eclipse.jdt.core.compiler.*;
14 import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
15 import org.eclipse.jdt.internal.compiler.ast.Argument;
16 import org.eclipse.jdt.internal.compiler.ast.ASTNode;
17 import org.eclipse.jdt.internal.compiler.ast.Block;
18 import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration;
19 import org.eclipse.jdt.internal.compiler.ast.ExplicitConstructorCall;
20 import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
21 import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration;
22 import org.eclipse.jdt.internal.compiler.ast.Statement;
23 import org.eclipse.jdt.internal.compiler.ast.SuperReference;
24 import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
25 import org.eclipse.jdt.internal.compiler.ast.TypeReference;
26 import org.eclipse.jdt.internal.compiler.lookup.BaseTypes;
27 import org.eclipse.jdt.internal.compiler.lookup.CompilerModifiers;
30 * Internal method structure for parsing recovery
33 public class RecoveredMethod extends RecoveredElement implements CompilerModifiers, TerminalTokens, BaseTypes {
35 public AbstractMethodDeclaration methodDeclaration;
37 public RecoveredType[] localTypes;
38 public int localTypeCount;
40 public RecoveredBlock methodBody;
41 public boolean discardBody = true;
43 public RecoveredMethod(AbstractMethodDeclaration methodDeclaration, RecoveredElement parent, int bracketBalance, Parser parser){
44 super(parent, bracketBalance, parser);
45 this.methodDeclaration = methodDeclaration;
46 this.foundOpeningBrace = !bodyStartsAtHeaderEnd();
47 if(this.foundOpeningBrace) {
48 this.bracketBalance++;
52 * Record a nested block declaration
54 public RecoveredElement add(Block nestedBlockDeclaration, int bracketBalanceValue) {
56 /* default behavior is to delegate recording to parent if any,
57 do not consider elements passed the known end (if set)
58 it must be belonging to an enclosing element
60 if (methodDeclaration.declarationSourceEnd > 0
61 && nestedBlockDeclaration.sourceStart
62 > methodDeclaration.declarationSourceEnd){
63 if (this.parent == null){
64 return this; // ignore
66 return this.parent.add(nestedBlockDeclaration, bracketBalanceValue);
69 /* consider that if the opening brace was not found, it is there */
70 if (!foundOpeningBrace){
71 foundOpeningBrace = true;
72 this.bracketBalance++;
75 methodBody = new RecoveredBlock(nestedBlockDeclaration, this, bracketBalanceValue);
76 if (nestedBlockDeclaration.sourceEnd == 0) return methodBody;
80 * Record a field declaration
82 public RecoveredElement add(FieldDeclaration fieldDeclaration, int bracketBalanceValue) {
84 /* local variables inside method can only be final and non void */
85 char[][] fieldTypeName;
86 if ((fieldDeclaration.modifiers & ~AccFinal) != 0 // local var can only be final
87 || (fieldDeclaration.type == null) // initializer
88 || ((fieldTypeName = fieldDeclaration.type.getTypeName()).length == 1 // non void
89 && CharOperation.equals(fieldTypeName[0], VoidBinding.sourceName()))){
91 if (this.parent == null){
92 return this; // ignore
94 this.updateSourceEndIfNecessary(this.previousAvailableLineEnd(fieldDeclaration.declarationSourceStart - 1));
95 return this.parent.add(fieldDeclaration, bracketBalanceValue);
98 /* default behavior is to delegate recording to parent if any,
99 do not consider elements passed the known end (if set)
100 it must be belonging to an enclosing element
102 if (methodDeclaration.declarationSourceEnd > 0
103 && fieldDeclaration.declarationSourceStart
104 > methodDeclaration.declarationSourceEnd){
105 if (this.parent == null){
106 return this; // ignore
108 return this.parent.add(fieldDeclaration, bracketBalanceValue);
111 /* consider that if the opening brace was not found, it is there */
112 if (!foundOpeningBrace){
113 foundOpeningBrace = true;
114 this.bracketBalance++;
116 // still inside method, treat as local variable
117 return this; // ignore
120 * Record a local declaration - regular method should have been created a block body
122 public RecoveredElement add(LocalDeclaration localDeclaration, int bracketBalanceValue) {
124 /* local variables inside method can only be final and non void */
126 char[][] localTypeName;
127 if ((localDeclaration.modifiers & ~AccFinal) != 0 // local var can only be final
128 || (localDeclaration.type == null) // initializer
129 || ((localTypeName = localDeclaration.type.getTypeName()).length == 1 // non void
130 && CharOperation.equals(localTypeName[0], VoidBinding.sourceName()))){
132 if (this.parent == null){
133 return this; // ignore
135 this.updateSourceEndIfNecessary(this.previousAvailableLineEnd(localDeclaration.declarationSourceStart - 1));
136 return this.parent.add(localDeclaration, bracketBalance);
140 /* do not consider a type starting passed the type end (if set)
141 it must be belonging to an enclosing type */
142 if (methodDeclaration.declarationSourceEnd != 0
143 && localDeclaration.declarationSourceStart > methodDeclaration.declarationSourceEnd){
145 if (this.parent == null) {
146 return this; // ignore
148 return this.parent.add(localDeclaration, bracketBalanceValue);
151 if (methodBody == null){
152 Block block = new Block(0);
153 block.sourceStart = methodDeclaration.bodyStart;
154 RecoveredElement currentBlock = this.add(block, 1);
155 if (this.bracketBalance > 0){
156 for (int i = 0; i < this.bracketBalance - 1; i++){
157 currentBlock = currentBlock.add(new Block(0), 1);
159 this.bracketBalance = 1;
161 return currentBlock.add(localDeclaration, bracketBalanceValue);
163 return methodBody.add(localDeclaration, bracketBalanceValue, true);
166 * Record a statement - regular method should have been created a block body
168 public RecoveredElement add(Statement statement, int bracketBalanceValue) {
170 /* do not consider a type starting passed the type end (if set)
171 it must be belonging to an enclosing type */
172 if (methodDeclaration.declarationSourceEnd != 0
173 && statement.sourceStart > methodDeclaration.declarationSourceEnd){
175 if (this.parent == null) {
176 return this; // ignore
178 return this.parent.add(statement, bracketBalanceValue);
181 if (methodBody == null){
182 Block block = new Block(0);
183 block.sourceStart = methodDeclaration.bodyStart;
184 RecoveredElement currentBlock = this.add(block, 1);
185 if (this.bracketBalance > 0){
186 for (int i = 0; i < this.bracketBalance - 1; i++){
187 currentBlock = currentBlock.add(new Block(0), 1);
189 this.bracketBalance = 1;
191 return currentBlock.add(statement, bracketBalanceValue);
193 return methodBody.add(statement, bracketBalanceValue, true);
195 public RecoveredElement add(TypeDeclaration typeDeclaration, int bracketBalanceValue) {
197 /* do not consider a type starting passed the type end (if set)
198 it must be belonging to an enclosing type */
199 if (methodDeclaration.declarationSourceEnd != 0
200 && typeDeclaration.declarationSourceStart > methodDeclaration.declarationSourceEnd){
202 if (this.parent == null) {
203 return this; // ignore
205 return this.parent.add(typeDeclaration, bracketBalanceValue);
207 if ((typeDeclaration.bits & ASTNode.IsLocalTypeMASK) != 0){
208 if (methodBody == null){
209 Block block = new Block(0);
210 block.sourceStart = methodDeclaration.bodyStart;
213 return methodBody.add(typeDeclaration, bracketBalanceValue, true);
215 if (typeDeclaration.isInterface()) {
216 this.updateSourceEndIfNecessary(this.previousAvailableLineEnd(typeDeclaration.declarationSourceStart - 1));
217 if (this.parent == null) {
218 return this; // ignore
220 // close the constructor
221 return this.parent.add(typeDeclaration, bracketBalanceValue);
223 if (localTypes == null) {
224 localTypes = new RecoveredType[5];
227 if (localTypeCount == localTypes.length) {
231 (localTypes = new RecoveredType[2 * localTypeCount]),
236 RecoveredType element = new RecoveredType(typeDeclaration, this, bracketBalanceValue);
237 localTypes[localTypeCount++] = element;
239 /* consider that if the opening brace was not found, it is there */
240 if (!foundOpeningBrace){
241 foundOpeningBrace = true;
242 this.bracketBalance++;
246 public boolean bodyStartsAtHeaderEnd(){
247 return methodDeclaration.bodyStart == methodDeclaration.sourceEnd+1;
250 * Answer the associated parsed structure
252 public ASTNode parseTree(){
253 return methodDeclaration;
256 * Answer the very source end of the corresponding parse node
258 public int sourceEnd(){
259 return this.methodDeclaration.declarationSourceEnd;
261 public String toString(int tab) {
262 StringBuffer result = new StringBuffer(tabString(tab));
263 result.append("Recovered method:\n"); //$NON-NLS-1$
264 this.methodDeclaration.print(tab + 1, result);
265 if (this.localTypes != null) {
266 for (int i = 0; i < this.localTypeCount; i++) {
267 result.append("\n"); //$NON-NLS-1$
268 result.append(this.localTypes[i].toString(tab + 1));
271 if (this.methodBody != null) {
272 result.append("\n"); //$NON-NLS-1$
273 result.append(this.methodBody.toString(tab + 1));
275 return result.toString();
278 * Update the bodyStart of the corresponding parse node
280 public void updateBodyStart(int bodyStart){
281 this.foundOpeningBrace = true;
282 this.methodDeclaration.bodyStart = bodyStart;
284 public AbstractMethodDeclaration updatedMethodDeclaration(){
286 if (methodBody != null){
287 Block block = methodBody.updatedBlock();
289 methodDeclaration.statements = block.statements;
291 /* first statement might be an explict constructor call destinated to a special slot */
292 if (methodDeclaration.isConstructor()) {
293 ConstructorDeclaration constructor = (ConstructorDeclaration)methodDeclaration;
294 if (methodDeclaration.statements != null
295 && methodDeclaration.statements[0] instanceof ExplicitConstructorCall){
296 constructor.constructorCall = (ExplicitConstructorCall)methodDeclaration.statements[0];
297 int length = methodDeclaration.statements.length;
299 methodDeclaration.statements,
301 (methodDeclaration.statements = new Statement[length-1]),
305 if (constructor.constructorCall == null){ // add implicit constructor call
306 constructor.constructorCall = SuperReference.implicitSuperConstructorCall();
311 if (localTypeCount > 0) methodDeclaration.bits |= ASTNode.HasLocalTypeMASK;
312 return methodDeclaration;
315 * Update the corresponding parse node from parser state which
316 * is about to disappear because of restarting recovery
318 public void updateFromParserState(){
320 if(this.bodyStartsAtHeaderEnd()){
321 Parser parser = this.parser();
322 /* might want to recover arguments or thrown exceptions */
323 if (parser.listLength > 0 && parser.astLengthPtr > 0){ // awaiting interface type references
324 /* has consumed the arguments - listed elements must be thrown exceptions */
325 if (methodDeclaration.sourceEnd == parser.rParenPos) {
327 // protection for bugs 15142
328 int length = parser.astLengthStack[parser.astLengthPtr];
329 int astPtr = parser.astPtr - length;
330 boolean canConsume = astPtr >= 0;
332 if((!(parser.astStack[astPtr] instanceof AbstractMethodDeclaration))) {
335 for (int i = 1, max = length + 1; i < max; i++) {
336 if(!(parser.astStack[astPtr + i ] instanceof TypeReference)) {
342 parser.consumeMethodHeaderThrowsClause();
343 // will reset typeListLength to zero
344 // thus this check will only be performed on first errorCheck after void foo() throws X, Y,
346 parser.listLength = 0;
349 /* has not consumed arguments yet, listed elements must be arguments */
350 if (parser.currentToken == TokenNameLPAREN || parser.currentToken == TokenNameSEMICOLON){
351 /* if currentToken is parenthesis this last argument is a method/field signature */
352 parser.astLengthStack[parser.astLengthPtr] --;
354 parser.listLength --;
355 parser.currentToken = 0;
357 int argLength = parser.astLengthStack[parser.astLengthPtr];
358 int argStart = parser.astPtr - argLength + 1;
359 boolean needUpdateRParenPos = parser.rParenPos < parser.lParenPos; // 12387 : rParenPos will be used
360 // to compute bodyStart, and thus used to set next checkpoint.
362 for (count = 0; count < argLength; count++){
363 Argument argument = (Argument)parser.astStack[argStart+count];
364 /* cannot be an argument if non final */
365 char[][] argTypeName = argument.type.getTypeName();
366 if ((argument.modifiers & ~AccFinal) != 0
367 || (argTypeName.length == 1
368 && CharOperation.equals(argTypeName[0], VoidBinding.sourceName()))){
369 parser.astLengthStack[parser.astLengthPtr] = count;
370 parser.astPtr = argStart+count-1;
371 parser.listLength = count;
372 parser.currentToken = 0;
375 if (needUpdateRParenPos) parser.rParenPos = argument.sourceEnd + 1;
377 if (parser.listLength > 0 && parser.astLengthPtr > 0){
379 // protection for bugs 15142
380 int length = parser.astLengthStack[parser.astLengthPtr];
381 int astPtr = parser.astPtr - length;
382 boolean canConsume = astPtr >= 0;
384 if((!(parser.astStack[astPtr] instanceof AbstractMethodDeclaration))) {
387 for (int i = 1, max = length + 1; i < max; i++) {
388 if(!(parser.astStack[astPtr + i ] instanceof Argument)) {
394 parser.consumeMethodHeaderParameters();
395 /* fix-up positions, given they were updated against rParenPos, which did not get set */
396 if (parser.currentElement == this){ // parameter addition might have added an awaiting (no return type) method - see 1FVXQZ4 */
397 methodDeclaration.sourceEnd = methodDeclaration.arguments[methodDeclaration.arguments.length-1].sourceEnd;
398 methodDeclaration.bodyStart = methodDeclaration.sourceEnd+1;
399 parser.lastCheckPoint = methodDeclaration.bodyStart;
408 * An opening brace got consumed, might be the expected opening one of the current element,
409 * in which case the bodyStart is updated.
411 public RecoveredElement updateOnOpeningBrace(int braceStart, int braceEnd){
413 /* in case the opening brace is close enough to the signature */
414 if (bracketBalance == 0){
416 if (parser.scanner.searchLineNumber(methodDeclaration.sourceEnd)
417 != parser.scanner.searchLineNumber(braceEnd)){
419 switch(parser().lastIgnoredToken){
421 case TokenNamethrows :
424 this.foundOpeningBrace = true;
425 bracketBalance = 1; // pretend the brace was already there
428 return super.updateOnOpeningBrace(braceStart, braceEnd);
430 public void updateParseTree(){
431 this.updatedMethodDeclaration();
434 * Update the declarationSourceEnd of the corresponding parse node
436 public void updateSourceEndIfNecessary(int braceStart, int braceEnd){
437 if (this.methodDeclaration.declarationSourceEnd == 0) {
438 if(parser().rBraceSuccessorStart >= braceEnd) {
439 this.methodDeclaration.declarationSourceEnd = parser().rBraceEnd;
440 this.methodDeclaration.bodyEnd = parser().rBraceStart;
442 this.methodDeclaration.declarationSourceEnd = braceEnd;
443 this.methodDeclaration.bodyEnd = braceStart - 1;