1 /*******************************************************************************
2 * Copyright (c) 2006, 2008 IBM Corporation and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html
9 * IBM Corporation - initial API and implementation
10 *******************************************************************************/
12 package net.sourceforge.phpdt.core.dom;
14 import java.util.List;
15 import java.util.Vector;
17 import org.eclipse.jdt.core.compiler.CategorizedProblem;
18 import net.sourceforge.phpdt.core.compiler.CharOperation;
19 import net.sourceforge.phpdt.core.compiler.IProblem;
20 import org.eclipse.jdt.internal.compiler.parser.RecoveryScanner;
21 import org.eclipse.jdt.internal.compiler.parser.RecoveryScannerData;
22 import net.sourceforge.phpdt.internal.compiler.parser.TerminalTokens;
23 import org.eclipse.jdt.internal.compiler.util.HashtableOfObjectToIntArray;
26 * Internal AST visitor for propagating syntax errors.
28 class ASTRecoveryPropagator extends DefaultASTVisitor {
29 private static final int NOTHING = -1;
30 HashtableOfObjectToIntArray endingTokens = new HashtableOfObjectToIntArray();
32 this.endingTokens.put(AnonymousClassDeclaration.class, new int[]{TerminalTokens.TokenNameRBRACE});
33 this.endingTokens.put(ArrayAccess.class, new int[]{TerminalTokens.TokenNameRBRACKET});
34 this.endingTokens.put(ArrayCreation.class, new int[]{NOTHING, TerminalTokens.TokenNameRBRACKET});
35 this.endingTokens.put(ArrayInitializer.class, new int[]{TerminalTokens.TokenNameRBRACE});
36 this.endingTokens.put(ArrayType.class, new int[]{TerminalTokens.TokenNameRBRACKET});
37 this.endingTokens.put(AssertStatement.class, new int[]{TerminalTokens.TokenNameSEMICOLON});
38 this.endingTokens.put(Block.class, new int[]{TerminalTokens.TokenNameRBRACE});
39 this.endingTokens.put(BooleanLiteral.class, new int[]{TerminalTokens.TokenNamefalse, TerminalTokens.TokenNametrue});
40 this.endingTokens.put(BreakStatement.class, new int[]{TerminalTokens.TokenNameSEMICOLON});
41 this.endingTokens.put(CharacterLiteral.class, new int[]{TerminalTokens.TokenNameCharacterLiteral});
42 this.endingTokens.put(ClassInstanceCreation.class, new int[]{TerminalTokens.TokenNameRBRACE, TerminalTokens.TokenNameRPAREN});
43 this.endingTokens.put(ConstructorInvocation.class, new int[]{TerminalTokens.TokenNameSEMICOLON});
44 this.endingTokens.put(ContinueStatement.class, new int[]{TerminalTokens.TokenNameSEMICOLON});
45 this.endingTokens.put(DoStatement.class, new int[]{TerminalTokens.TokenNameRPAREN});
46 this.endingTokens.put(EmptyStatement.class, new int[]{TerminalTokens.TokenNameSEMICOLON});
47 this.endingTokens.put(ExpressionStatement.class, new int[]{TerminalTokens.TokenNameSEMICOLON});
48 this.endingTokens.put(FieldDeclaration.class, new int[]{TerminalTokens.TokenNameSEMICOLON});
49 this.endingTokens.put(ImportDeclaration.class, new int[]{TerminalTokens.TokenNameSEMICOLON});
50 this.endingTokens.put(Initializer.class, new int[]{TerminalTokens.TokenNameRBRACE});
51 this.endingTokens.put(MethodDeclaration.class, new int[]{NOTHING, TerminalTokens.TokenNameSEMICOLON});
52 this.endingTokens.put(MethodInvocation.class, new int[]{TerminalTokens.TokenNameRPAREN});
53 this.endingTokens.put(NullLiteral.class, new int[]{TerminalTokens.TokenNamenull});
54 this.endingTokens.put(NumberLiteral.class, new int[]{TerminalTokens.TokenNameIntegerLiteral, TerminalTokens.TokenNameLongLiteral, TerminalTokens.TokenNameFloatingPointLiteral, TerminalTokens.TokenNameDoubleLiteral});
55 this.endingTokens.put(PackageDeclaration.class, new int[]{TerminalTokens.TokenNameSEMICOLON});
56 this.endingTokens.put(ParenthesizedExpression.class, new int[]{TerminalTokens.TokenNameRPAREN});
57 this.endingTokens.put(PostfixExpression.class, new int[]{TerminalTokens.TokenNamePLUS_PLUS, TerminalTokens.TokenNameMINUS_MINUS});
58 this.endingTokens.put(PrimitiveType.class, new int[]{TerminalTokens.TokenNamebyte, TerminalTokens.TokenNameshort, TerminalTokens.TokenNamechar, TerminalTokens.TokenNameint, TerminalTokens.TokenNamelong, TerminalTokens.TokenNamefloat, TerminalTokens.TokenNameboolean, TerminalTokens.TokenNamedouble, TerminalTokens.TokenNamevoid});
59 this.endingTokens.put(ReturnStatement.class, new int[]{TerminalTokens.TokenNameSEMICOLON});
60 this.endingTokens.put(SimpleName.class, new int[]{TerminalTokens.TokenNameIdentifier});
61 this.endingTokens.put(SingleVariableDeclaration.class, new int[]{TerminalTokens.TokenNameSEMICOLON});
62 this.endingTokens.put(StringLiteral.class, new int[]{TerminalTokens.TokenNameStringLiteral});
63 this.endingTokens.put(SuperConstructorInvocation.class, new int[]{TerminalTokens.TokenNameSEMICOLON});
64 this.endingTokens.put(SuperMethodInvocation.class, new int[]{TerminalTokens.TokenNameRPAREN});
65 this.endingTokens.put(SwitchCase.class, new int[]{TerminalTokens.TokenNameCOLON});
66 this.endingTokens.put(SwitchStatement.class, new int[]{TerminalTokens.TokenNameRBRACE});
67 this.endingTokens.put(SynchronizedStatement.class, new int[]{TerminalTokens.TokenNameRBRACE});
68 this.endingTokens.put(ThisExpression.class, new int[]{TerminalTokens.TokenNamethis});
69 this.endingTokens.put(ThrowStatement.class, new int[]{TerminalTokens.TokenNameSEMICOLON});
70 this.endingTokens.put(TypeDeclaration.class, new int[]{TerminalTokens.TokenNameRBRACE});
71 this.endingTokens.put(TypeLiteral.class, new int[]{TerminalTokens.TokenNameclass});
72 this.endingTokens.put(VariableDeclarationStatement.class, new int[]{TerminalTokens.TokenNameSEMICOLON});
75 private CategorizedProblem[] problems;
76 private boolean[] usedOrIrrelevantProblems;
78 private RecoveryScannerData data;
79 private int blockDepth = 0;
82 private int[] insertedTokensKind;
83 private int[] insertedTokensPosition;
84 private boolean[] insertedTokensFlagged;
86 private boolean[] removedTokensFlagged;
87 private boolean[] replacedTokensFlagged;
89 private Vector stack = new Vector();
92 * @noreference This method is not intended to be referenced by clients.
94 ASTRecoveryPropagator(CategorizedProblem[] problems, RecoveryScannerData data) {
95 // visit Javadoc.tags() as well
96 this.problems = problems;
97 this.usedOrIrrelevantProblems = new boolean[problems.length];
101 if(this.data != null) {
104 for (int i = 0; i < data.insertedTokensPtr + 1; i++) {
105 length += data.insertedTokens[i].length;
107 this.insertedTokensKind = new int[length];
108 this.insertedTokensPosition = new int[length];
109 this.insertedTokensFlagged = new boolean[length];
111 for (int i = 0; i < data.insertedTokensPtr + 1; i++) {
112 for (int j = 0; j < data.insertedTokens[i].length; j++) {
113 this.insertedTokensKind[tokenCount] = data.insertedTokens[i][j];
114 this.insertedTokensPosition[tokenCount] = data.insertedTokensPosition[i];
119 if(data.removedTokensPtr != -1) {
120 this.removedTokensFlagged = new boolean[data.removedTokensPtr + 1];
122 if(data.replacedTokensPtr != -1) {
123 this.replacedTokensFlagged = new boolean[data.replacedTokensPtr + 1];
128 public void endVisit(Block node) {
130 if(this.blockDepth <= 0) {
131 flagNodeWithInsertedTokens();
133 super.endVisit(node);
138 public boolean visit(Block node) {
139 boolean visitChildren = super.visit(node);
141 return visitChildren;
144 protected boolean visitNode(ASTNode node) {
145 if(this.blockDepth > 0) {
146 int start = node.getStartPosition();
147 int end = start + node.getLength() - 1;
149 // continue to visit the node only if it contains tokens modifications
151 if(this.insertedTokensFlagged != null) {
152 for (int i = 0; i < this.insertedTokensFlagged.length; i++) {
153 if(this.insertedTokensPosition[i] >= start &&
154 this.insertedTokensPosition[i] <= end) {
160 if(this.removedTokensFlagged != null) {
161 for (int i = 0; i <= this.data.removedTokensPtr; i++) {
162 if(this.data.removedTokensStart[i] >= start &&
163 this.data.removedTokensEnd[i] <= end) {
169 if(this.replacedTokensFlagged != null) {
170 for (int i = 0; i <= this.data.replacedTokensPtr; i++) {
171 if(this.data.replacedTokensStart[i] >= start &&
172 this.data.replacedTokensEnd[i] <= end) {
183 protected void endVisitNode(ASTNode node) {
184 int start = node.getStartPosition();
185 int end = start + node.getLength() - 1;
187 // is inside diet part of the ast
188 if(this.blockDepth < 1) {
189 switch (node.getNodeType()) {
190 case ASTNode.ANNOTATION_TYPE_DECLARATION:
191 case ASTNode.COMPILATION_UNIT:
192 case ASTNode.ENUM_DECLARATION:
193 case ASTNode.FIELD_DECLARATION:
194 case ASTNode.IMPORT_DECLARATION:
195 case ASTNode.INITIALIZER:
196 case ASTNode.METHOD_DECLARATION:
197 case ASTNode.PACKAGE_DECLARATION:
198 case ASTNode.TYPE_DECLARATION:
199 case ASTNode.MARKER_ANNOTATION:
200 case ASTNode.NORMAL_ANNOTATION:
201 case ASTNode.SINGLE_MEMBER_ANNOTATION:
203 if(this.markIncludedProblems(start, end)) {
204 node.setFlags(node.getFlags() | ASTNode.RECOVERED);
209 this.markIncludedProblems(start, end);
211 if(this.insertedTokensFlagged != null) {
212 if(this.lastEnd != end) {
213 flagNodeWithInsertedTokens();
215 this.stack.add(node);
218 if(this.removedTokensFlagged != null) {
219 for (int i = 0; i <= this.data.removedTokensPtr; i++) {
220 if(!this.removedTokensFlagged[i] &&
221 this.data.removedTokensStart[i] >= start &&
222 this.data.removedTokensEnd[i] <= end) {
223 node.setFlags(node.getFlags() | ASTNode.RECOVERED);
224 this.removedTokensFlagged[i] = true;
229 if(this.replacedTokensFlagged != null) {
230 for (int i = 0; i <= this.data.replacedTokensPtr; i++) {
231 if(!this.replacedTokensFlagged[i] &&
232 this.data.replacedTokensStart[i] >= start &&
233 this.data.replacedTokensEnd[i] <= end) {
234 node.setFlags(node.getFlags() | ASTNode.RECOVERED);
235 this.replacedTokensFlagged[i] = true;
243 private void flagNodeWithInsertedTokens() {
244 if(this.insertedTokensKind != null && this.insertedTokensKind.length > 0) {
245 int s = this.stack.size();
246 for (int i = s - 1; i > -1; i--) {
247 flagNodesWithInsertedTokensAtEnd((ASTNode)this.stack.get(i));
249 for (int i = 0; i < s; i++) {
250 flagNodesWithInsertedTokensInside((ASTNode)this.stack.get(i));
252 this.stack = new Vector();
256 private boolean flagNodesWithInsertedTokensAtEnd(ASTNode node) {
257 int[] expectedEndingToken = this.endingTokens.get(node.getClass());
258 if (expectedEndingToken != null) {
259 int start = node.getStartPosition();
260 int end = start + node.getLength() - 1;
262 boolean flagParent = false;
263 done : for (int i = this.insertedTokensKind.length - 1; i > -1 ; i--) {
264 if(!this.insertedTokensFlagged[i] &&
265 this.insertedTokensPosition[i] == end){
266 this.insertedTokensFlagged[i] = true;
267 for (int j = 0; j < expectedEndingToken.length; j++) {
268 if(expectedEndingToken[j] == this.insertedTokensKind[i]) {
269 node.setFlags(node.getFlags() | ASTNode.RECOVERED);
278 ASTNode parent = node.getParent();
279 while (parent != null) {
280 parent.setFlags(node.getFlags() | ASTNode.RECOVERED);
281 if((parent.getStartPosition() + parent.getLength() - 1) != end) {
284 parent = parent.getParent();
292 private boolean flagNodesWithInsertedTokensInside(ASTNode node) {
293 int start = node.getStartPosition();
294 int end = start + node.getLength() - 1;
295 for (int i = 0; i < this.insertedTokensKind.length; i++) {
296 if(!this.insertedTokensFlagged[i] &&
297 start <= this.insertedTokensPosition[i] &&
298 this.insertedTokensPosition[i] < end){
299 node.setFlags(node.getFlags() | ASTNode.RECOVERED);
300 this.insertedTokensFlagged[i] = true;
306 private boolean markIncludedProblems(int start, int end) {
307 boolean foundProblems = false;
308 next: for (int i = 0, max = this.problems.length; i < max; i++) {
309 CategorizedProblem problem = this.problems[i];
311 if(this.usedOrIrrelevantProblems[i]) continue next;
313 switch(problem.getID()) {
314 case IProblem.ParsingErrorOnKeywordNoSuggestion :
315 case IProblem.ParsingErrorOnKeyword :
316 case IProblem.ParsingError :
317 case IProblem.ParsingErrorNoSuggestion :
318 case IProblem.ParsingErrorInsertTokenBefore :
319 case IProblem.ParsingErrorInsertTokenAfter :
320 case IProblem.ParsingErrorDeleteToken :
321 case IProblem.ParsingErrorDeleteTokens :
322 case IProblem.ParsingErrorMergeTokens :
323 case IProblem.ParsingErrorInvalidToken :
324 case IProblem.ParsingErrorMisplacedConstruct :
325 case IProblem.ParsingErrorReplaceTokens :
326 case IProblem.ParsingErrorNoSuggestionForTokens :
327 case IProblem.ParsingErrorUnexpectedEOF :
328 case IProblem.ParsingErrorInsertToComplete :
329 case IProblem.ParsingErrorInsertToCompleteScope :
330 case IProblem.ParsingErrorInsertToCompletePhrase :
331 case IProblem.EndOfSource :
332 case IProblem.InvalidHexa :
333 case IProblem.InvalidOctal :
334 case IProblem.InvalidCharacterConstant :
335 case IProblem.InvalidEscape :
336 case IProblem.InvalidInput :
337 case IProblem.InvalidUnicodeEscape :
338 case IProblem.InvalidFloat :
339 case IProblem.NullSourceString :
340 case IProblem.UnterminatedString :
341 case IProblem.UnterminatedComment :
342 case IProblem.InvalidDigit :
345 this.usedOrIrrelevantProblems[i] = true;
350 int problemStart = problem.getSourceStart();
351 int problemEnd = problem.getSourceEnd();
352 if ((start <= problemStart) && (problemStart <= end) ||
353 (start <= problemEnd) && (problemEnd <= end)) {
354 this.usedOrIrrelevantProblems[i] = true;
355 foundProblems = true;
358 return foundProblems;
361 public void endVisit(ExpressionStatement node) {
363 if ((node.getFlags() & ASTNode.RECOVERED) == 0) return;
364 Expression expression = node.getExpression();
365 if (expression.getNodeType() == ASTNode.ASSIGNMENT) {
366 Assignment assignment = (Assignment) expression;
367 Expression rightHandSide = assignment.getRightHandSide();
368 if (rightHandSide.getNodeType() == ASTNode.SIMPLE_NAME) {
369 SimpleName simpleName = (SimpleName) rightHandSide;
370 if (CharOperation.equals(RecoveryScanner.FAKE_IDENTIFIER, simpleName.getIdentifier().toCharArray())) {
371 Expression expression2 = assignment.getLeftHandSide();
372 // unparent the expression to add it in the expression stateemnt
373 expression2.setParent(null, null);
374 expression2.setFlags(expression2.getFlags() | ASTNode.RECOVERED);
375 node.setExpression(expression2);
381 public void endVisit(ForStatement node) {
383 List initializers = node.initializers();
384 if (initializers.size() == 1) {
385 Expression expression = (Expression) initializers.get(0);
386 if (expression.getNodeType() == ASTNode.VARIABLE_DECLARATION_EXPRESSION) {
387 VariableDeclarationExpression variableDeclarationExpression = (VariableDeclarationExpression) expression;
388 List fragments = variableDeclarationExpression.fragments();
389 for (int i = 0, max = fragments.size(); i <max; i++) {
390 VariableDeclarationFragment fragment = (VariableDeclarationFragment) fragments.get(i);
391 SimpleName simpleName = fragment.getName();
392 if (CharOperation.equals(RecoveryScanner.FAKE_IDENTIFIER, simpleName.getIdentifier().toCharArray())) {
393 fragments.remove(fragment);
394 variableDeclarationExpression.setFlags(variableDeclarationExpression.getFlags() | ASTNode.RECOVERED);
401 public void endVisit(VariableDeclarationStatement node) {
403 List fragments = node.fragments();
404 for (int i = 0, max = fragments.size(); i <max; i++) {
405 VariableDeclarationFragment fragment = (VariableDeclarationFragment) fragments.get(i);
406 Expression expression = fragment.getInitializer();
407 if (expression == null) continue;
408 if ((expression.getFlags() & ASTNode.RECOVERED) == 0) continue;
409 if (expression.getNodeType() == ASTNode.SIMPLE_NAME) {
410 SimpleName simpleName = (SimpleName) expression;
411 if (CharOperation.equals(RecoveryScanner.FAKE_IDENTIFIER, simpleName.getIdentifier().toCharArray())) {
412 fragment.setInitializer(null);
413 fragment.setFlags(fragment.getFlags() | ASTNode.RECOVERED);
419 public void endVisit(NormalAnnotation node) {
421 // is inside diet part of the ast
422 if(this.blockDepth < 1) {
423 List values = node.values();
424 int size = values.size();
426 MemberValuePair lastMemberValuePair = (MemberValuePair)values.get(size - 1);
428 int annotationEnd = node.getStartPosition() + node.getLength();
429 int lastMemberValuePairEnd = lastMemberValuePair.getStartPosition() + lastMemberValuePair.getLength();
430 if (annotationEnd == lastMemberValuePairEnd) {
431 node.setFlags(node.getFlags() | ASTNode.RECOVERED);
437 public void endVisit(SingleMemberAnnotation node) {
439 // is inside diet part of the ast
440 if(this.blockDepth < 1) {
441 Expression value = node.getValue();
442 int annotationEnd = node.getStartPosition() + node.getLength();
443 int valueEnd = value.getStartPosition() + value.getLength();
444 if (annotationEnd == valueEnd) {
445 node.setFlags(node.getFlags() | ASTNode.RECOVERED);