Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: bb737d61bbc7bc0cc09b34175a3b9bc7bcaa874e (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
/*******************************************************************************
 * Copyright (c) 2000, 2018 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *     Stephan Herrmann - Contribution for
 *								bug 395002 - Self bound generic class doesn't resolve bounds properly for wildcards for certain parametrisation.
 *								bug 331649 - [compiler][null] consider null annotations for fields
 *								bug 400761 - [compiler][null] null may be return as boolean without a diagnostic
 *								Bug 427438 - [1.8][compiler] NPE at org.eclipse.jdt.internal.compiler.ast.ConditionalExpression.generateCode(ConditionalExpression.java:280)
 *								Bug 429403 - [1.8][null] null mismatch from type arguments is not reported at field initializer
 *								Bug 453483 - [compiler][null][loop] Improve null analysis for loops
 *								Bug 458396 - NPE in CodeStream.invoke()
 *        Andy Clement (GoPivotal, Inc) aclement@gopivotal.com - Contributions for
 *								Bug 409250 - [1.8][compiler] Various loose ends in 308 code generation
 *******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;

import java.util.List;

import static org.eclipse.jdt.internal.compiler.ast.ExpressionContext.ASSIGNMENT_CONTEXT;

import org.eclipse.jdt.core.compiler.IProblem;
import org.eclipse.jdt.internal.compiler.ASTVisitor;
import org.eclipse.jdt.internal.compiler.impl.*;
import org.eclipse.jdt.internal.compiler.ast.TypeReference.AnnotationCollector;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.codegen.*;
import org.eclipse.jdt.internal.compiler.flow.*;
import org.eclipse.jdt.internal.compiler.lookup.*;
import org.eclipse.jdt.internal.compiler.problem.ProblemReporter;
import org.eclipse.jdt.internal.compiler.problem.ProblemSeverities;
import org.eclipse.jdt.internal.compiler.util.Util;

@SuppressWarnings("rawtypes")
public class FieldDeclaration extends AbstractVariableDeclaration {

	public FieldBinding binding;
	public Javadoc javadoc;

	//allows to retrieve both the "type" part of the declaration (part1)
	//and also the part that decribe the name and the init and optionally
	//some other dimension ! ....
	//public int[] a, b[] = X, c ;
	//for b that would give for
	// - part1 : public int[]
	// - part2 : b[] = X,

	public int endPart1Position;
	public int endPart2Position;

public FieldDeclaration() {
	// for subtypes or conversion
}

public FieldDeclaration(	char[] name, int sourceStart, int sourceEnd) {
	this.name = name;
	//due to some declaration like
	// int x, y = 3, z , x ;
	//the sourceStart and the sourceEnd is ONLY on  the name
	this.sourceStart = sourceStart;
	this.sourceEnd = sourceEnd;
}

public FlowInfo analyseCode(MethodScope initializationScope, FlowContext flowContext, FlowInfo flowInfo) {
	if (this.binding != null && !this.binding.isUsed() && this.binding.isOrEnclosedByPrivateType()) {
		if (!initializationScope.referenceCompilationUnit().compilationResult.hasSyntaxError) {
			initializationScope.problemReporter().unusedPrivateField(this);
		}
	}
	// cannot define static non-constant field inside nested class
	if (this.binding != null
			&& this.binding.isValidBinding()
			&& this.binding.isStatic()
			&& this.binding.constant(initializationScope) == Constant.NotAConstant
			&& this.binding.declaringClass.isNestedType()
			&& !this.binding.declaringClass.isStatic()) {
		initializationScope.problemReporter().unexpectedStaticModifierForField(
			(SourceTypeBinding) this.binding.declaringClass,
			this);
	}

	if (this.initialization != null) {
		flowInfo =
			this.initialization
				.analyseCode(initializationScope, flowContext, flowInfo)
				.unconditionalInits();
		flowInfo.markAsDefinitelyAssigned(this.binding);
	}
	if (this.initialization != null && this.binding != null) {
		CompilerOptions options = initializationScope.compilerOptions();
		if (options.isAnnotationBasedNullAnalysisEnabled) {
			if (this.binding.isNonNull() || options.sourceLevel >= ClassFileConstants.JDK1_8) {
				int nullStatus = this.initialization.nullStatus(flowInfo, flowContext);
				NullAnnotationMatching.checkAssignment(initializationScope, flowContext, this.binding, flowInfo, nullStatus, this.initialization, this.initialization.resolvedType);
			}
		}
		this.initialization.checkNPEbyUnboxing(initializationScope, flowContext, flowInfo);
	}
	return flowInfo;
}

/**
 * Code generation for a field declaration:
 *	   standard assignment to a field
 *
 * @param currentScope org.eclipse.jdt.internal.compiler.lookup.BlockScope
 * @param codeStream org.eclipse.jdt.internal.compiler.codegen.CodeStream
 */
@Override
public void generateCode(BlockScope currentScope, CodeStream codeStream) {
	if ((this.bits & IsReachable) == 0) {
		return;
	}
	// do not generate initialization code if final and static (constant is then
	// recorded inside the field itself).
	int pc = codeStream.position;
	boolean isStatic;
	if (this.initialization != null
			&& !((isStatic = this.binding.isStatic()) && this.binding.constant() != Constant.NotAConstant)) {
		// non-static field, need receiver
		if (!isStatic)
			codeStream.aload_0();
		// generate initialization value
		this.initialization.generateCode(currentScope, codeStream, true);
		// store into field
		if (isStatic) {
			codeStream.fieldAccess(Opcodes.OPC_putstatic, this.binding, null /* default declaringClass */);
		} else {
			codeStream.fieldAccess(Opcodes.OPC_putfield, this.binding, null /* default declaringClass */);
		}
	}
	codeStream.recordPositionsFrom(pc, this.sourceStart);
}
public void getAllAnnotationContexts(int targetType, List allAnnotationContexts) {
	AnnotationCollector collector = new AnnotationCollector(this.type, targetType, allAnnotationContexts);
	for (int i = 0, max = this.annotations.length; i < max; i++) {
		Annotation annotation = this.annotations[i];
		annotation.traverse(collector, (BlockScope) null);
	}
}
/**
 * @see org.eclipse.jdt.internal.compiler.ast.AbstractVariableDeclaration#getKind()
 */
@Override
public int getKind() {
	return this.type == null ? ENUM_CONSTANT : FIELD;
}

public boolean isStatic() {
	if (this.binding != null)
		return this.binding.isStatic();
	return (this.modifiers & ClassFileConstants.AccStatic) != 0;
}

public boolean isFinal() {
	if (this.binding != null)
		return this.binding.isFinal();
	return (this.modifiers & ClassFileConstants.AccFinal) != 0;
}

@Override
public StringBuffer printStatement(int indent, StringBuffer output) {
	if (this.javadoc != null) {
		this.javadoc.print(indent, output);
	}
	return super.printStatement(indent, output);
}

public void resolve(MethodScope initializationScope) {
	// the two <constant = Constant.NotAConstant> could be regrouped into
	// a single line but it is clearer to have two lines while the reason of their
	// existence is not at all the same. See comment for the second one.

	//--------------------------------------------------------
	if ((this.bits & ASTNode.HasBeenResolved) != 0) return;
	if (this.binding == null || !this.binding.isValidBinding()) return;

	this.bits |= ASTNode.HasBeenResolved;

	// check if field is hiding some variable - issue is that field binding already got inserted in scope
	// thus must lookup separately in super type and outer context
	ClassScope classScope = initializationScope.enclosingClassScope();

	if (classScope != null) {
		checkHiding: {
			SourceTypeBinding declaringType = classScope.enclosingSourceType();
			checkHidingSuperField: {
				if (declaringType.superclass == null) break checkHidingSuperField;
				// https://bugs.eclipse.org/bugs/show_bug.cgi?id=318171, find field skipping visibility checks
				// we do the checks below ourselves, using the appropriate conditions for access check of
				// protected members from superclasses.
				FieldBinding existingVariable = classScope.findField(declaringType.superclass, this.name, this,  false /*do not resolve hidden field*/, true /* no visibility checks please */);
				if (existingVariable == null) break checkHidingSuperField; // keep checking outer scenario
				if (!existingVariable.isValidBinding())  break checkHidingSuperField; // keep checking outer scenario
				if (existingVariable.original() == this.binding) break checkHidingSuperField; // keep checking outer scenario
				if (!existingVariable.canBeSeenBy(declaringType, this, initializationScope)) break checkHidingSuperField; // keep checking outer scenario
				// collision with supertype field
				initializationScope.problemReporter().fieldHiding(this, existingVariable);
				break checkHiding; // already found a matching field
			}
			// only corner case is: lookup of outer field through static declaringType, which isn't detected by #getBinding as lookup starts
			// from outer scope. Subsequent static contexts are detected for free.
			Scope outerScope = classScope.parent;
			if (outerScope.kind == Scope.COMPILATION_UNIT_SCOPE) break checkHiding;
			Binding existingVariable = outerScope.getBinding(this.name, Binding.VARIABLE, this, false /*do not resolve hidden field*/);
			if (existingVariable == null) break checkHiding;
			if (!existingVariable.isValidBinding()) break checkHiding;
			if (existingVariable == this.binding) break checkHiding;
			if (existingVariable instanceof FieldBinding) {
				FieldBinding existingField = (FieldBinding) existingVariable;
				if (existingField.original() == this.binding) break checkHiding;
				if (!existingField.isStatic() && declaringType.isStatic()) break checkHiding;
			}
			// collision with outer field or local variable
			initializationScope.problemReporter().fieldHiding(this, existingVariable);
		}
	}

	if (this.type != null ) { // enum constants have no declared type
		this.type.resolvedType = this.binding.type; // update binding for type reference
	}

	FieldBinding previousField = initializationScope.initializedField;
	int previousFieldID = initializationScope.lastVisibleFieldID;
	try {
		initializationScope.initializedField = this.binding;
		initializationScope.lastVisibleFieldID = this.binding.id;

		resolveAnnotations(initializationScope, this.annotations, this.binding);
		// Check if this declaration should now have the type annotations bit set
		if (this.annotations != null) {
			for (int i = 0, max = this.annotations.length; i < max; i++) {
				TypeBinding resolvedAnnotationType = this.annotations[i].resolvedType;
				if (resolvedAnnotationType != null && (resolvedAnnotationType.getAnnotationTagBits() & TagBits.AnnotationForTypeUse) != 0) {
					this.bits |= ASTNode.HasTypeAnnotations;
					break;
				}
			}
		}
		
		// check @Deprecated annotation presence
		if ((this.binding.getAnnotationTagBits() & TagBits.AnnotationDeprecated) == 0
				&& (this.binding.modifiers & ClassFileConstants.AccDeprecated) != 0
				&& initializationScope.compilerOptions().sourceLevel >= ClassFileConstants.JDK1_5) {
			initializationScope.problemReporter().missingDeprecatedAnnotationForField(this);
		}
		// the resolution of the initialization hasn't been done
		if (this.initialization == null) {
			this.binding.setConstant(Constant.NotAConstant);
		} else {
			// break dead-lock cycles by forcing constant to NotAConstant
			this.binding.setConstant(Constant.NotAConstant);

			TypeBinding fieldType = this.binding.type;
			TypeBinding initializationType;
			this.initialization.setExpressionContext(ASSIGNMENT_CONTEXT);
			this.initialization.setExpectedType(fieldType); // needed in case of generic method invocation
			if (this.initialization instanceof ArrayInitializer) {

				if ((initializationType = this.initialization.resolveTypeExpecting(initializationScope, fieldType)) != null) {
					((ArrayInitializer) this.initialization).binding = (ArrayBinding) initializationType;
					this.initialization.computeConversion(initializationScope, fieldType, initializationType);
				}
			} else if ((initializationType = this.initialization.resolveType(initializationScope)) != null) {

				if (TypeBinding.notEquals(fieldType, initializationType)) // must call before computeConversion() and typeMismatchError()
					initializationScope.compilationUnitScope().recordTypeConversion(fieldType, initializationType);
				if (this.initialization.isConstantValueOfTypeAssignableToType(initializationType, fieldType)
						|| initializationType.isCompatibleWith(fieldType, classScope)) {
					this.initialization.computeConversion(initializationScope, fieldType, initializationType);
					if (initializationType.needsUncheckedConversion(fieldType)) {
						    initializationScope.problemReporter().unsafeTypeConversion(this.initialization, initializationType, fieldType);
					}
					if (this.initialization instanceof CastExpression
							&& (this.initialization.bits & ASTNode.UnnecessaryCast) == 0) {
						CastExpression.checkNeedForAssignedCast(initializationScope, fieldType, (CastExpression) this.initialization);
					}
				} else if (isBoxingCompatible(initializationType, fieldType, this.initialization, initializationScope)) {
					this.initialization.computeConversion(initializationScope, fieldType, initializationType);
					if (this.initialization instanceof CastExpression
							&& (this.initialization.bits & ASTNode.UnnecessaryCast) == 0) {
						CastExpression.checkNeedForAssignedCast(initializationScope, fieldType, (CastExpression) this.initialization);
					}
				} else {
					if ((fieldType.tagBits & TagBits.HasMissingType) == 0) {
						// if problem already got signaled on type, do not report secondary problem
						initializationScope.problemReporter().typeMismatchError(initializationType, fieldType, this.initialization, null);
					}
				}
				if (this.binding.isFinal()){ // cast from constant actual type to variable type
					this.binding.setConstant(this.initialization.constant.castTo((this.binding.type.id << 4) + this.initialization.constant.typeID()));
				}
			} else {
				this.binding.setConstant(Constant.NotAConstant);
			}
			// check for assignment with no effect
			if (this.binding == Expression.getDirectBinding(this.initialization)) {
				initializationScope.problemReporter().assignmentHasNoEffect(this, this.name);
			}
		}
	} finally {
		initializationScope.initializedField = previousField;
		initializationScope.lastVisibleFieldID = previousFieldID;
		if (this.binding.constant(initializationScope) == null)
			this.binding.setConstant(Constant.NotAConstant);
	}
}
public void resolveJavadoc(MethodScope initializationScope) {
	if (this.javadoc != null) {
		FieldBinding previousField = initializationScope.initializedField;
		int previousFieldID = initializationScope.lastVisibleFieldID;
		try {
			initializationScope.initializedField = this.binding;
			if (this.binding != null)
				initializationScope.lastVisibleFieldID = this.binding.id;
			this.javadoc.resolve(initializationScope);
		} finally {
			initializationScope.initializedField = previousField;
			initializationScope.lastVisibleFieldID = previousFieldID;
		}
	} else if (this.binding != null && this.binding.declaringClass != null && !this.binding.declaringClass.isLocalType()) {
		// Set javadoc visibility
		int javadocVisibility = this.binding.modifiers & ExtraCompilerModifiers.AccVisibilityMASK;
		ProblemReporter reporter = initializationScope.problemReporter();
		int severity = reporter.computeSeverity(IProblem.JavadocMissing);
		if (severity != ProblemSeverities.Ignore) {
			ClassScope classScope = initializationScope.enclosingClassScope();
			if (classScope != null) {
				javadocVisibility = Util.computeOuterMostVisibility(classScope.referenceType(), javadocVisibility);
			}
			int javadocModifiers = (this.binding.modifiers & ~ExtraCompilerModifiers.AccVisibilityMASK) | javadocVisibility;
			reporter.javadocMissing(this.sourceStart, this.sourceEnd, severity, javadocModifiers);
		}
	}
}

public void traverse(ASTVisitor visitor, MethodScope scope) {
	if (visitor.visit(this, scope)) {
		if (this.javadoc != null) {
			this.javadoc.traverse(visitor, scope);
		}
		if (this.annotations != null) {
			int annotationsLength = this.annotations.length;
			for (int i = 0; i < annotationsLength; i++)
				this.annotations[i].traverse(visitor, scope);
		}
		if (this.type != null) {
			this.type.traverse(visitor, scope);
		}
		if (this.initialization != null)
			this.initialization.traverse(visitor, scope);
	}
	visitor.endVisit(this, scope);
}
}

Back to the top