org.eclipse.jdt.internal.compiler.ast.UnlikelyArgumentCheck Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ecj Show documentation
Show all versions of ecj Show documentation
Eclipse Compiler for Java(TM)
/*******************************************************************************
* Copyright (c) 2015, 2017 GK Software AG 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:
* Stephan Herrmann - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;
import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
import org.eclipse.jdt.internal.compiler.lookup.ParameterizedTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
import org.eclipse.jdt.internal.compiler.lookup.Scope;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeConstants.DangerousMethod;
import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
/* @NonNullByDefault */
public class UnlikelyArgumentCheck {
public final DangerousMethod dangerousMethod;
public final TypeBinding typeToCheck;
public final TypeBinding expectedType;
public final TypeBinding typeToReport;
private UnlikelyArgumentCheck(DangerousMethod dangerousMethod, TypeBinding typeToCheck, TypeBinding expectedType,
TypeBinding typeToReport) {
this.dangerousMethod = dangerousMethod;
this.typeToCheck = typeToCheck;
this.expectedType = expectedType;
this.typeToReport = typeToReport;
}
/**
* Check if the invocation is likely a bug.
* @param currentScope
* @return false, if the typeToCheck does not seem to related to the expectedType
*/
public boolean isDangerous(BlockScope currentScope) {
TypeBinding typeToCheck2 = this.typeToCheck;
// take autoboxing into account
if (typeToCheck2.isBaseType()) {
typeToCheck2 = currentScope.boxing(typeToCheck2);
}
TypeBinding expectedType2 = this.expectedType;
if (expectedType2.isBaseType()) { // can happen for the first parameter of java.util.Object.equals
expectedType2 = currentScope.boxing(expectedType2);
}
if(this.dangerousMethod != DangerousMethod.Equals && currentScope.compilerOptions().reportUnlikelyCollectionMethodArgumentTypeStrict) {
return !typeToCheck2.isCompatibleWith(expectedType2, currentScope);
}
// unless both types are true type variables (not captures), take the erasure of both.
if (typeToCheck2.isCapture() || !typeToCheck2.isTypeVariable() || expectedType2.isCapture()
|| !expectedType2.isTypeVariable()) {
typeToCheck2 = typeToCheck2.erasure();
expectedType2 = expectedType2.erasure();
}
return !typeToCheck2.isCompatibleWith(expectedType2, currentScope)
&& !expectedType2.isCompatibleWith(typeToCheck2, currentScope);
}
/**
* When targeting a well-known dangerous method, returns an UnlikelyArgumentCheck object that contains the types to
* check against each other and to report
*/
public static /* @Nullable */ UnlikelyArgumentCheck determineCheckForNonStaticSingleArgumentMethod(
TypeBinding argumentType, Scope scope, char[] selector, TypeBinding actualReceiverType,
TypeBinding[] parameters) {
// detecting only methods with a single argument, typed either as Object or as Collection:
if (parameters.length != 1)
return null;
int paramTypeId = parameters[0].original().id;
if (paramTypeId != TypeIds.T_JavaLangObject && paramTypeId != TypeIds.T_JavaUtilCollection)
return null;
// check selectors before typeBits as to avoid unnecessary super-traversals for the receiver type
DangerousMethod suspect = DangerousMethod.detectSelector(selector);
if (suspect == null)
return null;
if (actualReceiverType.hasTypeBit(TypeIds.BitMap)) {
if (paramTypeId == TypeIds.T_JavaLangObject) {
switch (suspect) {
case ContainsKey:
case Get:
case Remove:
// map operations taking a key
ReferenceBinding mapType = actualReceiverType
.findSuperTypeOriginatingFrom(TypeIds.T_JavaUtilMap, false);
if (mapType != null && mapType.isParameterizedType())
return new UnlikelyArgumentCheck(suspect, argumentType,
((ParameterizedTypeBinding) mapType).typeArguments()[0], mapType);
break;
case ContainsValue:
// map operation taking a value
mapType = actualReceiverType.findSuperTypeOriginatingFrom(TypeIds.T_JavaUtilMap, false);
if (mapType != null && mapType.isParameterizedType())
return new UnlikelyArgumentCheck(suspect, argumentType,
((ParameterizedTypeBinding) mapType).typeArguments()[1], mapType);
break;
default: // no other suspects are detected in java.util.Map
}
}
}
if (actualReceiverType.hasTypeBit(TypeIds.BitCollection)) {
if (paramTypeId == TypeIds.T_JavaLangObject) {
switch (suspect) {
case Remove:
case Contains:
// collection operations taking a single element
ReferenceBinding collectionType = actualReceiverType
.findSuperTypeOriginatingFrom(TypeIds.T_JavaUtilCollection, false);
if (collectionType != null && collectionType.isParameterizedType())
return new UnlikelyArgumentCheck(suspect, argumentType,
((ParameterizedTypeBinding) collectionType).typeArguments()[0], collectionType);
break;
default: // no other suspects with Object-parameter are detected in java.util.Collection
}
} else if (paramTypeId == TypeIds.T_JavaUtilCollection) {
switch (suspect) {
case RemoveAll:
case ContainsAll:
case RetainAll:
// collection operations taking another collection
ReferenceBinding collectionType = actualReceiverType
.findSuperTypeOriginatingFrom(TypeIds.T_JavaUtilCollection, false);
ReferenceBinding argumentCollectionType = argumentType
.findSuperTypeOriginatingFrom(TypeIds.T_JavaUtilCollection, false);
if (collectionType != null && argumentCollectionType != null
&& argumentCollectionType.isParameterizedTypeWithActualArguments()
&& collectionType.isParameterizedTypeWithActualArguments()) {
return new UnlikelyArgumentCheck(suspect,
((ParameterizedTypeBinding) argumentCollectionType).typeArguments()[0],
((ParameterizedTypeBinding) collectionType).typeArguments()[0], collectionType);
}
break;
default: // no other suspects with Collection-parameter are detected in java.util.Collection
}
}
if (actualReceiverType.hasTypeBit(TypeIds.BitList)) {
if (paramTypeId == TypeIds.T_JavaLangObject) {
switch (suspect) {
case IndexOf:
case LastIndexOf:
// list operations taking a single element
ReferenceBinding listType = actualReceiverType
.findSuperTypeOriginatingFrom(TypeIds.T_JavaUtilList, false);
if (listType != null && listType.isParameterizedType())
return new UnlikelyArgumentCheck(suspect, argumentType,
((ParameterizedTypeBinding) listType).typeArguments()[0], listType);
break;
default: // no other suspects are detected in java.util.List
}
}
}
}
if (paramTypeId == TypeIds.T_JavaLangObject && suspect == DangerousMethod.Equals) {
return new UnlikelyArgumentCheck(suspect, argumentType, actualReceiverType, actualReceiverType);
}
return null; // not replacing
}
public static /* @Nullable */ UnlikelyArgumentCheck determineCheckForStaticTwoArgumentMethod(
TypeBinding secondParameter, Scope scope, char[] selector, TypeBinding firstParameter,
TypeBinding[] parameters, TypeBinding actualReceiverType) {
// detecting only methods with two arguments, both typed as Object:
if (parameters.length != 2)
return null;
int paramTypeId1 = parameters[0].original().id;
int paramTypeId2 = parameters[1].original().id;
if (paramTypeId1 != TypeIds.T_JavaLangObject || paramTypeId2 != TypeIds.T_JavaLangObject)
return null;
// check selectors before typeBits as to avoid unnecessary super-traversals for the receiver type
DangerousMethod suspect = DangerousMethod.detectSelector(selector);
if (suspect == null)
return null;
if (actualReceiverType.id == TypeIds.T_JavaUtilObjects && suspect == DangerousMethod.Equals) {
return new UnlikelyArgumentCheck(suspect, secondParameter, firstParameter, firstParameter);
}
return null;
}
}