| 1 | /******************************************************************************* |
|---|---|
| 2 | * Copyright (c) 2006, 2020 IBM Corporation and others. |
| 3 | * |
| 4 | * This program and the accompanying materials |
| 5 | * are made available under the terms of the Eclipse Public License 2.0 |
| 6 | * which accompanies this distribution, and is available at |
| 7 | * https://www.eclipse.org/legal/epl-2.0/ |
| 8 | * |
| 9 | * SPDX-License-Identifier: EPL-2.0 |
| 10 | * |
| 11 | * Contributors: |
| 12 | * IBM Corporation - initial API and implementation |
| 13 | *******************************************************************************/ |
| 14 | |
| 15 | package org.eclipse.jdt.core.dom; |
| 16 | |
| 17 | import java.util.ArrayList; |
| 18 | import java.util.List; |
| 19 | |
| 20 | import org.eclipse.jdt.core.compiler.CategorizedProblem; |
| 21 | import org.eclipse.jdt.core.compiler.CharOperation; |
| 22 | import org.eclipse.jdt.core.compiler.IProblem; |
| 23 | import org.eclipse.jdt.internal.compiler.parser.RecoveryScanner; |
| 24 | import org.eclipse.jdt.internal.compiler.parser.RecoveryScannerData; |
| 25 | import org.eclipse.jdt.internal.compiler.parser.TerminalTokens; |
| 26 | import org.eclipse.jdt.internal.compiler.util.HashtableOfObjectToIntArray; |
| 27 | |
| 28 | /** |
| 29 | * Internal AST visitor for propagating syntax errors. |
| 30 | */ |
| 31 | @SuppressWarnings({"rawtypes"}) |
| 32 | class ASTRecoveryPropagator extends DefaultASTVisitor { |
| 33 | private static final int NOTHING = -1; |
| 34 | HashtableOfObjectToIntArray endingTokens = new HashtableOfObjectToIntArray(); |
| 35 | { |
| 36 | this.endingTokens.put(AnonymousClassDeclaration.class, new int[]{TerminalTokens.TokenNameRBRACE}); |
| 37 | this.endingTokens.put(ArrayAccess.class, new int[]{TerminalTokens.TokenNameRBRACKET}); |
| 38 | this.endingTokens.put(ArrayCreation.class, new int[]{NOTHING, TerminalTokens.TokenNameRBRACKET}); |
| 39 | this.endingTokens.put(ArrayInitializer.class, new int[]{TerminalTokens.TokenNameRBRACE}); |
| 40 | this.endingTokens.put(ArrayType.class, new int[]{TerminalTokens.TokenNameRBRACKET}); |
| 41 | this.endingTokens.put(AssertStatement.class, new int[]{TerminalTokens.TokenNameSEMICOLON}); |
| 42 | this.endingTokens.put(Block.class, new int[]{TerminalTokens.TokenNameRBRACE}); |
| 43 | this.endingTokens.put(BooleanLiteral.class, new int[]{TerminalTokens.TokenNamefalse, TerminalTokens.TokenNametrue}); |
| 44 | this.endingTokens.put(BreakStatement.class, new int[]{TerminalTokens.TokenNameSEMICOLON}); |
| 45 | this.endingTokens.put(CharacterLiteral.class, new int[]{TerminalTokens.TokenNameCharacterLiteral}); |
| 46 | this.endingTokens.put(ClassInstanceCreation.class, new int[]{TerminalTokens.TokenNameRBRACE, TerminalTokens.TokenNameRPAREN}); |
| 47 | this.endingTokens.put(ConstructorInvocation.class, new int[]{TerminalTokens.TokenNameSEMICOLON}); |
| 48 | this.endingTokens.put(ContinueStatement.class, new int[]{TerminalTokens.TokenNameSEMICOLON}); |
| 49 | this.endingTokens.put(DoStatement.class, new int[]{TerminalTokens.TokenNameRPAREN}); |
| 50 | this.endingTokens.put(EmptyStatement.class, new int[]{TerminalTokens.TokenNameSEMICOLON}); |
| 51 | this.endingTokens.put(ExpressionStatement.class, new int[]{TerminalTokens.TokenNameSEMICOLON}); |
| 52 | this.endingTokens.put(FieldDeclaration.class, new int[]{TerminalTokens.TokenNameSEMICOLON}); |
| 53 | this.endingTokens.put(ImportDeclaration.class, new int[]{TerminalTokens.TokenNameSEMICOLON}); |
| 54 | this.endingTokens.put(Initializer.class, new int[]{TerminalTokens.TokenNameRBRACE}); |
| 55 | this.endingTokens.put(MethodDeclaration.class, new int[]{NOTHING, TerminalTokens.TokenNameSEMICOLON}); |
| 56 | this.endingTokens.put(MethodInvocation.class, new int[]{TerminalTokens.TokenNameRPAREN}); |
| 57 | this.endingTokens.put(ModuleDeclaration.class, new int[]{TerminalTokens.TokenNameRBRACE}); |
| 58 | this.endingTokens.put(ModuleDirective.class, new int[]{TerminalTokens.TokenNameSEMICOLON}); |
| 59 | this.endingTokens.put(NullLiteral.class, new int[]{TerminalTokens.TokenNamenull}); |
| 60 | this.endingTokens.put(NumberLiteral.class, new int[]{TerminalTokens.TokenNameIntegerLiteral, TerminalTokens.TokenNameLongLiteral, TerminalTokens.TokenNameFloatingPointLiteral, TerminalTokens.TokenNameDoubleLiteral}); |
| 61 | this.endingTokens.put(PackageDeclaration.class, new int[]{TerminalTokens.TokenNameSEMICOLON}); |
| 62 | this.endingTokens.put(ParenthesizedExpression.class, new int[]{TerminalTokens.TokenNameRPAREN}); |
| 63 | this.endingTokens.put(PostfixExpression.class, new int[]{TerminalTokens.TokenNamePLUS_PLUS, TerminalTokens.TokenNameMINUS_MINUS}); |
| 64 | 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}); |
| 65 | this.endingTokens.put(ReturnStatement.class, new int[]{TerminalTokens.TokenNameSEMICOLON}); |
| 66 | this.endingTokens.put(SimpleName.class, new int[]{TerminalTokens.TokenNameIdentifier}); |
| 67 | this.endingTokens.put(SingleVariableDeclaration.class, new int[]{TerminalTokens.TokenNameSEMICOLON}); |
| 68 | this.endingTokens.put(StringLiteral.class, new int[]{TerminalTokens.TokenNameStringLiteral}); |
| 69 | this.endingTokens.put(SuperConstructorInvocation.class, new int[]{TerminalTokens.TokenNameSEMICOLON}); |
| 70 | this.endingTokens.put(SuperMethodInvocation.class, new int[]{TerminalTokens.TokenNameRPAREN}); |
| 71 | this.endingTokens.put(SwitchCase.class, new int[]{TerminalTokens.TokenNameCOLON}); |
| 72 | this.endingTokens.put(SwitchStatement.class, new int[]{TerminalTokens.TokenNameRBRACE}); |
| 73 | this.endingTokens.put(SynchronizedStatement.class, new int[]{TerminalTokens.TokenNameRBRACE}); |
| 74 | this.endingTokens.put(ThisExpression.class, new int[]{TerminalTokens.TokenNamethis}); |
| 75 | this.endingTokens.put(ThrowStatement.class, new int[]{TerminalTokens.TokenNameSEMICOLON}); |
| 76 | this.endingTokens.put(TypeDeclaration.class, new int[]{TerminalTokens.TokenNameRBRACE}); |
| 77 | this.endingTokens.put(TypeLiteral.class, new int[]{TerminalTokens.TokenNameclass}); |
| 78 | this.endingTokens.put(VariableDeclarationStatement.class, new int[]{TerminalTokens.TokenNameSEMICOLON}); |
| 79 | } |
| 80 | |
| 81 | private CategorizedProblem[] problems; |
| 82 | private boolean[] usedOrIrrelevantProblems; |
| 83 | |
| 84 | private RecoveryScannerData data; |
| 85 | private int blockDepth = 0; |
| 86 | private int lastEnd; |
| 87 | |
| 88 | private int[] insertedTokensKind; |
| 89 | private int[] insertedTokensPosition; |
| 90 | private boolean[] insertedTokensFlagged; |
| 91 | |
| 92 | private boolean[] removedTokensFlagged; |
| 93 | private boolean[] replacedTokensFlagged; |
| 94 | |
| 95 | private ArrayList<ASTNode> stack = new ArrayList<>(); |
| 96 | |
| 97 | ASTRecoveryPropagator(CategorizedProblem[] problems, RecoveryScannerData data) { |
| 98 | // visit Javadoc.tags() as well |
| 99 | this.problems = problems; |
| 100 | this.usedOrIrrelevantProblems = new boolean[problems.length]; |
| 101 | |
| 102 | this.data = data; |
| 103 | |
| 104 | if(this.data != null) { |
| 105 | |
| 106 | int length = 0; |
| 107 | for (int i = 0; i < data.insertedTokensPtr + 1; i++) { |
| 108 | length += data.insertedTokens[i].length; |
| 109 | } |
| 110 | this.insertedTokensKind = new int[length]; |
| 111 | this.insertedTokensPosition = new int[length]; |
| 112 | this.insertedTokensFlagged = new boolean[length]; |
| 113 | int tokenCount = 0; |
| 114 | for (int i = 0; i < data.insertedTokensPtr + 1; i++) { |
| 115 | for (int j = 0; j < data.insertedTokens[i].length; j++) { |
| 116 | this.insertedTokensKind[tokenCount] = data.insertedTokens[i][j]; |
| 117 | this.insertedTokensPosition[tokenCount] = data.insertedTokensPosition[i]; |
| 118 | tokenCount++; |
| 119 | } |
| 120 | } |
| 121 | |
| 122 | if(data.removedTokensPtr != -1) { |
| 123 | this.removedTokensFlagged = new boolean[data.removedTokensPtr + 1]; |
| 124 | } |
| 125 | if(data.replacedTokensPtr != -1) { |
| 126 | this.replacedTokensFlagged = new boolean[data.replacedTokensPtr + 1]; |
| 127 | } |
| 128 | } |
| 129 | } |
| 130 | |
| 131 | @Override |
| 132 | public void endVisit(Block node) { |
| 133 | this.blockDepth--; |
| 134 | if(this.blockDepth <= 0) { |
| 135 | flagNodeWithInsertedTokens(); |
| 136 | } |
| 137 | super.endVisit(node); |
| 138 | } |
| 139 | |
| 140 | |
| 141 | |
| 142 | @Override |
| 143 | public boolean visit(Block node) { |
| 144 | boolean visitChildren = super.visit(node); |
| 145 | this.blockDepth++; |
| 146 | return visitChildren; |
| 147 | } |
| 148 | |
| 149 | @Override |
| 150 | protected boolean visitNode(ASTNode node) { |
| 151 | if(this.blockDepth > 0) { |
| 152 | int start = node.getStartPosition(); |
| 153 | int end = start + node.getLength() - 1; |
| 154 | |
| 155 | // continue to visit the node only if it contains tokens modifications |
| 156 | |
| 157 | if(this.insertedTokensFlagged != null) { |
| 158 | for (int i = 0; i < this.insertedTokensFlagged.length; i++) { |
| 159 | if(this.insertedTokensPosition[i] >= start && |
| 160 | this.insertedTokensPosition[i] <= end) { |
| 161 | return true; |
| 162 | } |
| 163 | } |
| 164 | } |
| 165 | |
| 166 | if(this.removedTokensFlagged != null) { |
| 167 | for (int i = 0; i <= this.data.removedTokensPtr; i++) { |
| 168 | if(this.data.removedTokensStart[i] >= start && |
| 169 | this.data.removedTokensEnd[i] <= end) { |
| 170 | return true; |
| 171 | } |
| 172 | } |
| 173 | } |
| 174 | |
| 175 | if(this.replacedTokensFlagged != null) { |
| 176 | for (int i = 0; i <= this.data.replacedTokensPtr; i++) { |
| 177 | if(this.data.replacedTokensStart[i] >= start && |
| 178 | this.data.replacedTokensEnd[i] <= end) { |
| 179 | return true; |
| 180 | } |
| 181 | } |
| 182 | } |
| 183 | |
| 184 | return false; |
| 185 | } |
| 186 | return true; |
| 187 | } |
| 188 | |
| 189 | @Override |
| 190 | protected void endVisitNode(ASTNode node) { |
| 191 | int start = node.getStartPosition(); |
| 192 | int end = start + node.getLength() - 1; |
| 193 | |
| 194 | // is inside diet part of the ast |
| 195 | if(this.blockDepth < 1) { |
| 196 | switch (node.getNodeType()) { |
| 197 | case ASTNode.ANNOTATION_TYPE_DECLARATION: |
| 198 | case ASTNode.COMPILATION_UNIT: |
| 199 | case ASTNode.ENUM_DECLARATION: |
| 200 | case ASTNode.FIELD_DECLARATION: |
| 201 | case ASTNode.IMPORT_DECLARATION: |
| 202 | case ASTNode.INITIALIZER: |
| 203 | case ASTNode.METHOD_DECLARATION: |
| 204 | case ASTNode.MODULE_DECLARATION: |
| 205 | case ASTNode.PACKAGE_DECLARATION: |
| 206 | case ASTNode.TYPE_DECLARATION: |
| 207 | case ASTNode.MARKER_ANNOTATION: |
| 208 | case ASTNode.NORMAL_ANNOTATION: |
| 209 | case ASTNode.SINGLE_MEMBER_ANNOTATION: |
| 210 | case ASTNode.BLOCK: |
| 211 | if(markIncludedProblems(start, end)) { |
| 212 | node.setFlags(node.getFlags() | ASTNode.RECOVERED); |
| 213 | } |
| 214 | break; |
| 215 | } |
| 216 | } else { |
| 217 | markIncludedProblems(start, end); |
| 218 | |
| 219 | if(this.insertedTokensFlagged != null) { |
| 220 | if(this.lastEnd != end) { |
| 221 | flagNodeWithInsertedTokens(); |
| 222 | } |
| 223 | this.stack.add(node); |
| 224 | } |
| 225 | |
| 226 | if(this.removedTokensFlagged != null) { |
| 227 | for (int i = 0; i <= this.data.removedTokensPtr; i++) { |
| 228 | if(!this.removedTokensFlagged[i] && |
| 229 | this.data.removedTokensStart[i] >= start && |
| 230 | this.data.removedTokensEnd[i] <= end) { |
| 231 | node.setFlags(node.getFlags() | ASTNode.RECOVERED); |
| 232 | this.removedTokensFlagged[i] = true; |
| 233 | } |
| 234 | } |
| 235 | } |
| 236 | |
| 237 | if(this.replacedTokensFlagged != null) { |
| 238 | for (int i = 0; i <= this.data.replacedTokensPtr; i++) { |
| 239 | if(!this.replacedTokensFlagged[i] && |
| 240 | this.data.replacedTokensStart[i] >= start && |
| 241 | this.data.replacedTokensEnd[i] <= end) { |
| 242 | node.setFlags(node.getFlags() | ASTNode.RECOVERED); |
| 243 | this.replacedTokensFlagged[i] = true; |
| 244 | } |
| 245 | } |
| 246 | } |
| 247 | } |
| 248 | this.lastEnd = end; |
| 249 | } |
| 250 | |
| 251 | private void flagNodeWithInsertedTokens() { |
| 252 | if(this.insertedTokensKind != null && this.insertedTokensKind.length > 0) { |
| 253 | int s = this.stack.size(); |
| 254 | for (int i = s - 1; i > -1; i--) { |
| 255 | flagNodesWithInsertedTokensAtEnd(this.stack.get(i)); |
| 256 | } |
| 257 | for (int i = 0; i < s; i++) { |
| 258 | flagNodesWithInsertedTokensInside(this.stack.get(i)); |
| 259 | } |
| 260 | this.stack = new ArrayList<>(); |
| 261 | } |
| 262 | } |
| 263 | |
| 264 | private boolean flagNodesWithInsertedTokensAtEnd(ASTNode node) { |
| 265 | int[] expectedEndingToken = this.endingTokens.get(node.getClass()); |
| 266 | if (expectedEndingToken != null) { |
| 267 | int start = node.getStartPosition(); |
| 268 | int end = start + node.getLength() - 1; |
| 269 | |
| 270 | boolean flagParent = false; |
| 271 | done : for (int i = this.insertedTokensKind.length - 1; i > -1 ; i--) { |
| 272 | if(!this.insertedTokensFlagged[i] && |
| 273 | this.insertedTokensPosition[i] == end){ |
| 274 | this.insertedTokensFlagged[i] = true; |
| 275 | for (int j = 0; j < expectedEndingToken.length; j++) { |
| 276 | if(expectedEndingToken[j] == this.insertedTokensKind[i]) { |
| 277 | node.setFlags(node.getFlags() | ASTNode.RECOVERED); |
| 278 | break done; |
| 279 | } |
| 280 | } |
| 281 | flagParent = true; |
| 282 | } |
| 283 | } |
| 284 | |
| 285 | if(flagParent) { |
| 286 | ASTNode parent = node.getParent(); |
| 287 | while (parent != null) { |
| 288 | parent.setFlags(node.getFlags() | ASTNode.RECOVERED); |
| 289 | if((parent.getStartPosition() + parent.getLength() - 1) != end) { |
| 290 | parent = null; |
| 291 | } else { |
| 292 | parent = parent.getParent(); |
| 293 | } |
| 294 | } |
| 295 | } |
| 296 | } |
| 297 | return true; |
| 298 | } |
| 299 | |
| 300 | private boolean flagNodesWithInsertedTokensInside(ASTNode node) { |
| 301 | int start = node.getStartPosition(); |
| 302 | int end = start + node.getLength() - 1; |
| 303 | for (int i = 0; i < this.insertedTokensKind.length; i++) { |
| 304 | if(!this.insertedTokensFlagged[i] && |
| 305 | start <= this.insertedTokensPosition[i] && |
| 306 | this.insertedTokensPosition[i] < end){ |
| 307 | node.setFlags(node.getFlags() | ASTNode.RECOVERED); |
| 308 | this.insertedTokensFlagged[i] = true; |
| 309 | } |
| 310 | } |
| 311 | return true; |
| 312 | } |
| 313 | |
| 314 | private boolean markIncludedProblems(int start, int end) { |
| 315 | boolean foundProblems = false; |
| 316 | next: for (int i = 0, max = this.problems.length; i < max; i++) { |
| 317 | CategorizedProblem problem = this.problems[i]; |
| 318 | |
| 319 | if(this.usedOrIrrelevantProblems[i]) continue next; |
| 320 | |
| 321 | switch(problem.getID()) { |
| 322 | case IProblem.ParsingErrorOnKeywordNoSuggestion : |
| 323 | case IProblem.ParsingErrorOnKeyword : |
| 324 | case IProblem.ParsingError : |
| 325 | case IProblem.ParsingErrorNoSuggestion : |
| 326 | case IProblem.ParsingErrorInsertTokenBefore : |
| 327 | case IProblem.ParsingErrorInsertTokenAfter : |
| 328 | case IProblem.ParsingErrorDeleteToken : |
| 329 | case IProblem.ParsingErrorDeleteTokens : |
| 330 | case IProblem.ParsingErrorMergeTokens : |
| 331 | case IProblem.ParsingErrorInvalidToken : |
| 332 | case IProblem.ParsingErrorMisplacedConstruct : |
| 333 | case IProblem.ParsingErrorReplaceTokens : |
| 334 | case IProblem.ParsingErrorNoSuggestionForTokens : |
| 335 | case IProblem.ParsingErrorUnexpectedEOF : |
| 336 | case IProblem.ParsingErrorInsertToComplete : |
| 337 | case IProblem.ParsingErrorInsertToCompleteScope : |
| 338 | case IProblem.ParsingErrorInsertToCompletePhrase : |
| 339 | case IProblem.EndOfSource : |
| 340 | case IProblem.InvalidHexa : |
| 341 | case IProblem.InvalidOctal : |
| 342 | case IProblem.InvalidCharacterConstant : |
| 343 | case IProblem.InvalidEscape : |
| 344 | case IProblem.InvalidInput : |
| 345 | case IProblem.InvalidUnicodeEscape : |
| 346 | case IProblem.InvalidFloat : |
| 347 | case IProblem.NullSourceString : |
| 348 | case IProblem.UnterminatedString : |
| 349 | case IProblem.UnterminatedComment : |
| 350 | case IProblem.InvalidDigit : |
| 351 | break; |
| 352 | default: |
| 353 | this.usedOrIrrelevantProblems[i] = true; |
| 354 | continue next; |
| 355 | |
| 356 | } |
| 357 | |
| 358 | int problemStart = problem.getSourceStart(); |
| 359 | int problemEnd = problem.getSourceEnd(); |
| 360 | if ((start <= problemStart) && (problemStart <= end) || |
| 361 | (start <= problemEnd) && (problemEnd <= end)) { |
| 362 | this.usedOrIrrelevantProblems[i] = true; |
| 363 | foundProblems = true; |
| 364 | } |
| 365 | } |
| 366 | return foundProblems; |
| 367 | } |
| 368 | |
| 369 | @Override |
| 370 | public void endVisit(ExpressionStatement node) { |
| 371 | endVisitNode(node); |
| 372 | if ((node.getFlags() & ASTNode.RECOVERED) == 0) return; |
| 373 | Expression expression = node.getExpression(); |
| 374 | if (expression.getNodeType() == ASTNode.ASSIGNMENT) { |
| 375 | Assignment assignment = (Assignment) expression; |
| 376 | Expression rightHandSide = assignment.getRightHandSide(); |
| 377 | if (rightHandSide.getNodeType() == ASTNode.SIMPLE_NAME) { |
| 378 | SimpleName simpleName = (SimpleName) rightHandSide; |
| 379 | if (CharOperation.equals(RecoveryScanner.FAKE_IDENTIFIER, simpleName.getIdentifier().toCharArray())) { |
| 380 | Expression expression2 = assignment.getLeftHandSide(); |
| 381 | // unparent the expression to add it in the expression stateemnt |
| 382 | expression2.setParent(null, null); |
| 383 | expression2.setFlags(expression2.getFlags() | ASTNode.RECOVERED); |
| 384 | node.setExpression(expression2); |
| 385 | } |
| 386 | } |
| 387 | } |
| 388 | } |
| 389 | |
| 390 | @Override |
| 391 | public void endVisit(ForStatement node) { |
| 392 | endVisitNode(node); |
| 393 | List initializers = node.initializers(); |
| 394 | if (initializers.size() == 1) { |
| 395 | Expression expression = (Expression) initializers.get(0); |
| 396 | if (expression.getNodeType() == ASTNode.VARIABLE_DECLARATION_EXPRESSION) { |
| 397 | VariableDeclarationExpression variableDeclarationExpression = (VariableDeclarationExpression) expression; |
| 398 | List fragments = variableDeclarationExpression.fragments(); |
| 399 | for (int i = 0, max = fragments.size(); i <max; i++) { |
| 400 | VariableDeclarationFragment fragment = (VariableDeclarationFragment) fragments.get(i); |
| 401 | SimpleName simpleName = fragment.getName(); |
| 402 | if (CharOperation.equals(RecoveryScanner.FAKE_IDENTIFIER, simpleName.getIdentifier().toCharArray())) { |
| 403 | fragments.remove(fragment); |
| 404 | variableDeclarationExpression.setFlags(variableDeclarationExpression.getFlags() | ASTNode.RECOVERED); |
| 405 | } |
| 406 | } |
| 407 | } |
| 408 | } |
| 409 | } |
| 410 | |
| 411 | @Override |
| 412 | public void endVisit(VariableDeclarationStatement node) { |
| 413 | endVisitNode(node); |
| 414 | List fragments = node.fragments(); |
| 415 | for (int i = 0, max = fragments.size(); i <max; i++) { |
| 416 | VariableDeclarationFragment fragment = (VariableDeclarationFragment) fragments.get(i); |
| 417 | Expression expression = fragment.getInitializer(); |
| 418 | if (expression == null) continue; |
| 419 | if ((expression.getFlags() & ASTNode.RECOVERED) == 0) continue; |
| 420 | if (expression.getNodeType() == ASTNode.SIMPLE_NAME) { |
| 421 | SimpleName simpleName = (SimpleName) expression; |
| 422 | if (CharOperation.equals(RecoveryScanner.FAKE_IDENTIFIER, simpleName.getIdentifier().toCharArray())) { |
| 423 | fragment.setInitializer(null); |
| 424 | fragment.setFlags(fragment.getFlags() | ASTNode.RECOVERED); |
| 425 | } |
| 426 | } |
| 427 | } |
| 428 | } |
| 429 | |
| 430 | @Override |
| 431 | public void endVisit(NormalAnnotation node) { |
| 432 | endVisitNode(node); |
| 433 | // is inside diet part of the ast |
| 434 | if(this.blockDepth < 1) { |
| 435 | List values = node.values(); |
| 436 | int size = values.size(); |
| 437 | if (size > 0) { |
| 438 | MemberValuePair lastMemberValuePair = (MemberValuePair)values.get(size - 1); |
| 439 | |
| 440 | int annotationEnd = node.getStartPosition() + node.getLength(); |
| 441 | int lastMemberValuePairEnd = lastMemberValuePair.getStartPosition() + lastMemberValuePair.getLength(); |
| 442 | if (annotationEnd == lastMemberValuePairEnd) { |
| 443 | node.setFlags(node.getFlags() | ASTNode.RECOVERED); |
| 444 | } |
| 445 | } |
| 446 | } |
| 447 | } |
| 448 | |
| 449 | @Override |
| 450 | public void endVisit(SingleMemberAnnotation node) { |
| 451 | endVisitNode(node); |
| 452 | // is inside diet part of the ast |
| 453 | if(this.blockDepth < 1) { |
| 454 | Expression value = node.getValue(); |
| 455 | int annotationEnd = node.getStartPosition() + node.getLength(); |
| 456 | int valueEnd = value.getStartPosition() + value.getLength(); |
| 457 | if (annotationEnd == valueEnd) { |
| 458 | node.setFlags(node.getFlags() | ASTNode.RECOVERED); |
| 459 | } |
| 460 | } |
| 461 | } |
| 462 | } |
| 463 |
Members