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 - Contribution for 14 * Bug 429958 - [1.8][null] evaluate new DefaultLocation attribute of @NonNullByDefault 15 * Bug 435570 - [1.8][null] @NonNullByDefault illegally tries to affect "throws E" 16 * Bug 466713 - Null Annotations: NullPointerException using <int @Nullable []> as Type Param 17 *******************************************************************************/ 18 package org.eclipse.jdt.internal.compiler.ast; 19 20 import java.util.function.Consumer; 21 22 import org.eclipse.jdt.core.compiler.CharOperation; 23 import org.eclipse.jdt.internal.compiler.ASTVisitor; 24 import org.eclipse.jdt.internal.compiler.lookup.AnnotationBinding; 25 import org.eclipse.jdt.internal.compiler.lookup.Binding; 26 import org.eclipse.jdt.internal.compiler.lookup.BlockScope; 27 import org.eclipse.jdt.internal.compiler.lookup.ClassScope; 28 import org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment; 29 import org.eclipse.jdt.internal.compiler.lookup.Scope; 30 import org.eclipse.jdt.internal.compiler.lookup.TagBits; 31 import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; 32 import org.eclipse.jdt.internal.compiler.lookup.TypeIds; 33 34 public class ArrayTypeReference extends SingleTypeReference { 35 public int dimensions; 36 private Annotation[][] annotationsOnDimensions; // jsr308 style type annotations on dimensions. 37 public int originalSourceEnd; 38 public int extendedDimensions; 39 public TypeBinding leafComponentTypeWithoutDefaultNullness; 40 41 /** 42 * ArrayTypeReference constructor comment. 43 * @param source char[] 44 * @param dimensions int 45 * @param pos int 46 */ ArrayTypeReference(char[] source, int dimensions, long pos)47 public ArrayTypeReference(char[] source, int dimensions, long pos) { 48 49 super(source, pos); 50 this.originalSourceEnd = this.sourceEnd; 51 this.dimensions = dimensions ; 52 this.annotationsOnDimensions = null; 53 } 54 ArrayTypeReference(char[] source, int dimensions, Annotation[][] annotationsOnDimensions, long pos)55 public ArrayTypeReference(char[] source, int dimensions, Annotation[][] annotationsOnDimensions, long pos) { 56 this(source, dimensions, pos); 57 if (annotationsOnDimensions != null) { 58 this.bits |= ASTNode.HasTypeAnnotations; 59 } 60 this.annotationsOnDimensions = annotationsOnDimensions; 61 } 62 63 @Override dimensions()64 public int dimensions() { 65 66 return this.dimensions; 67 } 68 69 @Override extraDimensions()70 public int extraDimensions() { 71 return this.extendedDimensions; 72 } 73 74 /** 75 @see org.eclipse.jdt.internal.compiler.ast.TypeReference#getAnnotationsOnDimensions(boolean) 76 */ 77 @Override getAnnotationsOnDimensions(boolean useSourceOrder)78 public Annotation[][] getAnnotationsOnDimensions(boolean useSourceOrder) { 79 if (useSourceOrder || this.annotationsOnDimensions == null || this.annotationsOnDimensions.length == 0 || this.extendedDimensions == 0 || this.extendedDimensions == this.dimensions) 80 return this.annotationsOnDimensions; 81 Annotation [][] externalAnnotations = new Annotation[this.dimensions][]; 82 final int baseDimensions = this.dimensions - this.extendedDimensions; 83 System.arraycopy(this.annotationsOnDimensions, baseDimensions, externalAnnotations, 0, this.extendedDimensions); 84 System.arraycopy(this.annotationsOnDimensions, 0, externalAnnotations, this.extendedDimensions, baseDimensions); 85 return externalAnnotations; 86 } 87 88 @Override setAnnotationsOnDimensions(Annotation [][] annotationsOnDimensions)89 public void setAnnotationsOnDimensions(Annotation [][] annotationsOnDimensions) { 90 this.annotationsOnDimensions = annotationsOnDimensions; 91 } 92 /** 93 * @return char[][] 94 */ 95 @Override getParameterizedTypeName()96 public char [][] getParameterizedTypeName(){ 97 int dim = this.dimensions; 98 char[] dimChars = new char[dim*2]; 99 for (int i = 0; i < dim; i++) { 100 int index = i*2; 101 dimChars[index] = '['; 102 dimChars[index+1] = ']'; 103 } 104 return new char[][]{ CharOperation.concat(this.token, dimChars) }; 105 } 106 @Override getTypeBinding(Scope scope)107 protected TypeBinding getTypeBinding(Scope scope) { 108 109 if (this.resolvedType != null) { 110 return this.resolvedType; 111 } 112 if (this.dimensions > 255) { 113 scope.problemReporter().tooManyDimensions(this); 114 } 115 TypeBinding leafComponentType = scope.getType(this.token); 116 return scope.createArrayType(leafComponentType, this.dimensions); 117 118 } 119 120 @Override printExpression(int indent, StringBuffer output)121 public StringBuffer printExpression(int indent, StringBuffer output){ 122 123 super.printExpression(indent, output); 124 if ((this.bits & IsVarArgs) != 0) { 125 for (int i= 0 ; i < this.dimensions - 1; i++) { 126 if (this.annotationsOnDimensions != null && this.annotationsOnDimensions[i] != null) { 127 output.append(' '); 128 printAnnotations(this.annotationsOnDimensions[i], output); 129 output.append(' '); 130 } 131 output.append("[]"); //$NON-NLS-1$ 132 } 133 if (this.annotationsOnDimensions != null && this.annotationsOnDimensions[this.dimensions - 1] != null) { 134 output.append(' '); 135 printAnnotations(this.annotationsOnDimensions[this.dimensions - 1], output); 136 output.append(' '); 137 } 138 output.append("..."); //$NON-NLS-1$ 139 } else { 140 for (int i= 0 ; i < this.dimensions; i++) { 141 if (this.annotationsOnDimensions != null && this.annotationsOnDimensions[i] != null) { 142 output.append(" "); //$NON-NLS-1$ 143 printAnnotations(this.annotationsOnDimensions[i], output); 144 output.append(" "); //$NON-NLS-1$ 145 } 146 output.append("[]"); //$NON-NLS-1$ 147 } 148 } 149 return output; 150 } 151 152 @Override traverse(ASTVisitor visitor, BlockScope scope)153 public void traverse(ASTVisitor visitor, BlockScope scope) { 154 if (visitor.visit(this, scope)) { 155 if (this.annotations != null) { 156 Annotation [] typeAnnotations = this.annotations[0]; 157 for (int i = 0, length = typeAnnotations == null ? 0 : typeAnnotations.length; i < length; i++) { 158 typeAnnotations[i].traverse(visitor, scope); 159 } 160 } 161 if (this.annotationsOnDimensions != null) { 162 for (int i = 0, max = this.annotationsOnDimensions.length; i < max; i++) { 163 Annotation[] annotations2 = this.annotationsOnDimensions[i]; 164 if (annotations2 != null) { 165 for (int j = 0, max2 = annotations2.length; j < max2; j++) { 166 Annotation annotation = annotations2[j]; 167 annotation.traverse(visitor, scope); 168 } 169 } 170 } 171 } 172 } 173 visitor.endVisit(this, scope); 174 } 175 176 @Override traverse(ASTVisitor visitor, ClassScope scope)177 public void traverse(ASTVisitor visitor, ClassScope scope) { 178 if (visitor.visit(this, scope)) { 179 if (this.annotations != null) { 180 Annotation [] typeAnnotations = this.annotations[0]; 181 for (int i = 0, length = typeAnnotations == null ? 0 : typeAnnotations.length; i < length; i++) { 182 typeAnnotations[i].traverse(visitor, scope); 183 } 184 } 185 if (this.annotationsOnDimensions != null) { 186 for (int i = 0, max = this.annotationsOnDimensions.length; i < max; i++) { 187 Annotation[] annotations2 = this.annotationsOnDimensions[i]; 188 if (annotations2 != null) { 189 for (int j = 0, max2 = annotations2.length; j < max2; j++) { 190 Annotation annotation = annotations2[j]; 191 annotation.traverse(visitor, scope); 192 } 193 } 194 } 195 } 196 } 197 visitor.endVisit(this, scope); 198 } 199 200 @Override internalResolveType(Scope scope, int location)201 protected TypeBinding internalResolveType(Scope scope, int location) { 202 TypeBinding internalResolveType = super.internalResolveType(scope, location); 203 internalResolveType = maybeMarkArrayContentsNonNull(scope, internalResolveType, this.sourceStart, this.dimensions, 204 leafType -> this.leafComponentTypeWithoutDefaultNullness = leafType); 205 206 return internalResolveType; 207 } 208 maybeMarkArrayContentsNonNull(Scope scope, TypeBinding typeBinding, int sourceStart, int dimensions, Consumer<TypeBinding> leafConsumer)209 static TypeBinding maybeMarkArrayContentsNonNull(Scope scope, TypeBinding typeBinding, int sourceStart, int dimensions, Consumer<TypeBinding> leafConsumer) { 210 LookupEnvironment environment = scope.environment(); 211 if (environment.usesNullTypeAnnotations() 212 && scope.hasDefaultNullnessFor(Binding.DefaultLocationArrayContents, sourceStart)) { 213 AnnotationBinding nonNullAnnotation = environment.getNonNullAnnotation(); 214 typeBinding = addNonNullToDimensions(scope, typeBinding, nonNullAnnotation, dimensions); 215 216 TypeBinding leafComponentType = typeBinding.leafComponentType(); 217 if ((leafComponentType.tagBits & TagBits.AnnotationNullMASK) == 0 && leafComponentType.acceptsNonNullDefault()) { 218 if (leafConsumer != null) 219 leafConsumer.accept(leafComponentType); 220 TypeBinding nonNullLeafComponentType = scope.environment().createAnnotatedType(leafComponentType, 221 new AnnotationBinding[] { nonNullAnnotation }); 222 typeBinding = scope.createArrayType(nonNullLeafComponentType, typeBinding.dimensions(), 223 typeBinding.getTypeAnnotations()); 224 } 225 } 226 return typeBinding; 227 } 228 addNonNullToDimensions(Scope scope, TypeBinding typeBinding, AnnotationBinding nonNullAnnotation, int dimensions2)229 static TypeBinding addNonNullToDimensions(Scope scope, TypeBinding typeBinding, 230 AnnotationBinding nonNullAnnotation, int dimensions2) { 231 AnnotationBinding[][] newAnnots = new AnnotationBinding[dimensions2][]; 232 AnnotationBinding[] oldAnnots = typeBinding.getTypeAnnotations(); 233 if (oldAnnots == null) { 234 for (int i = 1; i < dimensions2; i++) { 235 newAnnots[i] = new AnnotationBinding[] { nonNullAnnotation }; 236 } 237 } else { 238 int j = 0; 239 for (int i = 0; i < dimensions2; i++) { 240 if (j >= oldAnnots.length || oldAnnots[j] == null) { 241 if (i != 0) { 242 newAnnots[i] = new AnnotationBinding[] { nonNullAnnotation }; 243 } 244 j++; 245 } else { 246 int k = j; 247 boolean seen = false; 248 while (oldAnnots[k] != null) { 249 seen |= oldAnnots[k].getAnnotationType() 250 .hasNullBit(TypeIds.BitNonNullAnnotation | TypeIds.BitNullableAnnotation); 251 k++; 252 } 253 if (seen || i == 0) { 254 if (k > j) { 255 AnnotationBinding[] annotationsForDimension = new AnnotationBinding[k - j]; 256 System.arraycopy(oldAnnots, j, annotationsForDimension, 0, k - j); 257 newAnnots[i] = annotationsForDimension; 258 } 259 } else { 260 AnnotationBinding[] annotationsForDimension = new AnnotationBinding[k - j + 1]; 261 annotationsForDimension[0] = nonNullAnnotation; 262 System.arraycopy(oldAnnots, j, annotationsForDimension, 1, k - j); 263 newAnnots[i] = annotationsForDimension; 264 } 265 j = k + 1; 266 } 267 } 268 } 269 return scope.environment().createAnnotatedType(typeBinding, newAnnots); 270 } 271 272 @Override hasNullTypeAnnotation(AnnotationPosition position)273 public boolean hasNullTypeAnnotation(AnnotationPosition position) { 274 switch (position) { 275 case LEAF_TYPE: 276 // ignore annotationsOnDimensions: 277 return super.hasNullTypeAnnotation(position); 278 case MAIN_TYPE: 279 // outermost dimension only: 280 if (this.annotationsOnDimensions != null && this.annotationsOnDimensions.length > 0) { 281 Annotation[] innerAnnotations = this.annotationsOnDimensions[0]; 282 return containsNullAnnotation(innerAnnotations); 283 } 284 // e.g. subclass ParameterizedSingleTypeReference is not only used for arrays 285 return super.hasNullTypeAnnotation(position); 286 case ANY: 287 if (super.hasNullTypeAnnotation(position)) 288 return true; 289 if (this.resolvedType != null && !this.resolvedType.hasNullTypeAnnotations()) 290 return false; // shortcut 291 if (this.annotationsOnDimensions != null) { 292 for (int i = 0; i < this.annotationsOnDimensions.length; i++) { 293 Annotation[] innerAnnotations = this.annotationsOnDimensions[i]; 294 if (containsNullAnnotation(innerAnnotations)) 295 return true; 296 } 297 } 298 } 299 return false; 300 } 301 } 302