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.env.IGenericType;
27 import org.eclipse.jdt.internal.compiler.lookup.BaseTypes;
28 import org.eclipse.jdt.internal.compiler.lookup.CompilerModifiers;
31 * Internal method structure for parsing recovery
34 public class RecoveredMethod extends RecoveredElement implements CompilerModifiers, TerminalTokens, BaseTypes {
36 public AbstractMethodDeclaration methodDeclaration;
38 public RecoveredType[] localTypes;
39 public int localTypeCount;
41 public RecoveredBlock methodBody;
42 public boolean discardBody = true;
44 public RecoveredMethod(AbstractMethodDeclaration methodDeclaration, RecoveredElement parent, int bracketBalance, Parser parser){
45 super(parent, bracketBalance, parser);
46 this.methodDeclaration = methodDeclaration;
47 this.foundOpeningBrace = !bodyStartsAtHeaderEnd();
48 if(this.foundOpeningBrace) {
49 this.bracketBalance++;
53 * Record a nested block declaration
55 public RecoveredElement add(Block nestedBlockDeclaration, int bracketBalanceValue) {
57 /* default behavior is to delegate recording to parent if any,
58 do not consider elements passed the known end (if set)
59 it must be belonging to an enclosing element
61 if (methodDeclaration.declarationSourceEnd > 0
62 && nestedBlockDeclaration.sourceStart
63 > methodDeclaration.declarationSourceEnd){
64 if (this.parent == null){
65 return this; // ignore
67 return this.parent.add(nestedBlockDeclaration, bracketBalanceValue);
70 /* consider that if the opening brace was not found, it is there */
71 if (!foundOpeningBrace){
72 foundOpeningBrace = true;
73 this.bracketBalance++;
76 methodBody = new RecoveredBlock(nestedBlockDeclaration, this, bracketBalanceValue);
77 if (nestedBlockDeclaration.sourceEnd == 0) return methodBody;
81 * Record a field declaration
83 public RecoveredElement add(FieldDeclaration fieldDeclaration, int bracketBalanceValue) {
85 /* local variables inside method can only be final and non void */
86 char[][] fieldTypeName;
87 if ((fieldDeclaration.modifiers & ~AccFinal) != 0 // local var can only be final
88 || (fieldDeclaration.type == null) // initializer
89 || ((fieldTypeName = fieldDeclaration.type.getTypeName()).length == 1 // non void
90 && CharOperation.equals(fieldTypeName[0], VoidBinding.sourceName()))){
92 if (this.parent == null){
93 return this; // ignore
95 this.updateSourceEndIfNecessary(this.previousAvailableLineEnd(fieldDeclaration.declarationSourceStart - 1));
96 return this.parent.add(fieldDeclaration, bracketBalanceValue);
99 /* default behavior is to delegate recording to parent if any,
100 do not consider elements passed the known end (if set)
101 it must be belonging to an enclosing element
103 if (methodDeclaration.declarationSourceEnd > 0
104 && fieldDeclaration.declarationSourceStart
105 > methodDeclaration.declarationSourceEnd){
106 if (this.parent == null){
107 return this; // ignore
109 return this.parent.add(fieldDeclaration, bracketBalanceValue);
112 /* consider that if the opening brace was not found, it is there */
113 if (!foundOpeningBrace){
114 foundOpeningBrace = true;
115 this.bracketBalance++;
117 // still inside method, treat as local variable
118 return this; // ignore
121 * Record a local declaration - regular method should have been created a block body
123 public RecoveredElement add(LocalDeclaration localDeclaration, int bracketBalanceValue) {
125 /* local variables inside method can only be final and non void */
127 char[][] localTypeName;
128 if ((localDeclaration.modifiers & ~AccFinal) != 0 // local var can only be final
129 || (localDeclaration.type == null) // initializer
130 || ((localTypeName = localDeclaration.type.getTypeName()).length == 1 // non void
131 && CharOperation.equals(localTypeName[0], VoidBinding.sourceName()))){
133 if (this.parent == null){
134 return this; // ignore
136 this.updateSourceEndIfNecessary(this.previousAvailableLineEnd(localDeclaration.declarationSourceStart - 1));
137 return this.parent.add(localDeclaration, bracketBalance);
141 /* do not consider a type starting passed the type end (if set)
142 it must be belonging to an enclosing type */
143 if (methodDeclaration.declarationSourceEnd != 0
144 && localDeclaration.declarationSourceStart > methodDeclaration.declarationSourceEnd){
146 if (this.parent == null) {
147 return this; // ignore
149 return this.parent.add(localDeclaration, bracketBalanceValue);
152 if (methodBody == null){
153 Block block = new Block(0);
154 block.sourceStart = methodDeclaration.bodyStart;
155 RecoveredElement currentBlock = this.add(block, 1);
156 if (this.bracketBalance > 0){
157 for (int i = 0; i < this.bracketBalance - 1; i++){
158 currentBlock = currentBlock.add(new Block(0), 1);
160 this.bracketBalance = 1;
162 return currentBlock.add(localDeclaration, bracketBalanceValue);
164 return methodBody.add(localDeclaration, bracketBalanceValue, true);
167 * Record a statement - regular method should have been created a block body
169 public RecoveredElement add(Statement statement, int bracketBalanceValue) {
171 /* do not consider a type starting passed the type end (if set)
172 it must be belonging to an enclosing type */
173 if (methodDeclaration.declarationSourceEnd != 0
174 && statement.sourceStart > methodDeclaration.declarationSourceEnd){
176 if (this.parent == null) {
177 return this; // ignore
179 return this.parent.add(statement, bracketBalanceValue);
182 if (methodBody == null){
183 Block block = new Block(0);
184 block.sourceStart = methodDeclaration.bodyStart;
185 RecoveredElement currentBlock = this.add(block, 1);
186 if (this.bracketBalance > 0){
187 for (int i = 0; i < this.bracketBalance - 1; i++){
188 currentBlock = currentBlock.add(new Block(0), 1);
190 this.bracketBalance = 1;
192 return currentBlock.add(statement, bracketBalanceValue);
194 return methodBody.add(statement, bracketBalanceValue, true);
196 public RecoveredElement add(TypeDeclaration typeDeclaration, int bracketBalanceValue) {
198 /* do not consider a type starting passed the type end (if set)
199 it must be belonging to an enclosing type */
200 if (methodDeclaration.declarationSourceEnd != 0
201 && typeDeclaration.declarationSourceStart > methodDeclaration.declarationSourceEnd){
203 if (this.parent == null) {
204 return this; // ignore
206 return this.parent.add(typeDeclaration, bracketBalanceValue);
208 if ((typeDeclaration.bits & ASTNode.IsLocalTypeMASK) != 0){
209 if (methodBody == null){
210 Block block = new Block(0);
211 block.sourceStart = methodDeclaration.bodyStart;
214 return methodBody.add(typeDeclaration, bracketBalanceValue, true);
216 if (typeDeclaration.kind() == IGenericType.INTERFACE_DECL) {
217 this.updateSourceEndIfNecessary(this.previousAvailableLineEnd(typeDeclaration.declarationSourceStart - 1));
218 if (this.parent == null) {
219 return this; // ignore
221 // close the constructor
222 return this.parent.add(typeDeclaration, bracketBalanceValue);
224 if (localTypes == null) {
225 localTypes = new RecoveredType[5];
228 if (localTypeCount == localTypes.length) {
232 (localTypes = new RecoveredType[2 * localTypeCount]),
237 RecoveredType element = new RecoveredType(typeDeclaration, this, bracketBalanceValue);
238 localTypes[localTypeCount++] = element;
240 /* consider that if the opening brace was not found, it is there */
241 if (!foundOpeningBrace){
242 foundOpeningBrace = true;
243 this.bracketBalance++;
247 public boolean bodyStartsAtHeaderEnd(){
248 return methodDeclaration.bodyStart == methodDeclaration.sourceEnd+1;
251 * Answer the associated parsed structure
253 public ASTNode parseTree(){
254 return methodDeclaration;
257 * Answer the very source end of the corresponding parse node
259 public int sourceEnd(){
260 return this.methodDeclaration.declarationSourceEnd;
262 public String toString(int tab) {
263 StringBuffer result = new StringBuffer(tabString(tab));
264 result.append("Recovered method:\n"); //$NON-NLS-1$
265 this.methodDeclaration.print(tab + 1, result);
266 if (this.localTypes != null) {
267 for (int i = 0; i < this.localTypeCount; i++) {
268 result.append("\n"); //$NON-NLS-1$
269 result.append(this.localTypes[i].toString(tab + 1));
272 if (this.methodBody != null) {
273 result.append("\n"); //$NON-NLS-1$
274 result.append(this.methodBody.toString(tab + 1));
276 return result.toString();
279 * Update the bodyStart of the corresponding parse node
281 public void updateBodyStart(int bodyStart){
282 this.foundOpeningBrace = true;
283 this.methodDeclaration.bodyStart = bodyStart;
285 public AbstractMethodDeclaration updatedMethodDeclaration(){
287 if (methodBody != null){
288 Block block = methodBody.updatedBlock();
290 methodDeclaration.statements = block.statements;
292 /* first statement might be an explict constructor call destinated to a special slot */
293 if (methodDeclaration.isConstructor()) {
294 ConstructorDeclaration constructor = (ConstructorDeclaration)methodDeclaration;
295 if (methodDeclaration.statements != null
296 && methodDeclaration.statements[0] instanceof ExplicitConstructorCall){
297 constructor.constructorCall = (ExplicitConstructorCall)methodDeclaration.statements[0];
298 int length = methodDeclaration.statements.length;
300 methodDeclaration.statements,
302 (methodDeclaration.statements = new Statement[length-1]),
306 if (constructor.constructorCall == null){ // add implicit constructor call
307 constructor.constructorCall = SuperReference.implicitSuperConstructorCall();
312 if (localTypeCount > 0) methodDeclaration.bits |= ASTNode.HasLocalTypeMASK;
313 return methodDeclaration;
316 * Update the corresponding parse node from parser state which
317 * is about to disappear because of restarting recovery
319 public void updateFromParserState(){
321 if(this.bodyStartsAtHeaderEnd()){
322 Parser parser = this.parser();
323 /* might want to recover arguments or thrown exceptions */
324 if (parser.listLength > 0 && parser.astLengthPtr > 0){ // awaiting interface type references
325 /* has consumed the arguments - listed elements must be thrown exceptions */
326 if (methodDeclaration.sourceEnd == parser.rParenPos) {
328 // protection for bugs 15142
329 int length = parser.astLengthStack[parser.astLengthPtr];
330 int astPtr = parser.astPtr - length;
331 boolean canConsume = astPtr >= 0;
333 if((!(parser.astStack[astPtr] instanceof AbstractMethodDeclaration))) {
336 for (int i = 1, max = length + 1; i < max; i++) {
337 if(!(parser.astStack[astPtr + i ] instanceof TypeReference)) {
343 parser.consumeMethodHeaderThrowsClause();
344 // will reset typeListLength to zero
345 // thus this check will only be performed on first errorCheck after void foo() throws X, Y,
347 parser.listLength = 0;
350 /* has not consumed arguments yet, listed elements must be arguments */
351 if (parser.currentToken == TokenNameLPAREN || parser.currentToken == TokenNameSEMICOLON){
352 /* if currentToken is parenthesis this last argument is a method/field signature */
353 parser.astLengthStack[parser.astLengthPtr] --;
355 parser.listLength --;
356 parser.currentToken = 0;
358 int argLength = parser.astLengthStack[parser.astLengthPtr];
359 int argStart = parser.astPtr - argLength + 1;
360 boolean needUpdateRParenPos = parser.rParenPos < parser.lParenPos; // 12387 : rParenPos will be used
361 // to compute bodyStart, and thus used to set next checkpoint.
363 for (count = 0; count < argLength; count++){
364 Argument argument = (Argument)parser.astStack[argStart+count];
365 /* cannot be an argument if non final */
366 char[][] argTypeName = argument.type.getTypeName();
367 if ((argument.modifiers & ~AccFinal) != 0
368 || (argTypeName.length == 1
369 && CharOperation.equals(argTypeName[0], VoidBinding.sourceName()))){
370 parser.astLengthStack[parser.astLengthPtr] = count;
371 parser.astPtr = argStart+count-1;
372 parser.listLength = count;
373 parser.currentToken = 0;
376 if (needUpdateRParenPos) parser.rParenPos = argument.sourceEnd + 1;
378 if (parser.listLength > 0 && parser.astLengthPtr > 0){
380 // protection for bugs 15142
381 int length = parser.astLengthStack[parser.astLengthPtr];
382 int astPtr = parser.astPtr - length;
383 boolean canConsume = astPtr >= 0;
385 if((!(parser.astStack[astPtr] instanceof AbstractMethodDeclaration))) {
388 for (int i = 1, max = length + 1; i < max; i++) {
389 if(!(parser.astStack[astPtr + i ] instanceof Argument)) {
395 parser.consumeMethodHeaderRightParen();
396 /* fix-up positions, given they were updated against rParenPos, which did not get set */
397 if (parser.currentElement == this){ // parameter addition might have added an awaiting (no return type) method - see 1FVXQZ4 */
398 methodDeclaration.sourceEnd = methodDeclaration.arguments[methodDeclaration.arguments.length-1].sourceEnd;
399 methodDeclaration.bodyStart = methodDeclaration.sourceEnd+1;
400 parser.lastCheckPoint = methodDeclaration.bodyStart;
408 public RecoveredElement updateOnClosingBrace(int braceStart, int braceEnd){
409 if(this.methodDeclaration.isAnnotationMethod()) {
410 this.updateSourceEndIfNecessary(braceStart, braceEnd);
411 if(!this.foundOpeningBrace && this.parent != null) {
412 return this.parent.updateOnClosingBrace(braceStart, braceEnd);
416 return super.updateOnClosingBrace(braceStart, braceEnd);
419 * An opening brace got consumed, might be the expected opening one of the current element,
420 * in which case the bodyStart is updated.
422 public RecoveredElement updateOnOpeningBrace(int braceStart, int braceEnd){
424 /* in case the opening brace is close enough to the signature */
425 if (bracketBalance == 0){
427 if (parser.scanner.searchLineNumber(methodDeclaration.sourceEnd)
428 != parser.scanner.searchLineNumber(braceEnd)){
430 switch(parser().lastIgnoredToken){
432 case TokenNamethrows :
435 this.foundOpeningBrace = true;
436 bracketBalance = 1; // pretend the brace was already there
439 return super.updateOnOpeningBrace(braceStart, braceEnd);
441 public void updateParseTree(){
442 this.updatedMethodDeclaration();
445 * Update the declarationSourceEnd of the corresponding parse node
447 public void updateSourceEndIfNecessary(int braceStart, int braceEnd){
448 if (this.methodDeclaration.declarationSourceEnd == 0) {
449 if(parser().rBraceSuccessorStart >= braceEnd) {
450 this.methodDeclaration.declarationSourceEnd = parser().rBraceEnd;
451 this.methodDeclaration.bodyEnd = parser().rBraceStart;
453 this.methodDeclaration.declarationSourceEnd = braceEnd;
454 this.methodDeclaration.bodyEnd = braceStart - 1;