1 /******************************************************************************* 2 * Copyright (c) 2000, 2017 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 * Stephan Herrmann - Contributions for 14 * bug 342671 - ClassCastException: org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding cannot be cast to org.eclipse.jdt.internal.compiler.lookup.ArrayBinding 15 * bug 392099 - [1.8][compiler][null] Apply null annotation on types for null analysis 16 * Bug 415043 - [1.8][null] Follow-up re null type annotations after bug 392099 17 * Bug 416181 - [1.8][compiler][null] Invalid assignment is not rejected by the compiler 18 * Bug 429958 - [1.8][null] evaluate new DefaultLocation attribute of @NonNullByDefault 19 * Bug 434600 - Incorrect null analysis error reporting on type parameters 20 * Bug 435570 - [1.8][null] @NonNullByDefault illegally tries to affect "throws E" 21 * Bug 456508 - Unexpected RHS PolyTypeBinding for: <code-snippet> 22 * Bug 466713 - Null Annotations: NullPointerException using <int @Nullable []> as Type Param 23 * Andy Clement - Contributions for 24 * Bug 383624 - [1.8][compiler] Revive code generation support for type annotations (from Olivier's work) 25 *******************************************************************************/ 26 package org.eclipse.jdt.internal.compiler.ast; 27 28 import org.eclipse.jdt.core.compiler.CharOperation; 29 import org.eclipse.jdt.internal.compiler.ASTVisitor; 30 import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; 31 import org.eclipse.jdt.internal.compiler.impl.Constant; 32 import org.eclipse.jdt.internal.compiler.lookup.*; 33 34 /** 35 * Syntactic representation of a reference to a generic type. 36 * Note that it might also have a dimension. 37 */ 38 public class ParameterizedQualifiedTypeReference extends ArrayQualifiedTypeReference { 39 40 public TypeReference[][] typeArguments; 41 ReferenceBinding[] typesPerToken; 42 43 /** 44 * @param tokens 45 * @param dim 46 * @param positions 47 */ ParameterizedQualifiedTypeReference(char[][] tokens, TypeReference[][] typeArguments, int dim, long[] positions)48 public ParameterizedQualifiedTypeReference(char[][] tokens, TypeReference[][] typeArguments, int dim, long[] positions) { 49 50 super(tokens, dim, positions); 51 this.typeArguments = typeArguments; 52 annotationSearch: for (int i = 0, max = typeArguments.length; i < max; i++) { 53 TypeReference[] typeArgumentsOnTypeComponent = typeArguments[i]; 54 if (typeArgumentsOnTypeComponent != null) { 55 for (int j = 0, max2 = typeArgumentsOnTypeComponent.length; j < max2; j++) { 56 if ((typeArgumentsOnTypeComponent[j].bits & ASTNode.HasTypeAnnotations) != 0) { 57 this.bits |= ASTNode.HasTypeAnnotations; 58 break annotationSearch; 59 } 60 } 61 } 62 } 63 } ParameterizedQualifiedTypeReference(char[][] tokens, TypeReference[][] typeArguments, int dim, Annotation[][] annotationsOnDimensions, long[] positions)64 public ParameterizedQualifiedTypeReference(char[][] tokens, TypeReference[][] typeArguments, int dim, Annotation[][] annotationsOnDimensions, long[] positions) { 65 this(tokens, typeArguments, dim, positions); 66 setAnnotationsOnDimensions(annotationsOnDimensions); 67 if (annotationsOnDimensions != null) { 68 this.bits |= ASTNode.HasTypeAnnotations; 69 } 70 } 71 @Override checkBounds(Scope scope)72 public void checkBounds(Scope scope) { 73 if (this.resolvedType == null || !this.resolvedType.isValidBinding()) return; 74 75 checkBounds( 76 (ReferenceBinding) this.resolvedType.leafComponentType(), 77 scope, 78 this.typeArguments.length - 1); 79 } checkBounds(ReferenceBinding type, Scope scope, int index)80 public void checkBounds(ReferenceBinding type, Scope scope, int index) { 81 // recurse on enclosing type if any, and assuming explictly part of the reference (index>0) 82 if (index > 0) { 83 ReferenceBinding enclosingType = this.typesPerToken[index-1]; 84 if (enclosingType != null) 85 checkBounds(enclosingType, scope, index - 1); 86 } 87 if (type.isParameterizedTypeWithActualArguments()) { 88 ParameterizedTypeBinding parameterizedType = (ParameterizedTypeBinding) type; 89 ReferenceBinding currentType = parameterizedType.genericType(); 90 TypeVariableBinding[] typeVariables = currentType.typeVariables(); 91 if (typeVariables != null) { // argTypes may be null in error cases 92 parameterizedType.boundCheck(scope, this.typeArguments[index]); 93 } 94 } 95 } 96 @Override augmentTypeWithAdditionalDimensions(int additionalDimensions, Annotation[][] additionalAnnotations, boolean isVarargs)97 public TypeReference augmentTypeWithAdditionalDimensions(int additionalDimensions, Annotation[][] additionalAnnotations, boolean isVarargs) { 98 int totalDimensions = this.dimensions() + additionalDimensions; 99 Annotation [][] allAnnotations = getMergedAnnotationsOnDimensions(additionalDimensions, additionalAnnotations); 100 ParameterizedQualifiedTypeReference pqtr = new ParameterizedQualifiedTypeReference(this.tokens, this.typeArguments, totalDimensions, allAnnotations, this.sourcePositions); 101 pqtr.annotations = this.annotations; 102 pqtr.bits |= (this.bits & ASTNode.HasTypeAnnotations); 103 if (!isVarargs) 104 pqtr.extendedDimensions = additionalDimensions; 105 return pqtr; 106 } 107 @Override isParameterizedTypeReference()108 public boolean isParameterizedTypeReference() { 109 return true; 110 } 111 112 @Override hasNullTypeAnnotation(AnnotationPosition position)113 public boolean hasNullTypeAnnotation(AnnotationPosition position) { 114 if (super.hasNullTypeAnnotation(position)) 115 return true; 116 if (position == AnnotationPosition.ANY) { 117 if (this.resolvedType != null && !this.resolvedType.hasNullTypeAnnotations()) 118 return false; // shortcut 119 if (this.typeArguments != null) { 120 for (int i = 0; i < this.typeArguments.length; i++) { 121 TypeReference[] arguments = this.typeArguments[i]; 122 if (arguments != null) { 123 for (int j = 0; j < arguments.length; j++) { 124 if (arguments[j].hasNullTypeAnnotation(position)) 125 return true; 126 } 127 } 128 } 129 } 130 } 131 return false; 132 } 133 134 /** 135 * @return char[][] 136 */ 137 @Override getParameterizedTypeName()138 public char [][] getParameterizedTypeName(){ 139 int length = this.tokens.length; 140 char[][] qParamName = new char[length][]; 141 for (int i = 0; i < length; i++) { 142 TypeReference[] arguments = this.typeArguments[i]; 143 if (arguments == null) { 144 qParamName[i] = this.tokens[i]; 145 } else { 146 StringBuffer buffer = new StringBuffer(5); 147 buffer.append(this.tokens[i]); 148 buffer.append('<'); 149 for (int j = 0, argLength =arguments.length; j < argLength; j++) { 150 if (j > 0) buffer.append(','); 151 buffer.append(CharOperation.concatWith(arguments[j].getParameterizedTypeName(), '.')); 152 } 153 buffer.append('>'); 154 int nameLength = buffer.length(); 155 qParamName[i] = new char[nameLength]; 156 buffer.getChars(0, nameLength, qParamName[i], 0); 157 } 158 } 159 int dim = this.dimensions; 160 if (dim > 0) { 161 char[] dimChars = new char[dim*2]; 162 for (int i = 0; i < dim; i++) { 163 int index = i*2; 164 dimChars[index] = '['; 165 dimChars[index+1] = ']'; 166 } 167 qParamName[length-1] = CharOperation.concat(qParamName[length-1], dimChars); 168 } 169 return qParamName; 170 } 171 172 @Override getTypeArguments()173 public TypeReference[][] getTypeArguments() { 174 return this.typeArguments; 175 } 176 177 @Override getTypeBinding(Scope scope)178 protected TypeBinding getTypeBinding(Scope scope) { 179 return null; // not supported here - combined with resolveType(...) 180 } 181 182 /* 183 * No need to check for reference to raw type per construction 184 */ internalResolveType(Scope scope, boolean checkBounds, int location)185 private TypeBinding internalResolveType(Scope scope, boolean checkBounds, int location) { 186 // handle the error here 187 this.constant = Constant.NotAConstant; 188 if ((this.bits & ASTNode.DidResolve) != 0) { // is a shared type reference which was already resolved 189 if (this.resolvedType != null) { // is a shared type reference which was already resolved 190 if (this.resolvedType.isValidBinding()) { 191 return this.resolvedType; 192 } else { 193 switch (this.resolvedType.problemId()) { 194 case ProblemReasons.NotFound : 195 case ProblemReasons.NotVisible : 196 case ProblemReasons.InheritedNameHidesEnclosingName : 197 TypeBinding type = this.resolvedType.closestMatch(); 198 return type; 199 default : 200 return null; 201 } 202 } 203 } 204 } 205 this.bits |= ASTNode.DidResolve; 206 TypeBinding type = internalResolveLeafType(scope, checkBounds); 207 createArrayType(scope); 208 resolveAnnotations(scope, location); 209 if(this.dimensions > 0) { 210 this.resolvedType = ArrayTypeReference.maybeMarkArrayContentsNonNull(scope, this.resolvedType, this.sourceStart, this.dimensions, null); 211 } 212 213 if (this.typeArguments != null) 214 // relevant null annotations are on the inner most type: 215 checkIllegalNullAnnotations(scope, this.typeArguments[this.typeArguments.length-1]); 216 return type == null ? type : this.resolvedType; 217 } internalResolveLeafType(Scope scope, boolean checkBounds)218 private TypeBinding internalResolveLeafType(Scope scope, boolean checkBounds) { 219 boolean isClassScope = scope.kind == Scope.CLASS_SCOPE; 220 Binding binding = scope.getPackage(this.tokens); 221 if (binding != null && !binding.isValidBinding()) { 222 this.resolvedType = (ReferenceBinding) binding; 223 reportInvalidType(scope); 224 // be resilient, still attempt resolving arguments 225 for (int i = 0, max = this.tokens.length; i < max; i++) { 226 TypeReference[] args = this.typeArguments[i]; 227 if (args != null) { 228 int argLength = args.length; 229 for (int j = 0; j < argLength; j++) { 230 TypeReference typeArgument = args[j]; 231 if (isClassScope) { 232 typeArgument.resolveType((ClassScope) scope); 233 } else { 234 typeArgument.resolveType((BlockScope) scope, checkBounds); 235 } 236 } 237 } 238 } 239 return null; 240 } 241 242 PackageBinding packageBinding = binding == null ? null : (PackageBinding) binding; 243 rejectAnnotationsOnPackageQualifiers(scope, packageBinding); 244 245 boolean typeIsConsistent = true; 246 ReferenceBinding qualifyingType = null; 247 int max = this.tokens.length; 248 this.typesPerToken = new ReferenceBinding[max]; 249 for (int i = packageBinding == null ? 0 : packageBinding.compoundName.length; i < max; i++) { 250 findNextTypeBinding(i, scope, packageBinding); 251 if (!(this.resolvedType.isValidBinding())) { 252 reportInvalidType(scope); 253 // be resilient, still attempt resolving arguments 254 for (int j = i; j < max; j++) { 255 TypeReference[] args = this.typeArguments[j]; 256 if (args != null) { 257 int argLength = args.length; 258 for (int k = 0; k < argLength; k++) { 259 TypeReference typeArgument = args[k]; 260 if (isClassScope) { 261 typeArgument.resolveType((ClassScope) scope); 262 } else { 263 typeArgument.resolveType((BlockScope) scope); 264 } 265 } 266 } 267 } 268 return null; 269 } 270 ReferenceBinding currentType = (ReferenceBinding) this.resolvedType; 271 if (qualifyingType == null) { 272 qualifyingType = currentType.enclosingType(); // if member type 273 if (qualifyingType != null && currentType.hasEnclosingInstanceContext()) { 274 qualifyingType = scope.environment().convertToParameterizedType(qualifyingType); 275 } 276 } else { 277 if (this.annotations != null) 278 rejectAnnotationsOnStaticMemberQualififer(scope, currentType, this.annotations[i-1]); 279 if (typeIsConsistent && currentType.isStatic() 280 && (qualifyingType.isParameterizedTypeWithActualArguments() || qualifyingType.isGenericType())) { 281 scope.problemReporter().staticMemberOfParameterizedType(this, currentType, qualifyingType, i); 282 typeIsConsistent = false; 283 qualifyingType = qualifyingType.actualType(); // avoid raw/parameterized enclosing of static member 284 } 285 ReferenceBinding enclosingType = currentType.enclosingType(); 286 if (enclosingType != null && TypeBinding.notEquals(enclosingType.erasure(), qualifyingType.erasure())) { // qualifier != declaring/enclosing 287 qualifyingType = enclosingType; // inherited member type, leave it associated with its enclosing rather than subtype 288 } 289 } 290 291 // check generic and arity 292 TypeReference[] args = this.typeArguments[i]; 293 if (args != null) { 294 TypeReference keep = null; 295 if (isClassScope) { 296 keep = ((ClassScope) scope).superTypeReference; 297 ((ClassScope) scope).superTypeReference = null; 298 } 299 int argLength = args.length; 300 boolean isDiamond = argLength == 0 && (i == (max -1)) && ((this.bits & ASTNode.IsDiamond) != 0); 301 TypeBinding[] argTypes = new TypeBinding[argLength]; 302 boolean argHasError = false; 303 ReferenceBinding currentOriginal = (ReferenceBinding)currentType.original(); 304 for (int j = 0; j < argLength; j++) { 305 TypeReference arg = args[j]; 306 TypeBinding argType = isClassScope 307 ? arg.resolveTypeArgument((ClassScope) scope, currentOriginal, j) 308 : arg.resolveTypeArgument((BlockScope) scope, currentOriginal, j); 309 if (argType == null) { 310 argHasError = true; 311 } else { 312 argTypes[j] = argType; 313 } 314 } 315 if (argHasError) { 316 return null; 317 } 318 if (isClassScope) { 319 ((ClassScope) scope).superTypeReference = keep; 320 if (((ClassScope) scope).detectHierarchyCycle(currentOriginal, this)) 321 return null; 322 } 323 324 TypeVariableBinding[] typeVariables = currentOriginal.typeVariables(); 325 if (typeVariables == Binding.NO_TYPE_VARIABLES) { // check generic 326 if (scope.compilerOptions().originalSourceLevel >= ClassFileConstants.JDK1_5) { // below 1.5, already reported as syntax error 327 scope.problemReporter().nonGenericTypeCannotBeParameterized(i, this, currentType, argTypes); 328 return null; 329 } 330 this.resolvedType = (qualifyingType != null && qualifyingType.isParameterizedType()) 331 ? scope.environment().createParameterizedType(currentOriginal, null, qualifyingType) 332 : currentType; 333 return this.resolvedType; 334 } else if (argLength != typeVariables.length) { 335 if (!isDiamond) { // check arity 336 scope.problemReporter().incorrectArityForParameterizedType(this, currentType, argTypes, i); 337 return null; 338 } 339 } 340 // check parameterizing (non-)static member type of raw type 341 if (typeIsConsistent) { 342 if (!currentType.hasEnclosingInstanceContext()) { 343 if (qualifyingType != null && qualifyingType.isRawType()) 344 this.typesPerToken[i-1] = qualifyingType = qualifyingType.actualType(); // revert rawification of enclosing, since its generics are inaccessible 345 } else { 346 ReferenceBinding actualEnclosing = currentType.enclosingType(); 347 if (actualEnclosing != null && actualEnclosing.isRawType()) { 348 scope.problemReporter().rawMemberTypeCannotBeParameterized( 349 this, scope.environment().createRawType(currentOriginal, actualEnclosing), argTypes); 350 typeIsConsistent = false; 351 } 352 } 353 } 354 ParameterizedTypeBinding parameterizedType = scope.environment().createParameterizedType(currentOriginal, argTypes, qualifyingType); 355 // check argument type compatibility for non <> cases - <> case needs no bounds check, we will scream foul if needed during inference. 356 if (!isDiamond) { 357 if (checkBounds) // otherwise will do it in Scope.connectTypeVariables() or generic method resolution 358 parameterizedType.boundCheck(scope, args); 359 else 360 scope.deferBoundCheck(this); 361 } else { 362 parameterizedType.arguments = ParameterizedSingleTypeReference.DIAMOND_TYPE_ARGUMENTS; 363 } 364 qualifyingType = parameterizedType; 365 } else { 366 ReferenceBinding currentOriginal = (ReferenceBinding)currentType.original(); 367 if (isClassScope) 368 if (((ClassScope) scope).detectHierarchyCycle(currentOriginal, this)) 369 return null; 370 if (currentOriginal.isGenericType()) { 371 if (typeIsConsistent && qualifyingType != null && qualifyingType.isParameterizedType() && currentOriginal.hasEnclosingInstanceContext()) { 372 scope.problemReporter().parameterizedMemberTypeMissingArguments(this, scope.environment().createParameterizedType(currentOriginal, null, qualifyingType), i); 373 typeIsConsistent = false; 374 } 375 qualifyingType = scope.environment().createRawType(currentOriginal, qualifyingType); // raw type 376 } else { 377 qualifyingType = scope.environment().maybeCreateParameterizedType(currentOriginal, qualifyingType); 378 } 379 } 380 if (isTypeUseDeprecated(qualifyingType, scope)) 381 reportDeprecatedType(qualifyingType, scope, i); 382 this.resolvedType = qualifyingType; 383 this.typesPerToken[i] = qualifyingType; 384 recordResolution(scope.environment(), this.resolvedType); 385 } 386 return this.resolvedType; 387 } createArrayType(Scope scope)388 private void createArrayType(Scope scope) { 389 if (this.dimensions > 0) { 390 if (this.dimensions > 255) 391 scope.problemReporter().tooManyDimensions(this); 392 this.resolvedType = scope.createArrayType(this.resolvedType, this.dimensions); 393 } 394 } 395 396 @Override printExpression(int indent, StringBuffer output)397 public StringBuffer printExpression(int indent, StringBuffer output) { 398 int length = this.tokens.length; 399 for (int i = 0; i < length - 1; i++) { 400 if (this.annotations != null && this.annotations[i] != null) { 401 printAnnotations(this.annotations[i], output); 402 output.append(' '); 403 } 404 output.append(this.tokens[i]); 405 TypeReference[] typeArgument = this.typeArguments[i]; 406 if (typeArgument != null) { 407 output.append('<'); 408 int typeArgumentLength = typeArgument.length; 409 if (typeArgumentLength > 0) { 410 int max = typeArgumentLength - 1; 411 for (int j = 0; j < max; j++) { 412 typeArgument[j].print(0, output); 413 output.append(", ");//$NON-NLS-1$ 414 } 415 typeArgument[max].print(0, output); 416 } 417 output.append('>'); 418 } 419 output.append('.'); 420 } 421 if (this.annotations != null && this.annotations[length - 1] != null) { 422 output.append(" "); //$NON-NLS-1$ 423 printAnnotations(this.annotations[length - 1], output); 424 output.append(' '); 425 } 426 output.append(this.tokens[length - 1]); 427 TypeReference[] typeArgument = this.typeArguments[length - 1]; 428 if (typeArgument != null) { 429 output.append('<'); 430 int typeArgumentLength = typeArgument.length; 431 if (typeArgumentLength > 0) { 432 int max = typeArgumentLength - 1; 433 for (int j = 0; j < max; j++) { 434 typeArgument[j].print(0, output); 435 output.append(", ");//$NON-NLS-1$ 436 } 437 typeArgument[max].print(0, output); 438 } 439 output.append('>'); 440 } 441 Annotation [][] annotationsOnDimensions = this.getAnnotationsOnDimensions(); 442 if ((this.bits & IsVarArgs) != 0) { 443 for (int i= 0 ; i < this.dimensions - 1; i++) { 444 if (annotationsOnDimensions != null && annotationsOnDimensions[i] != null) { 445 output.append(" "); //$NON-NLS-1$ 446 printAnnotations(annotationsOnDimensions[i], output); 447 output.append(" "); //$NON-NLS-1$ 448 } 449 output.append("[]"); //$NON-NLS-1$ 450 } 451 if (annotationsOnDimensions != null && annotationsOnDimensions[this.dimensions - 1] != null) { 452 output.append(" "); //$NON-NLS-1$ 453 printAnnotations(annotationsOnDimensions[this.dimensions - 1], output); 454 output.append(" "); //$NON-NLS-1$ 455 } 456 output.append("..."); //$NON-NLS-1$ 457 } else { 458 for (int i= 0 ; i < this.dimensions; i++) { 459 if (annotationsOnDimensions != null && annotationsOnDimensions[i] != null) { 460 output.append(" "); //$NON-NLS-1$ 461 printAnnotations(annotationsOnDimensions[i], output); 462 output.append(" "); //$NON-NLS-1$ 463 } 464 output.append("[]"); //$NON-NLS-1$ 465 } 466 } 467 return output; 468 } 469 470 @Override resolveType(BlockScope scope, boolean checkBounds, int location)471 public TypeBinding resolveType(BlockScope scope, boolean checkBounds, int location) { 472 return internalResolveType(scope, checkBounds, location); 473 } 474 @Override resolveType(ClassScope scope, int location)475 public TypeBinding resolveType(ClassScope scope, int location) { 476 return internalResolveType(scope, false, location); 477 } 478 @Override traverse(ASTVisitor visitor, BlockScope scope)479 public void traverse(ASTVisitor visitor, BlockScope scope) { 480 if (visitor.visit(this, scope)) { 481 if (this.annotations != null) { 482 int annotationsLevels = this.annotations.length; 483 for (int i = 0; i < annotationsLevels; i++) { 484 int annotationsLength = this.annotations[i] == null ? 0 : this.annotations[i].length; 485 for (int j = 0; j < annotationsLength; j++) 486 this.annotations[i][j].traverse(visitor, scope); 487 } 488 } 489 Annotation [][] annotationsOnDimensions = getAnnotationsOnDimensions(true); 490 if (annotationsOnDimensions != null) { 491 for (int i = 0, max = annotationsOnDimensions.length; i < max; i++) { 492 Annotation[] annotations2 = annotationsOnDimensions[i]; 493 for (int j = 0, max2 = annotations2 == null ? 0 : annotations2.length; j < max2; j++) { 494 Annotation annotation = annotations2[j]; 495 annotation.traverse(visitor, scope); 496 } 497 } 498 } 499 for (int i = 0, max = this.typeArguments.length; i < max; i++) { 500 if (this.typeArguments[i] != null) { 501 for (int j = 0, max2 = this.typeArguments[i].length; j < max2; j++) { 502 this.typeArguments[i][j].traverse(visitor, scope); 503 } 504 } 505 } 506 } 507 visitor.endVisit(this, scope); 508 } 509 510 @Override traverse(ASTVisitor visitor, ClassScope scope)511 public void traverse(ASTVisitor visitor, ClassScope scope) { 512 if (visitor.visit(this, scope)) { 513 if (this.annotations != null) { 514 int annotationsLevels = this.annotations.length; 515 for (int i = 0; i < annotationsLevels; i++) { 516 int annotationsLength = this.annotations[i] == null ? 0 : this.annotations[i].length; 517 for (int j = 0; j < annotationsLength; j++) 518 this.annotations[i][j].traverse(visitor, scope); 519 } 520 } 521 Annotation [][] annotationsOnDimensions = getAnnotationsOnDimensions(true); 522 if (annotationsOnDimensions != null) { 523 for (int i = 0, max = annotationsOnDimensions.length; i < max; i++) { 524 Annotation[] annotations2 = annotationsOnDimensions[i]; 525 for (int j = 0, max2 = annotations2 == null ? 0 : annotations2.length; j < max2; j++) { 526 Annotation annotation = annotations2[j]; 527 annotation.traverse(visitor, scope); 528 } 529 } 530 } 531 for (int i = 0, max = this.typeArguments.length; i < max; i++) { 532 if (this.typeArguments[i] != null) { 533 for (int j = 0, max2 = this.typeArguments[i].length; j < max2; j++) { 534 this.typeArguments[i][j].traverse(visitor, scope); 535 } 536 } 537 } 538 } 539 visitor.endVisit(this, scope); 540 } 541 542 } 543