blob: 62d32de2af3681626d22a555344a2a688799ff89 [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.netbeans.modules.java.hints.bugs;
import com.sun.source.util.TreePath;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Types;
import org.netbeans.api.java.source.TreeUtilities;
import org.netbeans.spi.editor.hints.ErrorDescription;
import org.netbeans.spi.editor.hints.Fix;
import org.netbeans.spi.java.hints.HintContext;
import org.netbeans.spi.java.hints.JavaFix;
import org.netbeans.spi.java.hints.BooleanOption;
import org.netbeans.spi.java.hints.ConstraintVariableType;
import org.netbeans.spi.java.hints.Hint;
import org.netbeans.spi.java.hints.Hint.Options;
import org.netbeans.spi.java.hints.TriggerPattern;
import org.netbeans.spi.java.hints.TriggerPatterns;
import org.netbeans.spi.java.hints.UseOptions;
import org.netbeans.spi.java.hints.ErrorDescriptionFactory;
import org.netbeans.spi.java.hints.JavaFixUtilities;
import org.openide.util.NbBundle;
/**
*
* @author Jan Lahoda
*/
public class EqualsHint {
private static final boolean ERASURE_PREFS_DEFAULT = true; // NOI18N
@BooleanOption(displayName = "#LBL_org.netbeans.modules.java.hints.bugs.EqualsHint.ERASURE_PREFS_KEY", tooltip = "#TP_org.netbeans.modules.java.hints.bugs.EqualsHint.ERASURE_PREFS_KEY", defaultValue=ERASURE_PREFS_DEFAULT)
private static final String ERASURE_PREFS_KEY = "eguals-hint-erasure"; // NOI18N
@Hint(displayName = "#DN_org.netbeans.modules.java.hints.bugs.EqualsHint.arrayEquals", description = "#DESC_org.netbeans.modules.java.hints.bugs.EqualsHint.arrayEquals", category="bugs", suppressWarnings="ArrayEquals")
@TriggerPatterns({
@TriggerPattern(value="$obj.equals($arr)",
constraints={
@ConstraintVariableType(variable="$obj", type="java.lang.Object"),
@ConstraintVariableType(variable="$arr", type="java.lang.Object[]")
}),
@TriggerPattern(value="$obj.equals($arr)",
constraints={
@ConstraintVariableType(variable="$obj", type="java.lang.Object"),
@ConstraintVariableType(variable="$arr", type="boolean[]")
}),
@TriggerPattern(value="$obj.equals($arr)",
constraints={
@ConstraintVariableType(variable="$obj", type="java.lang.Object"),
@ConstraintVariableType(variable="$arr", type="byte[]")
}),
@TriggerPattern(value="$obj.equals($arr)",
constraints={
@ConstraintVariableType(variable="$obj", type="java.lang.Object"),
@ConstraintVariableType(variable="$arr", type="short[]")
}),
@TriggerPattern(value="$obj.equals($arr)",
constraints={
@ConstraintVariableType(variable="$obj", type="java.lang.Object"),
@ConstraintVariableType(variable="$arr", type="char[]")
}),
@TriggerPattern(value="$obj.equals($arr)",
constraints={
@ConstraintVariableType(variable="$obj", type="java.lang.Object"),
@ConstraintVariableType(variable="$arr", type="int[]")
}),
@TriggerPattern(value="$obj.equals($arr)",
constraints={
@ConstraintVariableType(variable="$obj", type="java.lang.Object"),
@ConstraintVariableType(variable="$arr", type="long[]")
}),
@TriggerPattern(value="$obj.equals($arr)",
constraints={
@ConstraintVariableType(variable="$obj", type="java.lang.Object"),
@ConstraintVariableType(variable="$arr", type="float[]")
}),
@TriggerPattern(value="$obj.equals($arr)",
constraints={
@ConstraintVariableType(variable="$obj", type="java.lang.Object"),
@ConstraintVariableType(variable="$arr", type="double[]")
})
})
public static ErrorDescription arrayEquals(HintContext ctx) {
//XXX: this check should not be needed:
TreePath arr = ctx.getVariables().get("$arr");
TypeMirror tm = ctx.getInfo().getTrees().getTypeMirror(arr);
if (tm == null || tm.getKind() != TypeKind.ARRAY) {
return null;
}
//XXX end
String fixArraysDisplayName = NbBundle.getMessage(EqualsHint.class, "FIX_ReplaceWithArraysEquals");
Fix arrays = JavaFixUtilities.rewriteFix(ctx, fixArraysDisplayName, ctx.getPath(), "java.util.Arrays.equals($obj, $arr)");
String fixInstanceDisplayName = NbBundle.getMessage(EqualsHint.class, "FIX_ReplaceWithInstanceEquals");
Fix instance = JavaFixUtilities.rewriteFix(ctx, fixInstanceDisplayName, ctx.getPath(), "$obj == $arr");
String displayName = NbBundle.getMessage(EqualsHint.class, "ERR_ARRAY_EQUALS");
return ErrorDescriptionFactory.forName(ctx, ctx.getPath(), displayName, arrays, instance);
}
@Hint(displayName = "#DN_org.netbeans.modules.java.hints.EqualsHint", description = "#DESC_org.netbeans.modules.java.hints.EqualsHint", id="org.netbeans.modules.java.hints.EqualsHint", category="bugs", suppressWarnings={"IncompatibleEquals", "", "EqualsBetweenInconvertibleTypes"}, options=Options.QUERY)
@UseOptions(ERASURE_PREFS_KEY)
@TriggerPattern(value="$this.equals($par)",
constraints={
@ConstraintVariableType(variable="$this", type="java.lang.Object"),
@ConstraintVariableType(variable="$par", type="java.lang.Object")
})
public static ErrorDescription incompatibleEquals(HintContext ctx) {
TreePath ths = ctx.getVariables().get("$this");
TreePath par = ctx.getVariables().get("$par");
TypeMirror thsType;
if (ths != null) {
thsType = ctx.getInfo().getTrees().getTypeMirror(ths);
} else {
TreePath cls = ctx.getPath();
while (cls != null && !TreeUtilities.CLASS_TREE_KINDS.contains(cls.getLeaf().getKind())) {
cls = cls.getParentPath();
}
if (cls == null) {
return null;
}
thsType = ctx.getInfo().getTrees().getTypeMirror(cls);
}
if (thsType == null || thsType.getKind() != TypeKind.DECLARED) {
return null;
}
TypeMirror parType = ctx.getInfo().getTrees().getTypeMirror(par);
if (parType == null || parType.getKind() != TypeKind.DECLARED) {
return null;
}
if (ctx.getPreferences().getBoolean(ERASURE_PREFS_KEY, ERASURE_PREFS_DEFAULT)) {
Types types = ctx.getInfo().getTypes();
thsType = types.erasure(thsType);
parType = types.erasure(parType);
}
boolean castable = ctx.getInfo().getTypeUtilities().isCastable(thsType, parType) || ctx.getInfo().getTypeUtilities().isCastable(parType, thsType);
if (castable) {
return null;
}
String displayName = NbBundle.getMessage(EqualsHint.class, "ERR_INCOMPATIBLE_EQUALS"); // NOI18N
return ErrorDescriptionFactory.forName(ctx, ctx.getPath(), displayName);
}
}