All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.jdt.internal.compiler.ast.Statement Maven / Gradle / Ivy

There is a newer version: 3.39.0
Show newest version
/*******************************************************************************
 * Copyright (c) 2000, 2023 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 - Contributions for
 *								bug 335093 - [compiler][null] minimal hook for future null annotation support
 *								bug 349326 - [1.7] new warning for missing try-with-resources
 *								bug 186342 - [compiler][null] Using annotations for null checking
 *								bug 365983 - [compiler][null] AIOOB with null annotation analysis and varargs
 *								bug 368546 - [compiler][resource] Avoid remaining false positives found when compiling the Eclipse SDK
 *								bug 370930 - NonNull annotation not considered for enhanced for loops
 *								bug 365859 - [compiler][null] distinguish warnings based on flow analysis vs. null annotations
 *								bug 392862 - [1.8][compiler][null] Evaluate null annotations on array types
 *								bug 331649 - [compiler][null] consider null annotations for fields
 *								bug 383368 - [compiler][null] syntactic null analysis for field references
 *								Bug 392099 - [1.8][compiler][null] Apply null annotation on types for null analysis
 *								Bug 415043 - [1.8][null] Follow-up re null type annotations after bug 392099
 *								Bug 415291 - [1.8][null] differentiate type incompatibilities due to null annotations
 *								Bug 392238 - [1.8][compiler][null] Detect semantically invalid null type annotations
 *								Bug 416307 - [1.8][compiler][null] subclass with type parameter substitution confuses null checking
 *								Bug 417758 - [1.8][null] Null safety compromise during array creation.
 *								Bug 400874 - [1.8][compiler] Inference infrastructure should evolve to meet JLS8 18.x (Part G of JSR335 spec)
 *								Bug 424415 - [1.8][compiler] Eventual resolution of ReferenceExpression is not seen to be happening.
 *								Bug 418537 - [1.8][null] Fix null type annotation analysis for poly conditional expressions
 *								Bug 428352 - [1.8][compiler] Resolution errors don't always surface
 *								Bug 429430 - [1.8] Lambdas and method reference infer wrong exception type with generics (RuntimeException instead of IOException)
 *								Bug 435805 - [1.8][compiler][null] Java 8 compiler does not recognize declaration style null annotations
 *								Bug 453483 - [compiler][null][loop] Improve null analysis for loops
 *								Bug 455723 - Nonnull argument not correctly inferred in loop
 *        Andy Clement - Contributions for
 *                          Bug 383624 - [1.8][compiler] Revive code generation support for type annotations (from Olivier's work)
 *                          Bug 409250 - [1.8][compiler] Various loose ends in 308 code generation
 *******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;

import java.util.function.BooleanSupplier;

import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.compiler.ASTVisitor;
import org.eclipse.jdt.internal.compiler.ast.NullAnnotationMatching.CheckMode;
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.impl.CompilerOptions;
import org.eclipse.jdt.internal.compiler.lookup.*;

public abstract class Statement extends ASTNode {

	/**
	 * Answers true if the if is identified as a known coding pattern which
	 * should be tolerated by dead code analysis.
	 * e.g. if (DEBUG) print(); // no complaint
	 * Only invoked when overall condition is known to be optimizeable into false/true.
	 */
	protected static boolean isKnowDeadCodePattern(Expression expression) {
		// if (!DEBUG) print(); - tolerated
		if (expression instanceof UnaryExpression) {
			expression = ((UnaryExpression) expression).expression;
		}
		// if (DEBUG) print(); - tolerated
		if (expression instanceof Reference) return true;

//		if (expression instanceof BinaryExpression) {
//			BinaryExpression binary = (BinaryExpression) expression;
//			switch ((binary.bits & ASTNode.OperatorMASK) >> ASTNode.OperatorSHIFT/* operator */) {
//				case OperatorIds.AND_AND :
//				case OperatorIds.OR_OR :
//					break;
//				default:
//					// if (DEBUG_LEVEL > 0) print(); - tolerated
//					if ((binary.left instanceof Reference) && binary.right.constant != Constant.NotAConstant)
//						return true;
//					// if (0 < DEBUG_LEVEL) print(); - tolerated
//					if ((binary.right instanceof Reference) && binary.left.constant != Constant.NotAConstant)
//						return true;
//			}
//		}
		return false;
	}
public abstract FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo);
/** Lambda shape analysis: *Assuming* this is reachable, analyze if this completes normally i.e control flow can reach the textually next statement.
   For blocks, we don't perform intra-reachability analysis. We assume the lambda body is free of intrinsic control flow errors (if such errors
   exist they will not be flagged by this analysis, but are guaranteed to surface later on.)

   @see Block#doesNotCompleteNormally()
*/
public boolean doesNotCompleteNormally() {
	return false;
}

/** Lambda shape analysis: *Assuming* this is reachable, analyze if this completes by continuing i.e control flow cannot reach the textually next statement.
    This is necessitated by the fact that continue claims to not complete normally. So this is necessary to discriminate between do { continue; } while (false);
    which completes normally and do { throw new Exception(); } while (false); which does not complete normally.
*/
public boolean completesByContinue() {
	return false;
}

/**
 * Switch Expression analysis: *Assuming* this is reachable, analyze if this completes normally
 *  i.e control flow can reach the textually next statement, as per JLS 14 Sec 14.22
 *  For blocks, we don't perform intra-reachability analysis.
 *  Note: delinking this from a similar (opposite) {@link #doesNotCompleteNormally()} since that was
 *  coded for a specific purpose of Lambda Shape Analysis.
 */
public boolean canCompleteNormally() {
	return true;
}
/**
 * The equivalent function of completesByContinue - implements both the rules concerning continue with
 * and without a label.
 */
public boolean continueCompletes() {
	return false;
}
	public static final int NOT_COMPLAINED = 0;
	public static final int COMPLAINED_FAKE_REACHABLE = 1;
	public static final int COMPLAINED_UNREACHABLE = 2;
	LocalVariableBinding[] patternVarsWhenTrue;
	LocalVariableBinding[] patternVarsWhenFalse;


/** Analysing arguments of MessageSend, ExplicitConstructorCall, AllocationExpression. */
protected void analyseArguments(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo, MethodBinding methodBinding, Expression[] arguments)
{
	// compare actual null-status against parameter annotations of the called method:
	if (arguments != null) {
		CompilerOptions compilerOptions = currentScope.compilerOptions();
		if (compilerOptions.sourceLevel >= ClassFileConstants.JDK1_7 && methodBinding.isPolymorphic())
			return;
		boolean considerTypeAnnotations = currentScope.environment().usesNullTypeAnnotations();
		boolean hasJDK15NullAnnotations = methodBinding.parameterNonNullness != null;
		int numParamsToCheck = methodBinding.parameters.length;
		int varArgPos = -1;
		TypeBinding varArgsType = null;
		boolean passThrough = false;
		if (considerTypeAnnotations || hasJDK15NullAnnotations) {
			// check if varargs need special treatment:
			if (methodBinding.isVarargs()) {
				varArgPos = numParamsToCheck-1;
				// this if-block essentially copied from generateArguments(..):
				varArgsType = methodBinding.parameters[varArgPos];
				if (numParamsToCheck == arguments.length) {
					TypeBinding lastType = arguments[varArgPos].resolvedType;
					if (lastType == TypeBinding.NULL
							|| (varArgsType.dimensions() == lastType.dimensions()
							&& lastType.isCompatibleWith(varArgsType)))
						passThrough = true; // pass directly as-is
				}
				if (!passThrough)
					numParamsToCheck--; // with non-passthrough varargs last param is fed from individual args -> don't check
			}
		}
		if (considerTypeAnnotations) {
			for (int i=0; i paramLength) {
			// right number but not directly compatible or too many arguments - wrap extra into array
			// called with (argLength - lastIndex) elements : foo(1, 2) or foo(1, 2, 3, 4)
			// need to gen elements into an array, then gen each remaining element into created array
			codeStream.generateInlinedValue(argLength - varArgIndex);
			codeStream.newArray(codeGenVarArgsType); // create a mono-dimensional array
			for (int i = varArgIndex; i < argLength; i++) {
				codeStream.dup();
				codeStream.generateInlinedValue(i - varArgIndex);
				arguments[i].generateCode(currentScope, codeStream, true);
				codeStream.arrayAtPut(elementsTypeID, false);
			}
		} else if (argLength == paramLength) {
			// right number of arguments - could be inexact - pass argument as is
			TypeBinding lastType = arguments[varArgIndex].resolvedType;
			if (lastType == TypeBinding.NULL
				|| (varArgsType.dimensions() == lastType.dimensions()
					&& lastType.isCompatibleWith(codeGenVarArgsType))) {
				// foo(1, new int[]{2, 3}) or foo(1, null) --> last arg is passed as-is
				arguments[varArgIndex].generateCode(currentScope, codeStream, true);
			} else {
				// right number but not directly compatible or too many arguments - wrap extra into array
				// need to gen elements into an array, then gen each remaining element into created array
				codeStream.generateInlinedValue(1);
				codeStream.newArray(codeGenVarArgsType); // create a mono-dimensional array
				codeStream.dup();
				codeStream.generateInlinedValue(0);
				arguments[varArgIndex].generateCode(currentScope, codeStream, true);
				codeStream.arrayAtPut(elementsTypeID, false);
			}
		} else { // not enough arguments - pass extra empty array
			// scenario: foo(1) --> foo(1, new int[0])
			// generate code for an empty array of parameterType
			codeStream.generateInlinedValue(0);
			codeStream.newArray(codeGenVarArgsType); // create a mono-dimensional array
		}
	} else if (arguments != null) { // standard generation for method arguments
		for (int i = 0, max = arguments.length; i < max; i++)
			arguments[i].generateCode(currentScope, codeStream, true);
	}
}

public abstract void generateCode(BlockScope currentScope, CodeStream codeStream);

public boolean isBoxingCompatible(TypeBinding expressionType, TypeBinding targetType, Expression expression, Scope scope) {
	if (scope.isBoxingCompatibleWith(expressionType, targetType))
		return true;

	return expressionType.isBaseType()  // narrowing then boxing ? Only allowed for some target types see 362279
		&& !targetType.isBaseType()
		&& !targetType.isTypeVariable()
		&& scope.compilerOptions().sourceLevel >= org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants.JDK1_5 // autoboxing
		&& (targetType.id == TypeIds.T_JavaLangByte || targetType.id == TypeIds.T_JavaLangShort || targetType.id == TypeIds.T_JavaLangCharacter)
		&& expression.isConstantValueOfTypeAssignableToType(expressionType, scope.environment().computeBoxingType(targetType));
}

public boolean isEmptyBlock() {
	return false;
}

public boolean isValidJavaStatement() {
	//the use of this method should be avoid in most cases
	//and is here mostly for documentation purpose.....
	//while the parser is responsible for creating
	//welled formed expression statement, which results
	//in the fact that java-non-semantic-expression-used-as-statement
	//should not be parsed...thus not being built.
	//It sounds like the java grammar as help the compiler job in removing
	//-by construction- some statement that would have no effect....
	//(for example all expression that may do side-effects are valid statement
	// -this is an approximative idea.....-)

	return true;
}

@Override
public StringBuffer print(int indent, StringBuffer output) {
	return printStatement(indent, output);
}

public abstract StringBuffer printStatement(int indent, StringBuffer output);

public abstract void resolve(BlockScope scope);
public LocalVariableBinding[] getPatternVariablesWhenTrue() {
	return this.patternVarsWhenTrue;
}
public LocalVariableBinding[] getPatternVariablesWhenFalse() {
	return this.patternVarsWhenFalse;
}
public void addPatternVariablesWhenTrue(LocalVariableBinding[] vars) {
	if (vars == null || vars.length == 0) return;
	this.patternVarsWhenTrue = addPatternVariables(this.patternVarsWhenTrue, vars);
}
public void addPatternVariablesWhenFalse(LocalVariableBinding[] vars) {
	if (vars == null || vars.length == 0) return;
	this.patternVarsWhenFalse = addPatternVariables(this.patternVarsWhenFalse, vars);
}
private LocalVariableBinding[] addPatternVariables(LocalVariableBinding[] current, LocalVariableBinding[] add) {
	if (add == null || add.length == 0)
		return current;
	if (current == null) {
		current = add;
	} else {
		for (LocalVariableBinding local : add) {
			current = addPatternVariables(current, local);
		}
	}
	return current;
}
private LocalVariableBinding[] addPatternVariables(LocalVariableBinding[] current, LocalVariableBinding add) {
	int oldSize = current.length;
	// it's odd that we only look at the last element, but in most cases
	// we will only have one in the array. In the unlikely case of having two
	// distinct pattern variables, the cost is nothing but setting the same
	// bit twice on the same object.
	if (oldSize > 0 && current[oldSize - 1] == add) {
		return current;
	}
	int newLength = current.length + 1;
	System.arraycopy(current, 0, (current = new LocalVariableBinding[newLength]), 0, oldSize);
	current[oldSize] = add;
	return current;
}
public void promotePatternVariablesIfApplicable(LocalVariableBinding[] patternVariablesInScope, BooleanSupplier condition) {
	if (patternVariablesInScope != null && condition.getAsBoolean()) {
		for (LocalVariableBinding binding : patternVariablesInScope) {
			binding.modifiers &= ~ExtraCompilerModifiers.AccPatternVariable;
		}
	}
}
public void resolveWithPatternVariablesInScope(LocalVariableBinding[] patternVariablesInScope, BlockScope scope) {
	if (patternVariablesInScope != null) {
		for (LocalVariableBinding binding : patternVariablesInScope) {
			binding.modifiers &= ~ExtraCompilerModifiers.AccPatternVariable;
		}
		this.resolve(scope);
		for (LocalVariableBinding binding : patternVariablesInScope) {
			binding.modifiers |= ExtraCompilerModifiers.AccPatternVariable;
		}
	} else {
		resolve(scope);
	}
}
/**
 * Returns the resolved expression if any associated to this statement - used
 * parameter statement has to be either a SwitchStatement or a SwitchExpression
 */
public TypeBinding resolveExpressionType(BlockScope scope) {
	return null;
}
public boolean containsPatternVariable() {
	return false;
}
/**
 * Implementation of {@link org.eclipse.jdt.internal.compiler.lookup.InvocationSite#invocationTargetType}
 * suitable at this level. Subclasses should override as necessary.
 * @see org.eclipse.jdt.internal.compiler.lookup.InvocationSite#invocationTargetType()
 */
public TypeBinding invocationTargetType() {
	return null;
}
/** Simpler notion of expected type, suitable for code assist purposes. */
public TypeBinding expectedType() {
	// for all but FunctionalExpressions, this is the same as invocationTargetType.
	return invocationTargetType();
}
public ExpressionContext getExpressionContext() {
	return ExpressionContext.VANILLA_CONTEXT;
}
/**
 * For all constructor invocations: find the constructor binding;
 * if site.innersNeedUpdate() perform some post processing for those and produce
 * any updates as side-effects into 'argumentTypes'.
 */
protected MethodBinding findConstructorBinding(BlockScope scope, Invocation site, ReferenceBinding receiverType, TypeBinding[] argumentTypes) {
	MethodBinding ctorBinding = scope.getConstructor(receiverType, argumentTypes, site);
	return resolvePolyExpressionArguments(site, ctorBinding, argumentTypes, scope);
}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy