Merge remote-tracking branch 'origin/master'

This commit is contained in:
Roman Shevchenko
2017-06-06 17:19:20 +03:00
321 changed files with 4892 additions and 3134 deletions
@@ -22,7 +22,6 @@ import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiReference;
import com.intellij.psi.search.PsiElementProcessor;
import com.intellij.psi.tree.TokenSet;
import com.intellij.psi.util.PsiElementFilter;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.IncorrectOperationException;
import org.intellij.lang.regexp.RegExpTT;
@@ -53,15 +52,12 @@ public class RegExpNamedGroupRefImpl extends RegExpElementImpl implements RegExp
@Nullable
public RegExpGroup resolve() {
final PsiElementProcessor.FindFilteredElement<RegExpGroup> processor = new PsiElementProcessor.FindFilteredElement<>(
new PsiElementFilter() {
@Override
public boolean isAccepted(PsiElement element) {
if (!(element instanceof RegExpGroup)) {
return false;
}
final RegExpGroup group = (RegExpGroup)element;
return group.isAnyNamedGroup() && Comparing.equal(getGroupName(), group.getGroupName());
element -> {
if (!(element instanceof RegExpGroup)) {
return false;
}
final RegExpGroup group = (RegExpGroup)element;
return group.isAnyNamedGroup() && Comparing.equal(getGroupName(), group.getGroupName());
}
);
PsiTreeUtil.processElements(getContainingFile(), processor);
@@ -142,16 +138,7 @@ public class RegExpNamedGroupRefImpl extends RegExpElementImpl implements RegExp
@NotNull
public Object[] getVariants() {
final PsiElementProcessor.CollectFilteredElements<RegExpGroup> processor = new PsiElementProcessor.CollectFilteredElements<>(
new PsiElementFilter() {
@Override
public boolean isAccepted(PsiElement element) {
if (!(element instanceof RegExpGroup)) {
return false;
}
final RegExpGroup regExpGroup = (RegExpGroup)element;
return regExpGroup.isAnyNamedGroup();
}
}
e -> e instanceof RegExpGroup && ((RegExpGroup)e).isAnyNamedGroup()
);
PsiTreeUtil.processElements(getContainingFile(), processor);
return processor.toArray();
@@ -20,7 +20,9 @@ import com.intellij.codeInspection.ProblemHighlightType;
import com.intellij.lang.ASTNode;
import com.intellij.lang.annotation.Annotation;
import com.intellij.lang.annotation.AnnotationHolder;
import com.intellij.lang.annotation.AnnotationSession;
import com.intellij.lang.annotation.Annotator;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiComment;
import com.intellij.psi.PsiElement;
@@ -35,7 +37,9 @@ import org.intellij.lang.regexp.psi.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public final class RegExpAnnotator extends RegExpElementVisitor implements Annotator {
@@ -43,6 +47,7 @@ public final class RegExpAnnotator extends RegExpElementVisitor implements Annot
"alnum", "alpha", "ascii", "blank", "cntrl", "digit", "graph", "lower", "print", "punct", "space", "upper", "word", "xdigit");
private AnnotationHolder myHolder;
private final RegExpLanguageHosts myLanguageHosts;
private final Key<Map<String, RegExpGroup>> NAMED_GROUP_MAP = new Key<>("REG_EXP_NAMED_GROUP_MAP");
public RegExpAnnotator() {
myLanguageHosts = RegExpLanguageHosts.getInstance();
@@ -288,6 +293,13 @@ public final class RegExpAnnotator extends RegExpElementVisitor implements Annot
final ASTNode node = group.getNode().findChildByType(RegExpTT.NAME);
if (node != null) myHolder.createErrorAnnotation(node, "Invalid group name");
}
final AnnotationSession session = myHolder.getCurrentAnnotationSession();
final Map<String, RegExpGroup> namedGroups = NAMED_GROUP_MAP.get(session, new HashMap<>());
if (namedGroups.isEmpty()) session.putUserData(NAMED_GROUP_MAP, namedGroups);
if (namedGroups.put(name, group) != null) {
final ASTNode node = group.getNode().findChildByType(RegExpTT.NAME);
if (node != null) myHolder.createErrorAnnotation(node, "Group with name '" + name + "' already defined");
}
final RegExpGroup.Type groupType = group.getType();
if (groupType == RegExpGroup.Type.POSITIVE_LOOKBEHIND || groupType == RegExpGroup.Type.NEGATIVE_LOOKBEHIND) {
final RegExpLanguageHost.Lookbehind support = myLanguageHosts.supportsLookbehind(group);
+11
View File
@@ -147,6 +147,17 @@ BITS=$?
"$RM" -f "$VERSION_LOG"
test ${BITS} -eq 0 && BITS="64" || BITS=""
#----------------------------------------------------------------------
# Set platform enviroment variables for IDE
#----------------------------------------------------------------------
if [ "$OS_TYPE" = "Linux" ] ; then
case "$XDG_CURRENT_DESKTOP" in
*Unity*)
export JAYATANA_FORCE=true
;;
esac
fi
# ---------------------------------------------------------------------
# Collect JVM options and IDE properties.
# ---------------------------------------------------------------------
Binary file not shown.
+1 -1
View File
@@ -1 +1 @@
172.SNAPSHOT
173.SNAPSHOT
@@ -1,7 +1,7 @@
<component xmlns="http://jetbrains.org/intellij/schema/application-info"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://jetbrains.org/intellij/schema/application-info http://jetbrains.org/intellij/schema/ApplicationInfo.xsd">
<version codename="Community Edition" major="2017" minor="2" eap="true"/>
<version codename="Community Edition" major="2017" minor="3" eap="true"/>
<company name="JetBrains s.r.o." url="https://www.jetbrains.com/?fromIDE"/>
<build number="__BUILD_NUMBER__" date="__BUILD_DATE__"/>
<logo url="/idea_community_logo.png" textcolor="000000" progressColor="ef5468" progressX="117" progressY="280" progressHeight="3" />
@@ -92,7 +92,7 @@ public class JavaValue extends XNamedValue implements NodeDescriptorProvider, XV
myContextSet = contextSet;
}
static JavaValue create(JavaValue parent,
public static JavaValue create(JavaValue parent,
@NotNull ValueDescriptorImpl valueDescriptor,
@NotNull EvaluationContextImpl evaluationContext,
NodeManagerImpl nodeManager,
@@ -364,11 +364,6 @@ public class JavaValue extends XNamedValue implements NodeDescriptorProvider, XV
return myValueDescriptor;
}
@Override
public void setRemaining(int remaining) {
node.tooManyChildren(remaining);
}
@Override
public void initChildrenArrayRenderer(ArrayRenderer renderer) {
renderer.START_INDEX = myCurrentChildrenStart;
@@ -408,6 +403,36 @@ public class JavaValue extends XNamedValue implements NodeDescriptorProvider, XV
@Nullable XDebuggerTreeNodeHyperlink link) {
node.setMessage(message, icon, attributes, link);
}
@Override
public void addChildren(@NotNull XValueChildrenList children, boolean last) {
node.addChildren(children, last);
}
@Override
public void tooManyChildren(int remaining) {
node.tooManyChildren(remaining);
}
@Override
public void setAlreadySorted(boolean alreadySorted) {
node.setAlreadySorted(alreadySorted);
}
@Override
public void setErrorMessage(@NotNull String errorMessage) {
node.setErrorMessage(errorMessage);
}
@Override
public void setErrorMessage(@NotNull String errorMessage, @Nullable XDebuggerTreeNodeHyperlink link) {
node.setErrorMessage(errorMessage, link);
}
@Override
public boolean isObsolete() {
return node.isObsolete();
}
}, myEvaluationContext);
}
});
@@ -0,0 +1,48 @@
/*
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed 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 com.intellij.debugger.ui;
import com.intellij.debugger.engine.DebugProcessImpl;
import com.intellij.openapi.util.Key;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author egor
*/
public class OverheadTimings {
public static final Key<OverheadTimings> KEY = Key.create("OVERHEAD_TIMINGS");
private final Map<Object, Long> myMap = new ConcurrentHashMap<>();
public static float get(DebugProcessImpl process, Object producer) {
return getTimings(process).myMap.get(producer);
}
public static void add(DebugProcessImpl process, Object producer, long overhead) {
getTimings(process).myMap.merge(producer, overhead, (old, value) -> old + value);
}
private static OverheadTimings getTimings(DebugProcessImpl process) {
OverheadTimings data = process.getUserData(KEY);
if (data == null) {
data = new OverheadTimings();
process.putUserData(KEY, data);
}
return data;
}
}
@@ -34,6 +34,7 @@ import com.intellij.debugger.requests.ClassPrepareRequestor;
import com.intellij.debugger.requests.Requestor;
import com.intellij.debugger.settings.DebuggerSettings;
import com.intellij.debugger.ui.impl.watch.CompilingEvaluatorImpl;
import com.intellij.debugger.ui.OverheadTimings;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ReadAction;
import com.intellij.openapi.project.Project;
@@ -222,40 +223,47 @@ public abstract class Breakpoint<P extends JavaBreakpointProperties> implements
@Override
public boolean processLocatableEvent(SuspendContextCommandImpl action, LocatableEvent event) throws EventProcessingException {
long start = System.currentTimeMillis();
SuspendContextImpl context = action.getSuspendContext();
if (!isValid()) {
context.getDebugProcess().getRequestsManager().deleteRequest(this);
return false;
}
String title = DebuggerBundle.message("title.error.evaluating.breakpoint.condition");
try {
StackFrameProxyImpl frameProxy = context.getThread().frame(0);
if (frameProxy == null) {
// might be if the thread has been collected
return false;
String title = DebuggerBundle.message("title.error.evaluating.breakpoint.condition");
try {
StackFrameProxyImpl frameProxy = context.getThread().frame(0);
if (frameProxy == null) {
// might be if the thread has been collected
return false;
}
EvaluationContextImpl evaluationContext = new EvaluationContextImpl(context, frameProxy, getThisObject(context, event));
if (!evaluateCondition(evaluationContext, event)) {
return false;
}
title = DebuggerBundle.message("title.error.evaluating.breakpoint.action");
runAction(evaluationContext, event);
}
catch (final EvaluateException ex) {
if (ApplicationManager.getApplication().isUnitTestMode()) {
System.out.println(ex.getMessage());
return false;
}
throw new EventProcessingException(title, ex.getMessage(), ex);
}
EvaluationContextImpl evaluationContext = new EvaluationContextImpl(context, frameProxy, getThisObject(context, event));
if (!evaluateCondition(evaluationContext, event)) {
return false;
}
title = DebuggerBundle.message("title.error.evaluating.breakpoint.action");
runAction(evaluationContext, event);
return true;
}
catch (final EvaluateException ex) {
if(ApplicationManager.getApplication().isUnitTestMode()) {
System.out.println(ex.getMessage());
return false;
}
throw new EventProcessingException(title, ex.getMessage(), ex);
finally {
OverheadTimings.add(context.getDebugProcess(), this, System.currentTimeMillis() - start);
}
return true;
}
private void runAction(EvaluationContextImpl context, LocatableEvent event) {
@@ -56,13 +56,17 @@ import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.ui.SimpleTextAttributes;
import com.intellij.ui.SpeedSearchComparator;
import com.intellij.ui.TreeSpeedSearch;
import com.intellij.xdebugger.frame.XDebuggerTreeNodeHyperlink;
import com.intellij.xdebugger.frame.XValueChildrenList;
import com.intellij.xdebugger.settings.XDebuggerSettingsManager;
import com.sun.jdi.*;
import com.sun.jdi.event.Event;
import com.sun.jdi.event.ExceptionEvent;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.event.TreeModelEvent;
@@ -576,9 +580,6 @@ public abstract class DebuggerTree extends DebuggerTreeBase implements DataProvi
return (ValueDescriptorImpl)getNode().getDescriptor();
}
@Override
public void setRemaining(int remaining) {}
@Override
public void initChildrenArrayRenderer(ArrayRenderer renderer) {}
@@ -591,6 +592,38 @@ public abstract class DebuggerTree extends DebuggerTreeBase implements DataProvi
}
updateUI(false);
}
@Override
public void addChildren(@NotNull XValueChildrenList children, boolean last) {
}
@Override
public void tooManyChildren(int remaining) {
}
@Override
public void setAlreadySorted(boolean alreadySorted) {
}
@Override
public void setErrorMessage(@NotNull String errorMessage) {
}
@Override
public void setErrorMessage(@NotNull String errorMessage, @Nullable XDebuggerTreeNodeHyperlink link) {
}
@Override
public void setMessage(@NotNull String message,
@Nullable Icon icon,
@NotNull SimpleTextAttributes attributes,
@Nullable XDebuggerTreeNodeHyperlink link) {
}
@Override
public boolean isObsolete() {
return false;
}
}
private class BuildStaticNodeCommand extends BuildNodeCommand {
@@ -21,21 +21,25 @@ import com.intellij.debugger.DebuggerManagerEx;
import com.intellij.debugger.actions.ArrayAction;
import com.intellij.debugger.engine.ContextUtil;
import com.intellij.debugger.engine.DebuggerManagerThreadImpl;
import com.intellij.debugger.engine.JavaValue;
import com.intellij.debugger.engine.evaluation.EvaluateException;
import com.intellij.debugger.engine.evaluation.EvaluationContext;
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
import com.intellij.debugger.engine.evaluation.TextWithImportsImpl;
import com.intellij.debugger.impl.DebuggerUtilsEx;
import com.intellij.debugger.memory.utils.ErrorsValueGroup;
import com.intellij.debugger.settings.NodeRendererSettings;
import com.intellij.debugger.settings.ViewsGeneralSettings;
import com.intellij.debugger.ui.impl.watch.ArrayElementDescriptorImpl;
import com.intellij.debugger.ui.impl.watch.NodeManagerImpl;
import com.intellij.debugger.ui.impl.watch.ValueDescriptorImpl;
import com.intellij.debugger.ui.tree.DebuggerTreeNode;
import com.intellij.debugger.ui.tree.NodeDescriptor;
import com.intellij.debugger.ui.tree.NodeDescriptorFactory;
import com.intellij.debugger.ui.tree.ValueDescriptor;
import com.intellij.icons.AllIcons;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.LanguageLevelProjectExtension;
import com.intellij.openapi.util.DefaultJDOMExternalizer;
import com.intellij.openapi.util.InvalidDataException;
@@ -49,6 +53,7 @@ import com.intellij.ui.SimpleTextAttributes;
import com.intellij.util.IncorrectOperationException;
import com.intellij.xdebugger.XExpression;
import com.intellij.xdebugger.frame.XDebuggerTreeNodeHyperlink;
import com.intellij.xdebugger.frame.XValueChildrenList;
import com.intellij.xdebugger.impl.ui.tree.XDebuggerTree;
import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl;
import com.sun.jdi.ArrayReference;
@@ -159,7 +164,7 @@ public class ArrayRenderer extends NodeRendererImpl{
builder.setMessage(DebuggerBundle.message("message.node.elements.null.hidden"), null, SimpleTextAttributes.REGULAR_ATTRIBUTES, null);
}
if (!myForced && END_INDEX < arrayLength - 1) {
builder.setRemaining(arrayLength - 1 - END_INDEX);
builder.tooManyChildren(arrayLength - 1 - END_INDEX);
}
}
}
@@ -252,6 +257,7 @@ public class ArrayRenderer extends NodeRendererImpl{
int added = 0;
if (arrayLength - 1 >= START_INDEX) {
ErrorsValueGroup errorsGroup = null;
for (int idx = START_INDEX; idx < arrayLength; idx++) {
try {
if (DebuggerUtilsEx.evaluateBoolean(cachedEvaluator.getEvaluator(evaluationContext.getProject()),
@@ -269,7 +275,17 @@ public class ArrayRenderer extends NodeRendererImpl{
}
}
catch (EvaluateException e) {
builder.addChildren(Collections.singletonList(nodeManager.createMessageNode(e.getMessage())), false);
if (errorsGroup == null) {
errorsGroup = new ErrorsValueGroup();
builder.addChildren(XValueChildrenList.bottomGroup(errorsGroup), false);
}
JavaValue childValue = JavaValue
.create(null,
(ValueDescriptorImpl)descriptorFactory.getArrayItemDescriptor(builder.getParentDescriptor(), array, idx),
((EvaluationContextImpl)evaluationContext),
nodeManager,
false);
errorsGroup.addErrorValue(e.getMessage(), childValue);
}
}
}
@@ -15,16 +15,15 @@
*/
package com.intellij.debugger.ui.tree.render;
import com.intellij.debugger.ui.tree.*;
import com.intellij.ui.SimpleTextAttributes;
import com.intellij.xdebugger.frame.XDebuggerTreeNodeHyperlink;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import com.intellij.debugger.ui.tree.DebuggerTreeNode;
import com.intellij.debugger.ui.tree.NodeDescriptorFactory;
import com.intellij.debugger.ui.tree.NodeManager;
import com.intellij.debugger.ui.tree.ValueDescriptor;
import com.intellij.xdebugger.frame.XCompositeNode;
import javax.swing.*;
import java.util.List;
public interface ChildrenBuilder {
public interface ChildrenBuilder extends XCompositeNode {
NodeDescriptorFactory getDescriptorManager();
NodeManager getNodeManager();
@@ -37,13 +36,10 @@ public interface ChildrenBuilder {
setChildren(children);
}
default void setMessage(@NotNull String message,
@Nullable Icon icon,
@NotNull SimpleTextAttributes attributes,
@Nullable XDebuggerTreeNodeHyperlink link) {
@Deprecated
default void setRemaining(int remaining) {
tooManyChildren(remaining);
}
void setRemaining(int remaining);
void initChildrenArrayRenderer(ArrayRenderer renderer);
}
@@ -195,7 +195,9 @@ public class HighlightMethodUtil {
if (returnType.equals(substitutedSuperReturnType)) return null;
if (!(returnType instanceof PsiPrimitiveType) && substitutedSuperReturnType.getDeepComponentType() instanceof PsiClassType) {
if (isJdk15 && TypeConversionUtil.isAssignable(substitutedSuperReturnType, returnType)) {
if (isJdk15 && LambdaUtil.performWithSubstitutedParameterBounds(methodSignature.getTypeParameters(),
methodSignature.getSubstitutor(),
() -> TypeConversionUtil.isAssignable(substitutedSuperReturnType, returnType))) {
return null;
}
}
@@ -1579,11 +1579,13 @@ public class HighlightUtil extends HighlightUtilBase {
final PsiElement parent = expr.getParent();
final PsiElement resolved = parent instanceof PsiReferenceExpression ? ((PsiReferenceExpression)parent).resolve() : null;
PsiClass containingClass =
ObjectUtils.notNull(resolved instanceof PsiMethod ? ((PsiMethod)resolved).getContainingClass() : null, aClass);
for (PsiClass superClass : classT.getSupers()) {
if (superClass.isInheritor(aClass, true)) {
if (superClass.isInheritor(containingClass, true)) {
String cause = null;
if (superClass.isInterface()) {
cause = "redundant interface " + format(aClass) + " is extended by ";
if (superClass.isInheritor(aClass, true) && superClass.isInterface()) {
cause = "redundant interface " + format(containingClass) + " is extended by ";
}
else if (resolved instanceof PsiMethod &&
MethodSignatureUtil.findMethodBySuperMethod(superClass, (PsiMethod)resolved, true) != resolved) {
@@ -20,6 +20,7 @@ import com.intellij.codeInsight.daemon.impl.HighlightInfo;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.refactoring.util.RefactoringChangeUtil;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
public class QualifySuperArgumentFix extends QualifyThisOrSuperArgumentFix {
@@ -50,7 +51,14 @@ public class QualifySuperArgumentFix extends QualifyThisOrSuperArgumentFix {
final PsiExpression superQualifierCopy = copy.getMethodExpression().getQualifierExpression();
LOG.assertTrue(superQualifierCopy != null);
superQualifierCopy.delete();
PsiMethod method = ((PsiMethodCallExpression)elementFactory.createExpressionFromText(copy.getText(), superClass)).resolveMethod();
PsiMethod method;
try {
method = ((PsiMethodCallExpression)elementFactory.createExpressionFromText(copy.getText(), superClass)).resolveMethod();
}
catch (IncorrectOperationException e) {
LOG.info(e);
return;
}
if (method != null && !method.hasModifierProperty(PsiModifier.ABSTRACT)) {
QuickFixAction.registerQuickFixAction(highlightInfo, new QualifySuperArgumentFix(expr, superClass));
}
@@ -1,157 +0,0 @@
/*
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed 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 com.intellij.codeInspection;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.siyeh.ig.psiutils.CommentTracker;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @author Tagir Valeev
*/
public class BoxForComparisonInspection extends BaseJavaBatchLocalInspectionTool {
@NotNull
@Override
public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
if(!PsiUtil.getLanguageLevel(holder.getFile()).isAtLeast(LanguageLevel.JDK_1_4)) {
return PsiElementVisitor.EMPTY_VISITOR;
}
return new JavaElementVisitor() {
@Override
public void visitMethodCallExpression(PsiMethodCallExpression call) {
PsiElement nameElement = call.getMethodExpression().getReferenceNameElement();
if (nameElement == null) return;
String name = nameElement.getText();
if (!"compareTo".equals(name)) return;
PsiExpression[] args = call.getArgumentList().getExpressions();
if (args.length != 1) return;
PsiExpression arg = args[0];
PsiExpression qualifier = call.getMethodExpression().getQualifierExpression();
if (qualifier == null) return;
PsiClassType boxedType = getBoxedType(call);
if (boxedType == null) return;
PsiPrimitiveType primitiveType = PsiPrimitiveType.getUnboxedType(boxedType);
if (primitiveType == null || !PsiType.DOUBLE.equals(primitiveType) && !PsiType.FLOAT.equals(primitiveType) &&
!PsiUtil.isLanguageLevel7OrHigher(call)) {
return;
}
PsiExpression left = extractPrimitive(boxedType, primitiveType, qualifier);
if (left == null) return;
PsiExpression right = extractPrimitive(boxedType, primitiveType, arg);
if (right == null) return;
holder.registerProblem(nameElement, "Can be replaced with '" + boxedType.getClassName() + ".compare'",
new ReplaceWithPrimitiveCompareFix(boxedType.getCanonicalText()));
}
};
}
@Nullable
static PsiClassType getBoxedType(PsiMethodCallExpression call) {
PsiMethod method = call.resolveMethod();
if (method == null) return null;
PsiClass aClass = method.getContainingClass();
if (aClass == null) return null;
return JavaPsiFacade.getElementFactory(call.getProject()).createType(aClass);
}
@Nullable
static PsiExpression extractPrimitive(PsiClassType type, PsiPrimitiveType primitiveType, PsiExpression expression) {
expression = PsiUtil.skipParenthesizedExprDown(expression);
if (expression == null) return null;
if (primitiveType.equals(expression.getType())) {
return expression;
}
if (expression instanceof PsiMethodCallExpression) {
PsiMethodCallExpression call = (PsiMethodCallExpression)expression;
if (!"valueOf".equals(call.getMethodExpression().getReferenceName())) return null;
PsiExpression[] args = call.getArgumentList().getExpressions();
if (args.length != 1) return null;
PsiMethod method = call.resolveMethod();
if (method == null || type.resolve() != method.getContainingClass()) return null;
return checkPrimitive(args[0]);
}
if (expression instanceof PsiTypeCastExpression) {
PsiTypeCastExpression cast = (PsiTypeCastExpression)expression;
if (!type.equals(cast.getType())) return null;
return checkPrimitive(cast.getOperand());
}
if (expression instanceof PsiNewExpression) {
PsiNewExpression newExpression = (PsiNewExpression)expression;
if (!type.equals(newExpression.getType())) return null;
PsiExpressionList argumentList = newExpression.getArgumentList();
if (argumentList == null) return null;
PsiExpression[] args = argumentList.getExpressions();
if (args.length != 1) return null;
if (!(args[0].getType() instanceof PsiPrimitiveType)) return null;
return checkPrimitive(args[0]);
}
return null;
}
private static PsiExpression checkPrimitive(PsiExpression expression) {
return expression != null && expression.getType() instanceof PsiPrimitiveType ? expression : null;
}
private static class ReplaceWithPrimitiveCompareFix implements LocalQuickFix {
private String myClassName;
public ReplaceWithPrimitiveCompareFix(String className) {
myClassName = className;
}
@Nls
@NotNull
@Override
public String getName() {
return "Replace with '" + StringUtil.getShortName(myClassName) + ".compare'";
}
@Nls
@NotNull
@Override
public String getFamilyName() {
return "Replace with static 'compare' method";
}
@Override
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
PsiMethodCallExpression call = PsiTreeUtil.getParentOfType(descriptor.getStartElement(), PsiMethodCallExpression.class);
if (call == null) return;
PsiClassType boxedType = getBoxedType(call);
if (boxedType == null) return;
PsiPrimitiveType primitiveType = PsiPrimitiveType.getUnboxedType(boxedType);
if (primitiveType == null) return;
PsiExpression qualifier = call.getMethodExpression().getQualifierExpression();
if (qualifier == null) return;
PsiExpression[] args = call.getArgumentList().getExpressions();
if (args.length != 1) return;
PsiExpression left = extractPrimitive(boxedType, primitiveType, qualifier);
if (left == null) return;
PsiExpression right = extractPrimitive(boxedType, primitiveType, args[0]);
if (right == null) return;
CommentTracker ct = new CommentTracker();
ct.replaceAndRestoreComments(call, boxedType.getCanonicalText() + ".compare(" + ct.text(left) + "," + ct.text(right) + ")");
}
}
}
@@ -0,0 +1,380 @@
/*
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed 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 com.intellij.codeInspection;
import com.intellij.codeInsight.PsiEquivalenceUtil;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.*;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.siyeh.ig.psiutils.CommentTracker;
import com.siyeh.ig.psiutils.ControlFlowUtils;
import com.siyeh.ig.psiutils.ExpressionUtils;
import one.util.streamex.StreamEx;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static com.intellij.util.ObjectUtils.tryCast;
/**
* @author Tagir Valeev
*/
public class UseCompareMethodInspection extends BaseJavaBatchLocalInspectionTool {
@NotNull
@Override
public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
if (!PsiUtil.getLanguageLevel(holder.getFile()).isAtLeast(LanguageLevel.JDK_1_4)) {
return PsiElementVisitor.EMPTY_VISITOR;
}
return new JavaElementVisitor() {
@Override
public void visitMethodCallExpression(PsiMethodCallExpression call) {
CompareInfo info = fromCall(call);
PsiElement nameElement = call.getMethodExpression().getReferenceNameElement();
if (info != null && nameElement != null) {
register(info, nameElement);
}
}
@Override
public void visitIfStatement(PsiIfStatement statement) {
CompareInfo info = fromIf(statement);
PsiElement keyword = statement.getFirstChild();
if (info != null && keyword != null) {
register(info, keyword);
}
}
@Override
public void visitConditionalExpression(PsiConditionalExpression expression) {
CompareInfo info = fromTernary(expression);
if (info != null) {
register(info, expression);
}
}
private void register(CompareInfo info, PsiElement nameElement) {
holder.registerProblem(nameElement, "Can be replaced with '" + info.myClass.getClassName() + ".compare'",
new ReplaceWithPrimitiveCompareFix(info.myClass.getCanonicalText()));
}
};
}
private static CompareInfo fromIf(PsiIfStatement ifStatement) {
PsiExpression firstCondition = ifStatement.getCondition();
if (firstCondition == null) return null;
PsiIfStatement elseIfStatement = tryCast(getElse(ifStatement), PsiIfStatement.class);
if (elseIfStatement == null) return null;
PsiExpression secondCondition = elseIfStatement.getCondition();
if (secondCondition == null) return null;
PsiStatement firstStatement = ControlFlowUtils.stripBraces(ifStatement.getThenBranch());
if (firstStatement == null) return null;
PsiStatement secondStatement = ControlFlowUtils.stripBraces(elseIfStatement.getThenBranch());
if (secondStatement == null) return null;
PsiStatement thirdStatement = getElse(elseIfStatement);
if (thirdStatement == null) return null;
Map<Integer, PsiExpression> result = new HashMap<>(3);
// like if(...) return 1; else if(...) return -1; return 0;
if (firstStatement instanceof PsiReturnStatement) {
if (!(secondStatement instanceof PsiReturnStatement) || !(thirdStatement instanceof PsiReturnStatement)) return null;
PsiExpression firstValue = ((PsiReturnStatement)firstStatement).getReturnValue();
if (!storeCondition(result, firstCondition, firstValue)) return null;
if (!storeCondition(result, secondCondition, ((PsiReturnStatement)secondStatement).getReturnValue())) return null;
if (!storeCondition(result, null, ((PsiReturnStatement)thirdStatement).getReturnValue())) return null;
return fromMap(result, firstValue, firstStatement);
}
// like if(...) x = 1; else if(...) x = -1; else x = 0;
PsiAssignmentExpression assignment = ExpressionUtils.getAssignment(firstStatement);
if (assignment == null) return null;
PsiReferenceExpression ref = tryCast(assignment.getLExpression(), PsiReferenceExpression.class);
if (ref == null) return null;
PsiVariable variable = tryCast(ref.resolve(), PsiVariable.class);
if (variable == null) return null;
PsiExpression firstExpression = assignment.getRExpression();
if (!storeCondition(result, firstCondition, firstExpression)) return null;
if (!storeCondition(result, secondCondition, ExpressionUtils.getAssignmentTo(secondStatement, variable))) return null;
if (!storeCondition(result, null, ExpressionUtils.getAssignmentTo(thirdStatement, variable))) return null;
return fromMap(result, firstExpression, assignment);
}
private static PsiStatement getElse(PsiIfStatement ifStatement) {
PsiStatement branch = ControlFlowUtils.stripBraces(ifStatement.getElseBranch());
if (branch != null) return branch;
PsiStatement thenBranch = ControlFlowUtils.stripBraces(ifStatement.getThenBranch());
if (!(thenBranch instanceof PsiReturnStatement)) return null;
PsiElement next = PsiTreeUtil.skipSiblingsForward(ifStatement, PsiComment.class, PsiWhiteSpace.class);
return tryCast(next, PsiStatement.class);
}
@Nullable
private static Map<Integer, PsiExpression> extractConditions(PsiConditionalExpression ternary) {
Map<Integer, PsiExpression> result = new HashMap<>(3);
if (!storeCondition(result, ternary.getCondition(), ternary.getThenExpression())) return null;
PsiExpression elseExpression = PsiUtil.skipParenthesizedExprDown(ternary.getElseExpression());
if (elseExpression instanceof PsiConditionalExpression) {
Map<Integer, PsiExpression> m = extractConditions((PsiConditionalExpression)elseExpression);
if (m == null) return null;
result.putAll(m);
return result;
}
return storeCondition(result, null, elseExpression) ? result : null;
}
@Contract("_, _, null -> false")
private static boolean storeCondition(@NotNull Map<Integer, PsiExpression> result,
@Nullable PsiExpression condition,
@Nullable PsiExpression expression) {
if (expression == null) return false;
Object thenValue = ExpressionUtils.computeConstantExpression(expression);
if (!(thenValue instanceof Integer) || Math.abs((Integer)thenValue) > 1) return false;
result.put((Integer)thenValue, condition);
return true;
}
private static CompareInfo fromTernary(PsiConditionalExpression ternary) {
if (!PsiType.INT.equals(ternary.getType())) return null;
Map<Integer, PsiExpression> map = extractConditions(ternary);
return fromMap(map, ternary, ternary);
}
private static CompareInfo fromMap(@Nullable Map<Integer, PsiExpression> map,
@NotNull PsiExpression expression,
@NotNull PsiElement template) {
if (map == null || map.size() != 3) {
return null;
}
PsiExpression lt = map.get(-1);
Pair<PsiExpression, PsiExpression> ltPair = getOperands(lt, JavaTokenType.LT);
if (lt != null && ltPair == null) return null;
PsiExpression gt = map.get(1);
Pair<PsiExpression, PsiExpression> gtPair = getOperands(gt, JavaTokenType.GT);
if ((gt != null || ltPair == null) && gtPair == null) return null;
if (ltPair != null && gtPair != null) {
if (!PsiEquivalenceUtil.areElementsEquivalent(ltPair.getFirst(), gtPair.getFirst())) return null;
if (!PsiEquivalenceUtil.areElementsEquivalent(ltPair.getSecond(), gtPair.getSecond())) return null;
}
Pair<PsiExpression, PsiExpression> canonicalPair = ltPair == null ? gtPair : ltPair;
PsiType leftType = canonicalPair.getFirst().getType();
PsiType rightType = canonicalPair.getSecond().getType();
if (!isTypeConvertible(leftType, expression) || !leftType.equals(rightType)) return null;
PsiExpression eq = map.get(0);
Pair<PsiExpression, PsiExpression> eqPair = getOperands(eq, JavaTokenType.EQEQ);
if (eq != null && eqPair == null) return null;
if (eqPair != null) {
if ((!PsiEquivalenceUtil.areElementsEquivalent(canonicalPair.getFirst(), eqPair.getFirst()) ||
!PsiEquivalenceUtil.areElementsEquivalent(canonicalPair.getSecond(), eqPair.getSecond())) &&
(!PsiEquivalenceUtil.areElementsEquivalent(canonicalPair.getFirst(), eqPair.getSecond()) ||
!PsiEquivalenceUtil.areElementsEquivalent(canonicalPair.getSecond(), eqPair.getFirst()))) {
return null;
}
}
PsiClassType boxedType = ((PsiPrimitiveType)leftType).getBoxedType(expression);
return new CompareInfo(template, expression, canonicalPair.getFirst(), canonicalPair.getSecond(), boxedType);
}
private static Pair<PsiExpression, PsiExpression> getOperands(PsiExpression expression, IElementType expectedToken) {
expression = PsiUtil.skipParenthesizedExprDown(expression);
if (!(expression instanceof PsiBinaryExpression)) return null;
PsiBinaryExpression binOp = (PsiBinaryExpression)expression;
PsiExpression left = PsiUtil.skipParenthesizedExprDown(binOp.getLOperand());
PsiExpression right = PsiUtil.skipParenthesizedExprDown(binOp.getROperand());
if (left == null || right == null) return null;
if (binOp.getOperationTokenType().equals(expectedToken)) {
return Pair.create(left, right);
}
if (expectedToken.equals(JavaTokenType.GT) && binOp.getOperationTokenType().equals(JavaTokenType.LT) ||
expectedToken.equals(JavaTokenType.LT) && binOp.getOperationTokenType().equals(JavaTokenType.GT)) {
return Pair.create(right, left);
}
return null;
}
@Contract("null -> null")
private static CompareInfo fromCall(PsiMethodCallExpression call) {
if (call == null) return null;
PsiElement nameElement = call.getMethodExpression().getReferenceNameElement();
if (nameElement == null) return null;
String name = nameElement.getText();
if (!"compareTo".equals(name)) return null;
PsiExpression[] args = call.getArgumentList().getExpressions();
if (args.length != 1) return null;
PsiExpression arg = args[0];
PsiExpression qualifier = call.getMethodExpression().getQualifierExpression();
if (qualifier == null) return null;
PsiClassType boxedType = getBoxedType(call);
if (boxedType == null) return null;
PsiPrimitiveType primitiveType = PsiPrimitiveType.getUnboxedType(boxedType);
if (!isTypeConvertible(primitiveType, call)) return null;
PsiExpression left = extractPrimitive(boxedType, primitiveType, qualifier);
if (left == null) return null;
PsiExpression right = extractPrimitive(boxedType, primitiveType, arg);
if (right == null) return null;
return new CompareInfo(call, call, left, right, boxedType);
}
@Nullable
static PsiClassType getBoxedType(PsiMethodCallExpression call) {
PsiMethod method = call.resolveMethod();
if (method == null) return null;
PsiClass aClass = method.getContainingClass();
if (aClass == null) return null;
return JavaPsiFacade.getElementFactory(call.getProject()).createType(aClass);
}
@Nullable
static PsiExpression extractPrimitive(PsiClassType type, PsiPrimitiveType primitiveType, PsiExpression expression) {
expression = PsiUtil.skipParenthesizedExprDown(expression);
if (expression == null) return null;
if (primitiveType.equals(expression.getType())) {
return expression;
}
if (expression instanceof PsiMethodCallExpression) {
PsiMethodCallExpression call = (PsiMethodCallExpression)expression;
if (!"valueOf".equals(call.getMethodExpression().getReferenceName())) return null;
PsiExpression[] args = call.getArgumentList().getExpressions();
if (args.length != 1) return null;
PsiMethod method = call.resolveMethod();
if (method == null || type.resolve() != method.getContainingClass()) return null;
return checkPrimitive(args[0]);
}
if (expression instanceof PsiTypeCastExpression) {
PsiTypeCastExpression cast = (PsiTypeCastExpression)expression;
if (!type.equals(cast.getType())) return null;
return checkPrimitive(cast.getOperand());
}
if (expression instanceof PsiNewExpression) {
PsiNewExpression newExpression = (PsiNewExpression)expression;
if (!type.equals(newExpression.getType())) return null;
PsiExpressionList argumentList = newExpression.getArgumentList();
if (argumentList == null) return null;
PsiExpression[] args = argumentList.getExpressions();
if (args.length != 1) return null;
if (!(args[0].getType() instanceof PsiPrimitiveType)) return null;
return checkPrimitive(args[0]);
}
return null;
}
private static PsiExpression checkPrimitive(PsiExpression expression) {
return expression != null && expression.getType() instanceof PsiPrimitiveType ? expression : null;
}
@Contract("null, _ -> false")
private static boolean isTypeConvertible(PsiType type, PsiElement context) {
return type instanceof PsiPrimitiveType && (PsiType.DOUBLE.equals(type) ||
PsiType.FLOAT.equals(type) ||
PsiUtil.isLanguageLevel7OrHigher(context));
}
static class CompareInfo {
final PsiElement myTemplate;
final PsiExpression myToReplace;
final PsiExpression myLeft;
final PsiExpression myRight;
final PsiClassType myClass;
CompareInfo(PsiElement template,
PsiExpression toReplace,
PsiExpression left,
PsiExpression right,
PsiClassType aClass) {
myTemplate = template;
myToReplace = toReplace;
myLeft = left;
myRight = right;
myClass = aClass;
}
private void replace(PsiElement toReplace, CommentTracker ct) {
String replacement = this.myClass.getCanonicalText() + ".compare(" + ct.text(this.myLeft) + "," + ct.text(this.myRight) + ")";
if(toReplace == myTemplate) {
ct.replaceAndRestoreComments(myToReplace, replacement);
} else {
ct.replace(myToReplace, replacement);
ct.replaceAndRestoreComments(toReplace, myTemplate);
}
}
}
private static class ReplaceWithPrimitiveCompareFix implements LocalQuickFix {
private String myClassName;
public ReplaceWithPrimitiveCompareFix(String className) {
myClassName = className;
}
@Nls
@NotNull
@Override
public String getName() {
return "Replace with '" + StringUtil.getShortName(myClassName) + ".compare'";
}
@Nls
@NotNull
@Override
public String getFamilyName() {
return "Replace with static 'compare' method";
}
@Override
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
PsiElement element = descriptor.getStartElement();
PsiElement toReplace;
List<PsiElement> toDelete = new ArrayList<>();
CompareInfo info;
if (element instanceof PsiConditionalExpression) {
toReplace = element;
info = fromTernary((PsiConditionalExpression)element);
}
else {
PsiElement parent = element.getParent();
if (parent instanceof PsiIfStatement) {
toReplace = parent;
info = fromIf((PsiIfStatement)parent);
PsiStatement elseIf = getElse((PsiIfStatement)parent);
toDelete.add(elseIf);
if(elseIf instanceof PsiIfStatement) {
toDelete.add(getElse((PsiIfStatement)elseIf));
}
} else {
PsiMethodCallExpression call = PsiTreeUtil.getParentOfType(element, PsiMethodCallExpression.class);
info = fromCall(call);
toReplace = call;
}
}
if (info == null) return;
CommentTracker ct = new CommentTracker();
info.replace(toReplace, ct);
StreamEx.of(toDelete).nonNull().filter(PsiElement::isValid).forEach(e -> new CommentTracker().deleteAndRestoreComments(e));
}
}
}
@@ -16,22 +16,35 @@
package com.intellij.codeInsight;
import com.intellij.codeInspection.dataFlow.HardcodedContracts;
import com.intellij.codeInspection.dataFlow.Nullness;
import com.intellij.openapi.components.PersistentStateComponent;
import com.intellij.openapi.components.State;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.DefaultJDOMExternalizer;
import com.intellij.openapi.util.InvalidDataException;
import com.intellij.openapi.util.WriteExternalException;
import com.intellij.psi.PsiElement;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.CachedValueProvider;
import com.intellij.psi.util.CachedValuesManager;
import com.intellij.psi.util.PsiModificationTracker;
import com.intellij.util.containers.ContainerUtil;
import one.util.streamex.StreamEx;
import org.jdom.Element;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jps.model.serialization.java.compiler.JpsJavaCompilerNotNullableSerializer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@State(name = "NullableNotNullManager")
public class NullableNotNullManagerImpl extends NullableNotNullManager implements PersistentStateComponent<Element> {
public NullableNotNullManagerImpl() {
public static final String TYPE_QUALIFIER_NICKNAME = "javax.annotation.meta.TypeQualifierNickname";
public NullableNotNullManagerImpl(Project project) {
super(project);
myNotNulls.addAll(getPredefinedNotNulls());
}
@@ -80,4 +93,64 @@ public class NullableNotNullManagerImpl extends NullableNotNullManager implement
LOG.error(e);
}
}
private List<PsiClass> getAllNullabilityNickNames() {
if (!getNotNulls().contains(JAVAX_ANNOTATION_NONNULL)) {
return Collections.emptyList();
}
return CachedValuesManager.getManager(myProject).getCachedValue(myProject, () -> {
List<PsiClass> result = new ArrayList<>();
GlobalSearchScope scope = GlobalSearchScope.allScope(myProject);
for (PsiClass tqNick : JavaPsiFacade.getInstance(myProject).findClasses(TYPE_QUALIFIER_NICKNAME, scope)) {
result.addAll(ContainerUtil.findAll(MetaAnnotationUtil.getChildren(tqNick, scope), candidate -> {
String qname = candidate.getQualifiedName();
if (qname == null || qname.startsWith("javax.annotation.")) return false;
return getNickNamedNullability(candidate) != Nullness.UNKNOWN;
}));
}
return CachedValueProvider.Result.create(result, PsiModificationTracker.MODIFICATION_COUNT);
});
}
private static Nullness getNickNamedNullability(@NotNull PsiClass psiClass) {
if (AnnotationUtil.findAnnotation(psiClass, TYPE_QUALIFIER_NICKNAME) == null) return Nullness.UNKNOWN;
PsiAnnotation nonNull = AnnotationUtil.findAnnotation(psiClass, JAVAX_ANNOTATION_NONNULL);
return nonNull != null ? extractNullityFromWhenValue(nonNull) : Nullness.UNKNOWN;
}
@NotNull
private static Nullness extractNullityFromWhenValue(PsiAnnotation nonNull) {
PsiAnnotationMemberValue when = nonNull.findAttributeValue("when");
if (when instanceof PsiReferenceExpression) {
String refName = ((PsiReferenceExpression)when).getReferenceName();
if ("ALWAYS".equals(refName)) {
return Nullness.NOT_NULL;
}
if ("MAYBE".equals(refName) || "NEVER".equals(refName)) {
return Nullness.NULLABLE;
}
}
return Nullness.UNKNOWN;
}
private List<String> filterNickNames(Nullness nullness) {
return StreamEx.of(getAllNullabilityNickNames()).filter(c -> getNickNamedNullability(c) == nullness).map(PsiClass::getQualifiedName).toList();
}
@NotNull
@Override
protected List<String> getNullablesWithNickNames() {
return CachedValuesManager.getManager(myProject).getCachedValue(myProject, () ->
CachedValueProvider.Result.create(ContainerUtil.concat(getNullables(), filterNickNames(Nullness.NULLABLE)),
PsiModificationTracker.MODIFICATION_COUNT));
}
@NotNull
@Override
protected List<String> getNotNullsWithNickNames() {
return CachedValuesManager.getManager(myProject).getCachedValue(myProject, () ->
CachedValueProvider.Result.create(ContainerUtil.concat(getNotNulls(), filterNickNames(Nullness.NOT_NULL)),
PsiModificationTracker.MODIFICATION_COUNT));
}
}
@@ -19,7 +19,6 @@ import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.lookup.LookupElementDecorator;
import com.intellij.codeInsight.lookup.LookupElementPresentation;
import com.intellij.codeInsight.lookup.TypedLookupItem;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.util.ClassConditionKey;
import com.intellij.openapi.util.Key;
@@ -39,19 +38,23 @@ import java.util.Set;
*/
public class JavaChainLookupElement extends LookupElementDecorator<LookupElement> implements TypedLookupItem {
public static final Key<Boolean> CHAIN_QUALIFIER = Key.create("CHAIN_QUALIFIER");
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.completion.JavaChainLookupElement");
public static final ClassConditionKey<JavaChainLookupElement> CLASS_CONDITION_KEY = ClassConditionKey.create(JavaChainLookupElement.class);
private final LookupElement myQualifier;
private final String mySeparator;
public JavaChainLookupElement(LookupElement qualifier, LookupElement main) {
this(qualifier, main, ".");
}
public JavaChainLookupElement(LookupElement qualifier, LookupElement main, String separator) {
super(main);
myQualifier = qualifier;
mySeparator = separator;
}
@NotNull
@Override
public String getLookupString() {
return maybeAddParentheses(myQualifier.getLookupString()) + "." + getDelegate().getLookupString();
return maybeAddParentheses(myQualifier.getLookupString()) + mySeparator + getDelegate().getLookupString();
}
public LookupElement getQualifier() {
@@ -70,7 +73,7 @@ public class JavaChainLookupElement extends LookupElementDecorator<LookupElement
@NotNull
@Override
public String toString() {
return maybeAddParentheses(myQualifier.toString()) + "." + getDelegate();
return maybeAddParentheses(myQualifier.toString()) + mySeparator + getDelegate();
}
private String maybeAddParentheses(String s) {
@@ -98,7 +101,7 @@ public class JavaChainLookupElement extends LookupElementDecorator<LookupElement
myQualifier.renderElement(qualifierPresentation);
String name = maybeAddParentheses(qualifierPresentation.getItemText());
final String qualifierText = myQualifier.as(CastingLookupElementDecorator.CLASS_CONDITION_KEY) != null ? "(" + name + ")" : name;
presentation.setItemText(qualifierText + "." + presentation.getItemText());
presentation.setItemText(qualifierText + mySeparator + presentation.getItemText());
if (myQualifier instanceof JavaPsiClassReferenceElement) {
presentation.appendTailText(((JavaPsiClassReferenceElement)myQualifier).getLocationString(), false);
@@ -129,9 +132,9 @@ public class JavaChainLookupElement extends LookupElementDecorator<LookupElement
if (atTail != ';') {
return;
}
document.replaceString(qualifierContext.getTailOffset(), qualifierContext.getTailOffset() + 1, ".");
document.replaceString(qualifierContext.getTailOffset(), qualifierContext.getTailOffset() + 1, mySeparator);
CompletionUtil.emulateInsertion(getDelegate(), qualifierContext.getTailOffset() + 1, context);
CompletionUtil.emulateInsertion(getDelegate(), qualifierContext.getTailOffset() + mySeparator.length(), context);
context.commitDocument();
int formatStart = context.getOffset(oldStart);
@@ -145,11 +145,12 @@ public class JavaNoVariantsDelegator extends CompletionContributor {
for (LookupElement base : suggestQualifierItems(parameters, (PsiJavaCodeReferenceElement)qualifier, filter)) {
PsiType type = JavaCompletionUtil.getLookupElementType(base);
if (type != null && !PsiType.VOID.equals(type)) {
PsiReferenceExpression ref = ReferenceExpressionCompletionContributor.createMockReference(position, type, base);
String separator = parent instanceof PsiMethodReferenceExpression ? "::" : ".";
PsiReferenceExpression ref = ReferenceExpressionCompletionContributor.createMockReference(position, type, base, separator);
if (ref != null) {
for (final LookupElement item : JavaSmartCompletionContributor.completeReference(position, ref, filter, true, true, parameters,
result.getPrefixMatcher())) {
qualifiedCollector.addElement(JavaCompletionUtil.highlightIfNeeded(null, new JavaChainLookupElement(base, item), item.getObject(), position));
for (LookupElement item : JavaSmartCompletionContributor.completeReference(position, ref, filter, true, true, parameters,
result.getPrefixMatcher())) {
qualifiedCollector.addElement(JavaCompletionUtil.highlightIfNeeded(null, new JavaChainLookupElement(base, item, separator), item.getObject(), position));
}
}
}
@@ -213,13 +213,18 @@ public class ReferenceExpressionCompletionContributor {
}
@Nullable
public static PsiReferenceExpression createMockReference(final PsiElement place, @NotNull PsiType qualifierType, LookupElement qualifierItem) {
static PsiReferenceExpression createMockReference(PsiElement place, @NotNull PsiType qualifierType, LookupElement qualifierItem) {
return createMockReference(place, qualifierType, qualifierItem, ".");
}
@Nullable
static PsiReferenceExpression createMockReference(PsiElement place, @NotNull PsiType qualifierType, LookupElement qualifierItem, String separator) {
PsiElementFactory factory = JavaPsiFacade.getElementFactory(place.getProject());
if (qualifierItem.getObject() instanceof PsiClass) {
final String qname = ((PsiClass)qualifierItem.getObject()).getQualifiedName();
if (qname == null) return null;
final String text = qname + ".xxx";
String text = qname + separator + "xxx";
try {
final PsiExpression expr = factory.createExpressionFromText(text, place);
if (expr instanceof PsiReferenceExpression) {
@@ -233,7 +238,7 @@ public class ReferenceExpressionCompletionContributor {
}
}
return (PsiReferenceExpression) factory.createExpressionFromText("xxx.xxx", JavaCompletionUtil
return (PsiReferenceExpression) factory.createExpressionFromText("xxx" + separator + "xxx", JavaCompletionUtil
.createContextWithXxxVariable(place, qualifierType));
}
@@ -23,6 +23,7 @@ import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiTypesUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.util.ArrayUtilRt;
@@ -92,7 +93,7 @@ public class SurroundWithArrayFix extends PsiElementBaseIntentionAction {
final PsiType paramType = psiParameters[idx].getType();
if (paramType instanceof PsiArrayType) {
final PsiType expressionType = TypeConversionUtil.erasure(expression.getType());
if (expressionType != null) {
if (expressionType != null && PsiTypesUtil.isDenotableType(expressionType) && expressionType != PsiType.NULL) {
final PsiType componentType = ((PsiArrayType)paramType).getComponentType();
if (TypeConversionUtil.isAssignable(componentType, expressionType)) {
return expression;
@@ -114,7 +114,7 @@ public class VariableAccessFromInnerClassFix implements IntentionAction {
makeArray();
break;
case COPY_TO_FINAL:
copyToFinal();
copyToFinal(myVariable, myContext);
break;
}
}
@@ -129,7 +129,7 @@ public class VariableAccessFromInnerClassFix implements IntentionAction {
private void makeArray() {
for (PsiVariable var : getVariablesToFix()) {
makeArray(var);
makeArray(var, myContext);
}
}
@@ -165,11 +165,11 @@ public class VariableAccessFromInnerClassFix implements IntentionAction {
}
}
private void makeArray(PsiVariable variable) throws IncorrectOperationException {
private static void makeArray(PsiVariable variable, PsiElement context) throws IncorrectOperationException {
variable.normalizeDeclaration();
PsiType type = variable.getType();
PsiElementFactory factory = JavaPsiFacade.getInstance(myContext.getProject()).getElementFactory();
PsiElementFactory factory = JavaPsiFacade.getInstance(context.getProject()).getElementFactory();
PsiType newType = type.createArrayType();
PsiDeclarationStatement variableDeclarationStatement;
@@ -199,22 +199,22 @@ public class VariableAccessFromInnerClassFix implements IntentionAction {
variable.replace(newVariable);
}
private void copyToFinal() throws IncorrectOperationException {
PsiManager psiManager = myContext.getManager();
private static void copyToFinal(PsiVariable variable, PsiElement context) throws IncorrectOperationException {
PsiManager psiManager = context.getManager();
final Project project = psiManager.getProject();
PsiElementFactory factory = JavaPsiFacade.getInstance(project).getElementFactory();
PsiExpression initializer = factory.createExpressionFromText(myVariable.getName(), myContext);
String newName = suggestNewName(project, myVariable);
PsiType type = myVariable.getType();
PsiExpression initializer = factory.createExpressionFromText(variable.getName(), context);
String newName = suggestNewName(project, variable);
PsiType type = variable.getType();
PsiDeclarationStatement copyDecl = factory.createVariableDeclarationStatement(newName, type, initializer);
PsiVariable newVariable = (PsiVariable)copyDecl.getDeclaredElements()[0];
final boolean mustBeFinal =
!PsiUtil.isLanguageLevel8OrHigher(myContext) || CodeStyleSettingsManager.getSettings(project).GENERATE_FINAL_LOCALS;
!PsiUtil.isLanguageLevel8OrHigher(context) || CodeStyleSettingsManager.getSettings(project).GENERATE_FINAL_LOCALS;
PsiUtil.setModifierProperty(newVariable, PsiModifier.FINAL, mustBeFinal);
PsiElement statement = getStatementToInsertBefore();
PsiElement statement = getStatementToInsertBefore(variable, context);
if (statement == null) return;
PsiExpression newExpression = factory.createExpressionFromText(newName, myVariable);
replaceReferences(myContext, myVariable, newExpression);
PsiExpression newExpression = factory.createExpressionFromText(newName, variable);
replaceReferences(context, variable, newExpression);
if (RefactoringUtil.isLoopOrIf(statement.getParent())) {
RefactoringUtil.putStatementInLoopBody(copyDecl, statement.getParent(), statement);
} else {
@@ -222,12 +222,12 @@ public class VariableAccessFromInnerClassFix implements IntentionAction {
}
}
private PsiElement getStatementToInsertBefore() {
PsiElement declarationScope = myVariable instanceof PsiParameter
? ((PsiParameter)myVariable).getDeclarationScope() : PsiUtil.getVariableCodeBlock(myVariable, null);
private static PsiElement getStatementToInsertBefore(PsiVariable variable, PsiElement context) {
PsiElement declarationScope = variable instanceof PsiParameter
? ((PsiParameter)variable).getDeclarationScope() : PsiUtil.getVariableCodeBlock(variable, null);
if (declarationScope == null) return null;
PsiElement statement = myContext;
PsiElement statement = context;
nextInnerClass:
do {
statement = RefactoringUtil.getParentStatement(statement, false);
@@ -361,4 +361,20 @@ public class VariableAccessFromInnerClassFix implements IntentionAction {
public boolean startInWriteAction() {
return false;
}
public static void fixAccess(@NotNull PsiVariable variable, @NotNull PsiElement context) {
int type = getQuickFixType(variable);
if (type == -1) return;
switch (type) {
case MAKE_FINAL:
PsiUtil.setModifierProperty(variable, PsiModifier.FINAL, true);
break;
case MAKE_ARRAY:
makeArray(variable, context);
break;
case COPY_TO_FINAL:
copyToFinal(variable, context);
break;
}
}
}
@@ -46,8 +46,26 @@ public class AssignFieldFromParameterAction extends BaseIntentionAction {
return false;
}
final PsiField field = findFieldToAssign(project, myParameter);
if (field == null) return false;
if (field == null || type == null || !field.getType().isAssignableFrom(type)) return false;
if (!field.getLanguage().isKindOf(JavaLanguage.INSTANCE)) return false;
PsiElement scope = myParameter.getDeclarationScope();
if (scope instanceof PsiMethod && field.hasModifierProperty(PsiModifier.FINAL)) {
if (((PsiMethod)scope).isConstructor()) {
PsiCodeBlock body = ((PsiMethod)scope).getBody();
LOG.assertTrue(body != null);
PsiStatement[] statements = body.getStatements();
if (statements.length > 0 && statements[0] instanceof PsiExpressionStatement) {
PsiExpression expression = ((PsiExpressionStatement)statements[0]).getExpression();
if (expression instanceof PsiMethodCallExpression &&
PsiKeyword.THIS.equals(((PsiMethodCallExpression)expression).getMethodExpression().getReferenceName())) {
return false;
}
}
}
else {
return false;
}
}
setText(CodeInsightBundle.message("intention.assign.field.from.parameter.text", field.getName()));
return true;
@@ -33,40 +33,33 @@ public class CreateSwitchIntention extends BaseElementAtCaretIntentionAction {
public static final String TEXT = "Create switch statement";
@Override
public void invoke(@NotNull final Project project, final Editor editor, @NotNull final PsiElement element) throws IncorrectOperationException {
final PsiExpressionStatement expressionStatement = resolveExpressionStatement(element);
final PsiElementFactory elementFactory = JavaPsiFacade.getInstance(project).getElementFactory();
PsiSwitchStatement switchStatement = (PsiSwitchStatement)elementFactory
.createStatementFromText(String.format("switch (%s) {}", expressionStatement.getExpression().getText()), null);
public void invoke(@NotNull Project project, Editor editor, @NotNull PsiElement element) throws IncorrectOperationException {
PsiExpressionStatement expressionStatement = PsiTreeUtil.getParentOfType(element, PsiExpressionStatement.class, false);
PsiElementFactory elementFactory = JavaPsiFacade.getInstance(project).getElementFactory();
String valueToSwitch = expressionStatement.getExpression().getText();
PsiSwitchStatement switchStatement = (PsiSwitchStatement)elementFactory.createStatementFromText("switch (" + valueToSwitch + ") {}", null);
switchStatement = (PsiSwitchStatement)expressionStatement.replace(switchStatement);
CodeStyleManager.getInstance(project).reformat(switchStatement);
final PsiJavaToken lBrace = switchStatement.getBody().getLBrace();
PsiJavaToken lBrace = switchStatement.getBody().getLBrace();
editor.getCaretModel().moveToOffset(lBrace.getTextOffset() + lBrace.getTextLength());
}
@Override
public boolean isAvailable(@NotNull final Project project, final Editor editor, @NotNull final PsiElement element) {
final PsiExpressionStatement expressionStatement = resolveExpressionStatement(element);
return expressionStatement != null && isValidTypeForSwitch(expressionStatement.getExpression().getType(), expressionStatement);
public boolean isAvailable(@NotNull Project project, Editor editor, @NotNull PsiElement element) {
PsiExpressionStatement expressionStatement = PsiTreeUtil.getParentOfType(element, PsiExpressionStatement.class, false);
return expressionStatement != null &&
expressionStatement.getParent() instanceof PsiCodeBlock &&
isValidTypeForSwitch(expressionStatement.getExpression().getType(), expressionStatement);
}
private static PsiExpressionStatement resolveExpressionStatement(final PsiElement element) {
if (element instanceof PsiExpressionStatement) {
return (PsiExpressionStatement)element;
} else {
final PsiStatement psiStatement = PsiTreeUtil.getParentOfType(element, PsiStatement.class);
return psiStatement instanceof PsiExpressionStatement ? (PsiExpressionStatement)psiStatement : null;
}
}
private static boolean isValidTypeForSwitch(@Nullable final PsiType type, final PsiElement context) {
private static boolean isValidTypeForSwitch(@Nullable PsiType type, PsiElement context) {
if (type == null) {
return false;
}
if (type instanceof PsiClassType) {
final PsiClass resolvedClass = ((PsiClassType)type).resolve();
PsiClass resolvedClass = ((PsiClassType)type).resolve();
if (resolvedClass == null) {
return false;
}
@@ -112,9 +112,9 @@ public final class FieldFromParameterUtils {
for (PsiReference reference : ReferencesSearch.search(parameter, new LocalSearchScope(parameter.getDeclarationScope()), false)) {
if (!(reference instanceof PsiReferenceExpression)) continue;
final PsiReferenceExpression expression = (PsiReferenceExpression)reference;
if (!(expression.getParent() instanceof PsiAssignmentExpression)) continue;
final PsiAssignmentExpression assignmentExpression = (PsiAssignmentExpression)expression.getParent();
if (assignmentExpression.getRExpression() != expression) continue;
PsiAssignmentExpression assignmentExpression = PsiTreeUtil.getParentOfType(expression, PsiAssignmentExpression.class, true, PsiClass.class);
if (assignmentExpression == null) continue;
if (!PsiTreeUtil.isAncestor(assignmentExpression.getRExpression(), expression, false)) continue;
final PsiExpression lExpression = assignmentExpression.getLExpression();
if (!(lExpression instanceof PsiReferenceExpression)) continue;
final PsiElement element = ((PsiReferenceExpression)lExpression).resolve();
@@ -83,18 +83,12 @@ public class MakeVoidQuickFix implements LocalQuickFix {
for (final PsiMethod oMethod : OverridingMethodsSearch.search(psiMethod)) {
replaceReturnStatements(oMethod);
}
final PsiParameter[] params = psiMethod.getParameterList().getParameters();
final ParameterInfoImpl[] infos = new ParameterInfoImpl[params.length];
for (int i = 0; i < params.length; i++) {
PsiParameter param = params[i];
infos[i] = new ParameterInfoImpl(i, param.getName(), param.getType());
}
final ChangeSignatureProcessor csp = new ChangeSignatureProcessor(project,
psiMethod,
false, null, psiMethod.getName(),
PsiType.VOID,
infos);
ParameterInfoImpl.fromMethod(psiMethod));
csp.run();
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,8 +18,11 @@ package com.intellij.codeInspection.miscGenerics;
import com.intellij.codeInspection.*;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.search.PsiSearchHelper;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.refactoring.typeMigration.TypeMigrationProcessor;
import com.intellij.refactoring.typeMigration.TypeMigrationRules;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -103,18 +106,21 @@ public class RawTypeCanBeGenericInspection extends BaseJavaBatchLocalInspectionT
return InspectionsBundle.message("inspection.raw.variable.type.can.be.generic.family.quickfix");
}
@Override
public boolean startInWriteAction() {
return false;
}
@Override
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
final PsiElement element = descriptor.getStartElement().getParent();
if (element instanceof PsiVariable) {
final PsiVariable variable = (PsiVariable)element;
final PsiTypeElement typeElement = variable.getTypeElement();
if (typeElement != null) {
final PsiType type = getSuggestedType(variable);
if (type != null) {
final PsiElementFactory factory = JavaPsiFacade.getInstance(variable.getProject()).getElementFactory();
typeElement.replace(factory.createTypeElement(type));
}
final PsiType type = getSuggestedType(variable);
if (type != null) {
final TypeMigrationRules rules = new TypeMigrationRules();
rules.setBoundScope(PsiSearchHelper.SERVICE.getInstance(project).getUseScope(variable));
TypeMigrationProcessor.runHighlightingTypeMigration(project, null, rules, variable, type, false);
}
}
}
@@ -309,7 +309,7 @@ public class JavaLangInvokeHandleSignatureInspection extends BaseJavaBatchLocalI
* from arguments of MethodType.methodType(Class...) and MethodType.genericMethodType(int, boolean?)
*/
@Nullable
private static ReflectiveSignature composeMethodSignature(@Nullable PsiExpression methodTypeExpression) {
public static ReflectiveSignature composeMethodSignature(@Nullable PsiExpression methodTypeExpression) {
final PsiExpression typeDefinition = findDefinition(methodTypeExpression);
if (typeDefinition instanceof PsiMethodCallExpression) {
final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)typeDefinition;
@@ -20,6 +20,7 @@ import com.intellij.codeInspection.InspectionsBundle;
import com.intellij.codeInspection.ProblemsHolder;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.containers.ContainerUtil;
import com.siyeh.ig.psiutils.ParenthesesUtils;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
@@ -131,22 +132,34 @@ public class JavaReflectionInvocationInspection extends BaseJavaBatchLocalInspec
if (definition instanceof PsiMethodCallExpression) {
final PsiMethodCallExpression definitionCall = (PsiMethodCallExpression)definition;
if (methodPredicate.test(definitionCall)) {
final PsiExpression[] arguments = definitionCall.getArgumentList().getExpressions();
if (arguments.length == argumentOffset + 1) {
final PsiExpression[] arrayElements = getVarargAsArray(arguments[argumentOffset]);
if (arrayElements != null) {
return Arrays.asList(arrayElements);
}
}
if (arguments.length >= argumentOffset) {
return Arrays.asList(arguments).subList(argumentOffset, arguments.length);
}
return getRequiredMethodArguments(definitionCall, argumentOffset);
}
}
return null;
}
private static List<PsiExpression> getRequiredMethodArguments(@NotNull PsiMethodCallExpression definitionCall, int argumentOffset) {
final PsiExpression[] arguments = definitionCall.getArgumentList().getExpressions();
if (arguments.length == argumentOffset + 1) {
final PsiExpression[] arrayElements = getVarargAsArray(arguments[argumentOffset]);
if (arrayElements != null) {
return Arrays.asList(arrayElements);
}
}
if (arguments.length >= argumentOffset) {
return Arrays.asList(arguments).subList(argumentOffset, arguments.length);
}
return null;
}
@Nullable
public static List<ReflectiveType> getReflectionMethodParameterTypes(@NotNull PsiMethodCallExpression definitionCall,
int argumentOffset) {
List<PsiExpression> arguments = getRequiredMethodArguments(definitionCall, argumentOffset);
return arguments != null ? ContainerUtil.map(arguments, type -> getReflectiveType(type)) : null;
}
@Nullable
static Arguments getActualMethodArguments(PsiExpression[] arguments, int argumentOffset, boolean allowVarargAsArray) {
if (allowVarargAsArray && arguments.length == argumentOffset + 1) {
@@ -279,6 +279,11 @@ public class JavaReflectionMemberAccessInspection extends BaseJavaBatchLocalInsp
final List<ReflectiveType> argumentTypes =
ContainerUtil.map(methodArguments.expressions, JavaReflectionReferenceUtil::getReflectiveType);
return matchMethod(methods, argumentTypes);
}
@Nullable
public static PsiMethod matchMethod(@NotNull PsiMethod[] methods, @NotNull List<ReflectiveType> argumentTypes) {
int mismatchCount = Integer.MAX_VALUE;
PsiMethod bestGuess = null;
for (PsiMethod method : methods) {
@@ -1,534 +0,0 @@
/*
* Copyright 2000-2012 JetBrains s.r.o.
*
* Licensed 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 com.intellij.psi.impl.source.codeStyle;
import com.intellij.lang.ASTNode;
import com.intellij.lang.Language;
import com.intellij.lang.java.JavaLanguage;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.codeStyle.CodeStyleSettings;
import com.intellij.psi.codeStyle.CommonCodeStyleSettings;
import com.intellij.psi.formatter.FormatterUtil;
import com.intellij.psi.impl.source.tree.TreeUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* This class handles a use-case when reformatted text conflicts with 'use tab' code style setting. E.g. target text uses
* tabs for indentation but our code style is configured to use spaces.
* <p/>
* We already have corresponding support at the block level but it's possible that multiline text is treated as a single block,
* i.e. all its internal indents are not visible to the formatter. That's why current class is introduced.
* <p/>
* Thread-safe.
*
* @author Denis Zhdanov
* @since 8/1/12 2:38 PM
*/
public class TabPostFormatProcessor implements PostFormatProcessor {
@Override
public PsiElement processElement(@NotNull PsiElement source, @NotNull CodeStyleSettings settings) {
doProcess(source, TextRange.from(source.getTextRange().getStartOffset(), source.getTextLength()), settings);
return source;
}
@NotNull
@Override
public TextRange processText(@NotNull PsiFile source, @NotNull TextRange rangeToReformat, @NotNull CodeStyleSettings settings) {
return doProcess(source, rangeToReformat, settings);
}
@NotNull
private static TextRange doProcess(@NotNull PsiElement source, @NotNull TextRange range, @NotNull CodeStyleSettings settings) {
ASTNode node = source.getNode();
if (node == null) {
return range;
}
Language language = source.getLanguage();
if (language != JavaLanguage.INSTANCE) {
// We had the only complaint for tabs not being converted to spaces for now. It was for the java code which has
// a single block for the multi-line comment. This check should be removed if it is decided to generalize
// this logic to other languages as well.
return range;
}
if (!source.isValid()) return range;
PsiFile file = source.getContainingFile();
CommonCodeStyleSettings.IndentOptions indentOptions = settings.getIndentOptionsByFile(file, range);
boolean useTabs = indentOptions.USE_TAB_CHARACTER;
boolean smartTabs = indentOptions.SMART_TABS;
int tabWidth = indentOptions.TAB_SIZE;
return processViaPsi(node, range, new TreeHelperImpl(), useTabs, smartTabs, tabWidth);
}
@NotNull
public static TextRange processViaPsi(@NotNull ASTNode node,
@NotNull TextRange range,
@NotNull TreeHelper treeHelper,
boolean useTabs,
boolean smartTabs,
int tabWidth)
{
AstHelper helper = new AstHelper(node, treeHelper);
do {
if (useTabs) {
if (smartTabs) {
range = processSmartTabs(helper, range, tabWidth);
}
else {
range = processTabs(helper, range, tabWidth);
}
}
else {
range = processSpaces(helper, range, tabWidth);
}
}
while (helper.nextLine());
return range;
}
@NotNull
public static TextRange processViaDocument(@NotNull Document document,
@NotNull TextRange range,
boolean useTabs,
boolean useSmartTabs,
int tabWidth)
{
TextRange result = range;
int startLine = document.getLineNumber(Math.min(document.getTextLength(), range.getStartOffset()));
int endLine = document.getLineNumber(Math.max(0, Math.min(document.getTextLength(), range.getEndOffset()) - 1));
DocumentHelper helper = new DocumentHelper(document, startLine);
for (int line = startLine; line <= endLine; line++) {
helper.setLine(line);
if (useTabs) {
if (useSmartTabs) {
result = processSmartTabs(helper, result, tabWidth);
}
else {
result = processTabs(helper, result, tabWidth);
}
}
else {
result = processSpaces(helper, result, tabWidth);
}
}
return result;
}
/**
* Converts tabulations to white spaces at the target line's indent space.
*
* @param helper data facade
* @param range target range allowed for modification
* @param tabWidth tab width in columns to use during conversion (each tab symbol is replaced by white spaces which number is
* equal to tab width)
* @return given text range if no modification to the target line's indent space has been performed:
* adjusted range that points to semantically the same region otherwise
*/
@NotNull
private static TextRange processSpaces(@NotNull Helper helper, @NotNull TextRange range, int tabWidth) {
CharSequence indent = helper.getCurrentLineIndent();
int start = Math.max(0, range.getStartOffset() - helper.getCurrentLineStartOffset());
int end = Math.min(indent.length(), range.getEndOffset() - helper.getCurrentLineStartOffset());
int tabsNumber = 0;
int indentOffset = end;
for (int i = start; i < end; i++) {
char c = indent.charAt(i);
if (c == '\t') {
tabsNumber++;
}
else if (c != ' ') {
indentOffset = i;
break;
}
}
if (tabsNumber > 0) {
helper.replace(start, indentOffset, StringUtil.repeat(" ", indentOffset - start - tabsNumber + tabsNumber * tabWidth));
return TextRange.create(range.getStartOffset(), range.getEndOffset() - tabsNumber + tabsNumber * tabWidth);
}
else {
return range;
}
}
/**
* Converts white spaces to tabulations at the target line's indent space.
*
* @param helper data facade
* @param range target range allowed for modification
* @param tabWidth tab width in columns to use during conversion (each tab symbol is replaced by white spaces which number is
* equal to tab width)
* @return given text range if no modification to the target line's indent space has been performed:
* adjusted range that points to semantically the same region otherwise
*/
@NotNull
private static TextRange processTabs(@NotNull Helper helper, @NotNull TextRange range, int tabWidth) {
CharSequence indent = helper.getCurrentLineIndent();
int start = Math.max(0, range.getStartOffset() - helper.getCurrentLineStartOffset());
int end = Math.min(indent.length(), range.getEndOffset() - helper.getCurrentLineStartOffset());
int replacementsNumber = 0;
int consecutiveSpaces = 0;
for (int i = start; i < end; i++) {
char c = indent.charAt(i);
if (c == ' ') {
++consecutiveSpaces;
}
else {
int tabsNumber = consecutiveSpaces / tabWidth;
if (tabsNumber > 0) {
helper.replace(i - consecutiveSpaces, i - consecutiveSpaces + tabsNumber * tabWidth, StringUtil.repeat("\t", tabsNumber));
replacementsNumber++;
consecutiveSpaces = 0;
}
if (c != '\t') {
break;
}
}
}
int tabsNumber = consecutiveSpaces / tabWidth;
if (tabsNumber > 0) {
helper.replace(end - consecutiveSpaces, end - consecutiveSpaces + tabsNumber * tabWidth, StringUtil.repeat("\t", tabsNumber));
}
if (replacementsNumber > 0) {
return TextRange.create(range.getStartOffset(), range.getEndOffset() - replacementsNumber * (tabWidth - 1));
}
else {
return range;
}
}
/**
* Converts tabulations to white spaces at the target line's indent space.
*
* @param helper data facade
* @param range target range allowed for modification
* @param tabWidth tab width in columns to use during conversion (every group of 'tab width' white spaces from the indent space might
* be replaced by a tab symbol)
* @return given text range if no modification to the target line's indent space has been performed:
* adjusted range that points to semantically the same region otherwise
*/
@SuppressWarnings("AssignmentToForLoopParameter")
@NotNull
private static TextRange processSmartTabs(@NotNull Helper helper, @NotNull TextRange range, int tabWidth) {
// Adjust current line indent. The general idea is to replace white spaces by tab symbols if that maps to the previous line indent.
CharSequence prevLineIndent = helper.getPrevLineIndent();
if (prevLineIndent == null) {
return processTabs(helper, range, tabWidth);
}
CharSequence currentLineIndent = helper.getCurrentLineIndent();
int lineStart = 0;
int start = Math.max(0, range.getStartOffset() - helper.getCurrentLineStartOffset());
int end = Math.min(currentLineIndent.length(), range.getEndOffset() - helper.getCurrentLineStartOffset());
int indentOffset = 0;
int tabsReplaced = 0;
for (int i = lineStart; i < end && indentOffset < prevLineIndent.length(); i++, indentOffset++) {
char c = currentLineIndent.charAt(i);
if (prevLineIndent.charAt(indentOffset) == ' ') {
if (c == ' ') {
continue;
}
else {
break;
}
}
// Assuming that target prevLineIndent symbol is tab then.
if (c == '\t') {
continue;
}
if (end - i < tabWidth) {
break;
}
boolean canReplace = true;
for (int j = i + 1, max = Math.min(end, i + tabWidth); j < max; j++) {
if (currentLineIndent.charAt(j) != ' ') {
canReplace = false;
break;
}
}
if (!canReplace) {
break;
}
if (i < start) {
// Continue processing if target range doesn't cover the whole white spaces which are intended to replace tab symbol.
i += tabWidth - 1; // -1 because of 'for' loop increment
continue;
}
helper.replace(i, i + tabWidth, "\t");
tabsReplaced++;
end -= tabWidth - 1;
}
return tabsReplaced > 0 ? TextRange.create(range.getStartOffset(), range.getEndOffset() - tabsReplaced * (tabWidth - 1)) : range;
}
/**
* There are two possible processing use-cases:
* <pre>
* <ul>
* <li>document-based processing;</li>
* <li>PSI-based processing;</li>
* </ul>
* </pre>
* That's why we hide implementation-specific processing behind the current interface and use it at the generic 'engine'.
* <p/>
* The general idea is to process indent spaces line-by-line from top to bottom.
*/
interface Helper {
/**
* @return previous line indent space if current line is not the first one; {@code null} otherwise
*/
@Nullable CharSequence getPrevLineIndent();
int getCurrentLineStartOffset();
/** @return current line's indent space */
@NotNull CharSequence getCurrentLineIndent();
/**
* Asks current helper to modify target line's indent space.
*
* @param start start offset of the indent range to modify (counts from the line start, i.e. doesn't take into
* consideration line start offset at the document)
* @param end end offset of the indent range to modify (counts from the line start, i.e. doesn't take into
* consideration line start offset at the document)
* @param newText replacement text
*/
void replace(int start, int end, @NotNull String newText);
}
private static class DocumentHelper implements Helper {
@NotNull private final Document myDocument;
private int myLine;
private int myLineStartOffset;
DocumentHelper(@NotNull Document document, int line) {
myDocument = document;
setLine(line);
}
@Nullable
@Override
public CharSequence getPrevLineIndent() {
if (myLine <= 0) {
return null;
}
int prevLineStart = myDocument.getLineStartOffset(myLine - 1);
int prevLineIndentEnd = prevLineStart;
int prevLineEnd = myDocument.getLineEndOffset(myLine - 1);
CharSequence text = myDocument.getCharsSequence();
for (; prevLineIndentEnd < prevLineEnd; prevLineIndentEnd++) {
char c = text.charAt(prevLineIndentEnd);
if (c != '\t' && c != ' ') {
break;
}
}
return text.subSequence(prevLineStart, prevLineIndentEnd);
}
@Override
public int getCurrentLineStartOffset() {
return myLineStartOffset;
}
@NotNull
@Override
public CharSequence getCurrentLineIndent() {
int end = myDocument.getLineEndOffset(myLine);
CharSequence text = myDocument.getCharsSequence();
for (int i = myLineStartOffset; i < end; i++) {
char c = text.charAt(i);
if (c != ' ' && c != '\t') {
return text.subSequence(myLineStartOffset, i);
}
}
return text.subSequence(myLineStartOffset, end);
}
@Override
public void replace(int start, int end, @NotNull String newText) {
myDocument.replaceString(myLineStartOffset + start, myLineStartOffset + end, newText);
}
public void setLine(int line) {
myLine = line;
myLineStartOffset = myDocument.getLineStartOffset(line);
}
}
private static class AstHelper implements Helper {
@NotNull private final TreeHelper myHelper;
@Nullable private ASTNode myCurrentIndentHolder;
private int myLineStartOffset;
AstHelper(@NotNull ASTNode startNode, @NotNull TreeHelper helper) {
myHelper = helper;
myCurrentIndentHolder = myHelper.firstLeaf(startNode);
if (startNode.getStartOffset() <= 0) {
return;
}
nextLine();
}
@SuppressWarnings("LoopStatementThatDoesntLoop")
@Override
public CharSequence getPrevLineIndent() {
if (myCurrentIndentHolder == null) {
return null;
}
// Check if current white space is multiline.
int end = myLineStartOffset - 1;
CharSequence text = myCurrentIndentHolder.getChars();
for (int i = end - 1; i >= 0; i--) {
if (text.charAt(i) == '\n') {
return text.subSequence(i + 1, end);
}
}
for (ASTNode prev = prevIndentNode(myCurrentIndentHolder); prev != null; prev = prevIndentNode(prev)) {
CharSequence chars = prev.getChars();
for (int i = chars.length() - 1; i >= 0; i--) {
if (chars.charAt(i) == '\n') {
return chars.subSequence(i + 1, chars.length());
}
}
return chars;
}
return null;
}
@Override
public int getCurrentLineStartOffset() {
ASTNode whiteSpace = myCurrentIndentHolder;
return whiteSpace == null ? 0 : whiteSpace.getStartOffset() + myLineStartOffset;
}
@SuppressWarnings("UnusedAssignment")
@NotNull
@Override
public CharSequence getCurrentLineIndent() {
if (myCurrentIndentHolder == null || myLineStartOffset < 0) {
return "";
}
CharSequence text = myCurrentIndentHolder.getChars();
for (int i = myLineStartOffset; i < text.length(); i++) {
char c = text.charAt(i);
if (c == '\n' || (c != ' ' && c != '\t')) {
return text.subSequence(myLineStartOffset, i);
}
}
return text.subSequence(myLineStartOffset, text.length());
}
@Override
public void replace(int start, int end, @NotNull String newText) {
if (myCurrentIndentHolder != null) {
myHelper.replace(newText, TextRange.create(start, end).shiftRight(getCurrentLineStartOffset()), myCurrentIndentHolder);
}
}
public boolean nextLine() {
if (myCurrentIndentHolder == null) {
return false;
}
for (ASTNode node = myHelper.nextLeaf(myCurrentIndentHolder); node != null; node = myHelper.nextLeaf(node)) {
if (myCurrentIndentHolder.getTextLength() <= 0) {
continue;
}
CharSequence text = node.getChars();
for (myLineStartOffset = 0; myLineStartOffset < text.length(); myLineStartOffset++) {
char c = text.charAt(myLineStartOffset);
if (c == '\n' && myLineStartOffset < text.length() - 1) {
myCurrentIndentHolder = node;
myLineStartOffset++;
return true;
}
}
}
myCurrentIndentHolder = null;
return false;
}
@Nullable
private ASTNode prevIndentNode(@NotNull ASTNode current) {
for (ASTNode candidate = myHelper.prevLeaf(current); candidate != null; candidate = myHelper.prevLeaf(candidate)) {
if (candidate.getStartOffset() <= 0 || StringUtil.contains(candidate.getChars(), 0, candidate.getTextLength(), '\n')) {
return candidate;
}
}
return null;
}
}
public interface TreeHelper {
@Nullable
ASTNode prevLeaf(@NotNull ASTNode current);
@Nullable
ASTNode nextLeaf(@NotNull ASTNode current);
@Nullable
ASTNode firstLeaf(@NotNull ASTNode startNode);
void replace(@NotNull String newText, @NotNull TextRange range, @NotNull ASTNode leaf);
}
private static class TreeHelperImpl implements TreeHelper {
@Override
public ASTNode prevLeaf(@NotNull ASTNode current) {
return TreeUtil.prevLeaf(current);
}
@Nullable
@Override
public ASTNode nextLeaf(@NotNull ASTNode current) {
return TreeUtil.nextLeaf(current);
}
@Nullable
@Override
public ASTNode firstLeaf(@NotNull ASTNode startNode) {
return TreeUtil.findFirstLeaf(startNode);
}
@Override
public void replace(@NotNull String newText, @NotNull TextRange range, @NotNull ASTNode leaf) {
FormatterUtil.replaceInnerWhiteSpace(newText, leaf, range);
}
}
}
@@ -18,9 +18,11 @@ package com.intellij.psi.impl.source.resolve.reference.impl;
import com.intellij.codeInsight.completion.InsertHandler;
import com.intellij.codeInsight.completion.InsertionContext;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.lookup.LookupElementBuilder;
import com.intellij.codeInspection.reflectiveAccess.JavaReflectionInvocationInspection;
import com.intellij.codeInspection.reflectiveAccess.JavaReflectionMemberAccessInspection;
import com.intellij.psi.*;
import com.intellij.psi.util.MethodSignatureBackedByPsiMethod;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.containers.ContainerUtil;
import gnu.trove.THashSet;
@@ -28,10 +30,7 @@ import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Objects;
import java.util.Set;
import java.util.*;
import static com.intellij.psi.impl.source.resolve.reference.impl.JavaReflectionReferenceUtil.*;
@@ -73,17 +72,29 @@ public class JavaLangClassMemberReference extends PsiReferenceBase<PsiLiteralExp
}
case GET_METHOD: {
final PsiMethod[] methods = psiClass.findMethodsByName(name, true);
final PsiMethod publicMethod = ContainerUtil.find(methods, method -> isRegularMethod(method) && isPublic(method));
if (publicMethod != null) {
return publicMethod;
PsiMethod[] methods = psiClass.findMethodsByName(name, true);
if (methods.length > 1) {
methods =
ContainerUtil.filter(methods, method -> isRegularMethod(method) && isPublic(method))
.toArray(PsiMethod.EMPTY_ARRAY);
if (methods.length > 1) {
return findOverloadedMethod(methods);
}
}
return ContainerUtil.find(methods, method -> isRegularMethod(method));
return methods.length != 0 ? methods[0] : null;
}
case GET_DECLARED_METHOD: {
final PsiMethod[] methods = psiClass.findMethodsByName(name, false);
return ContainerUtil.find(methods, method -> isRegularMethod(method) && isPotentiallyAccessible(method, psiClass));
PsiMethod[] methods = psiClass.findMethodsByName(name, false);
if (methods.length > 1) {
methods =
ContainerUtil.filter(methods, method -> isRegularMethod(method) && isPotentiallyAccessible(method, psiClass))
.toArray(PsiMethod.EMPTY_ARRAY);
if (methods.length > 1) {
return findOverloadedMethod(methods);
}
}
return methods.length != 0 ? methods[0] : null;
}
}
}
@@ -155,6 +166,18 @@ public class JavaLangClassMemberReference extends PsiReferenceBase<PsiLiteralExp
return member != null && (member.getContainingClass() == psiClass || isPublic(member));
}
@Nullable
private PsiElement findOverloadedMethod(PsiMethod[] methods) {
final PsiMethodCallExpression definitionCall = PsiTreeUtil.getParentOfType(myElement, PsiMethodCallExpression.class);
if (definitionCall != null) {
final List<ReflectiveType> parameterTypes = JavaReflectionInvocationInspection.getReflectionMethodParameterTypes(definitionCall, 1);
if (parameterTypes != null) {
return JavaReflectionMemberAccessInspection.matchMethod(methods, parameterTypes);
}
}
return null;
}
@Override
public void handleInsert(InsertionContext context, LookupElement item) {
final Object object = item.getObject();
@@ -19,13 +19,16 @@ import com.intellij.codeInsight.completion.InsertHandler;
import com.intellij.codeInsight.completion.InsertionContext;
import com.intellij.codeInsight.completion.JavaLookupElementBuilder;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInspection.reflectiveAccess.JavaLangInvokeHandleSignatureInspection;
import com.intellij.openapi.util.Condition;
import com.intellij.psi.*;
import com.intellij.psi.util.MethodSignatureBackedByPsiMethod;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.ArrayUtil;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.ProcessingContext;
import com.intellij.util.containers.ContainerUtil;
import com.siyeh.ig.psiutils.ParenthesesUtils;
import gnu.trove.THashSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -97,9 +100,25 @@ public class JavaLangInvokeHandleReference extends PsiReferenceBase<PsiLiteralEx
return field != null && filter.value(field) ? field : null;
}
private static PsiElement resolveMethod(@NotNull String name, @NotNull PsiClass psiClass, Condition<? super PsiMethod> filter) {
final PsiMethod[] methods = psiClass.findMethodsByName(name, true);
return ContainerUtil.find(methods, filter);
private PsiElement resolveMethod(@NotNull String name, @NotNull PsiClass psiClass, Condition<? super PsiMethod> filter) {
PsiMethod[] methods = psiClass.findMethodsByName(name, true);
if (methods.length != 0) {
methods = ContainerUtil.filter(methods, filter).toArray(PsiMethod.EMPTY_ARRAY);
if (methods.length > 1) {
final PsiMethodCallExpression definitionCall = PsiTreeUtil.getParentOfType(myElement, PsiMethodCallExpression.class);
if (definitionCall != null) {
final PsiExpression[] arguments = definitionCall.getArgumentList().getExpressions();
if (arguments.length > 2) {
final PsiExpression typeExpression = ParenthesesUtils.stripParentheses(arguments[2]);
final ReflectiveSignature expectedSignature = JavaLangInvokeHandleSignatureInspection.composeMethodSignature(typeExpression);
if (expectedSignature != null) {
return ContainerUtil.find(methods, method -> expectedSignature.equals(getMethodSignature(method)));
}
}
}
}
}
return methods.length != 0 ? methods[0] : null;
}
@NotNull
@@ -16,7 +16,6 @@
package com.intellij.refactoring.typeMigration;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Ref;
@@ -35,12 +34,18 @@ import com.intellij.ui.content.Content;
import com.intellij.usageView.UsageInfo;
import com.intellij.usageView.UsageViewDescriptor;
import com.intellij.usageView.UsageViewManager;
import com.intellij.util.*;
import com.intellij.util.containers.*;
import com.intellij.util.Function;
import com.intellij.util.Functions;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.SmartList;
import com.intellij.util.containers.ContainerUtil;
import gnu.trove.THashSet;
import org.jetbrains.annotations.NotNull;
import java.util.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import static com.intellij.util.ObjectUtils.assertNotNull;
@@ -137,7 +142,7 @@ public class TypeMigrationProcessor extends BaseRefactoringProcessor {
protected boolean preprocessUsages(@NotNull Ref<UsageInfo[]> refUsages) {
if (hasFailedConversions()) {
if (ApplicationManager.getApplication().isUnitTestMode()) {
throw new RuntimeException(StringUtil.join(myLabeler.getFailedConversionsReport(), "\n"));
throw new BaseRefactoringProcessor.ConflictsInTestsException(Arrays.asList(myLabeler.getFailedConversionsReport()));
}
FailedConversionsDialog dialog = new FailedConversionsDialog(myLabeler.getFailedConversionsReport(), myProject);
if (!dialog.showAndGet()) {
@@ -34,6 +34,7 @@ import java.util.*;
*/
public abstract class NullableNotNullManager {
protected static final Logger LOG = Logger.getInstance(NullableNotNullManager.class);
protected final Project myProject;
public String myDefaultNullable = AnnotationUtil.NULLABLE;
public String myDefaultNotNull = AnnotationUtil.NOT_NULL;
@@ -41,7 +42,7 @@ public abstract class NullableNotNullManager {
public final JDOMExternalizableStringList myNotNulls = new JDOMExternalizableStringList();
private static final String JAVAX_ANNOTATION_NULLABLE = "javax.annotation.Nullable";
private static final String JAVAX_ANNOTATION_NONNULL = "javax.annotation.Nonnull";
protected static final String JAVAX_ANNOTATION_NONNULL = "javax.annotation.Nonnull";
static final String[] DEFAULT_NULLABLES = {AnnotationUtil.NULLABLE,
JAVAX_ANNOTATION_NULLABLE, "javax.annotation.CheckForNull",
@@ -49,7 +50,8 @@ public abstract class NullableNotNullManager {
"edu.umd.cs.findbugs.annotations.Nullable", "android.support.annotation.Nullable"
};
public NullableNotNullManager() {
public NullableNotNullManager(Project project) {
myProject = project;
Collections.addAll(myNullables, DEFAULT_NULLABLES);
}
@@ -202,7 +204,7 @@ public abstract class NullableNotNullManager {
String qName = annotation.getQualifiedName();
if (qName == null) return null;
List<String> contradictory = nullable ? getNotNulls() : getNullables();
List<String> contradictory = nullable ? getNotNullsWithNickNames() : getNullablesWithNickNames();
if (contradictory.contains(qName)) return null;
return annotation;
@@ -241,13 +243,24 @@ public abstract class NullableNotNullManager {
}
private PsiAnnotation findPlainNullabilityAnnotation(@NotNull PsiModifierListOwner owner, boolean checkBases) {
Set<String> qNames = ContainerUtil.newHashSet(getNullables());
qNames.addAll(getNotNulls());
Set<String> qNames = ContainerUtil.newHashSet(getNullablesWithNickNames());
qNames.addAll(getNotNullsWithNickNames());
return checkBases && owner instanceof PsiMethod
? AnnotationUtil.findAnnotationInHierarchy(owner, qNames)
: AnnotationUtil.findAnnotation(owner, qNames);
}
@NotNull
protected List<String> getNullablesWithNickNames() {
return getNullables();
}
@NotNull
protected List<String> getNotNullsWithNickNames() {
return getNotNulls();
}
protected boolean hasHardcodedContracts(PsiElement element) {
return false;
}
@@ -360,10 +373,10 @@ public abstract class NullableNotNullManager {
public abstract List<String> getPredefinedNotNulls();
public static boolean isNullableAnnotation(@NotNull PsiAnnotation annotation) {
return getInstance(annotation.getProject()).getNullables().contains(annotation.getQualifiedName());
return getInstance(annotation.getProject()).getNullablesWithNickNames().contains(annotation.getQualifiedName());
}
public static boolean isNotNullAnnotation(@NotNull PsiAnnotation annotation) {
return getInstance(annotation.getProject()).getNotNulls().contains(annotation.getQualifiedName());
return getInstance(annotation.getProject()).getNotNullsWithNickNames().contains(annotation.getQualifiedName());
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,7 +18,7 @@ package com.intellij.psi.util;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.Nullable;
import java.util.List;
@@ -102,8 +102,8 @@ public class PsiConcatenationUtil {
formatParameters.add(getBoxedArgument(expression));
}
private static PsiExpression getBoxedArgument(PsiExpression arg) throws IncorrectOperationException {
arg = PsiUtil.deparenthesizeExpression(arg);
private static PsiExpression getBoxedArgument(PsiExpression arg) {
arg = unwrapExpression(arg);
assert arg != null;
if (PsiUtil.isLanguageLevel5OrHigher(arg)) {
return arg;
@@ -130,4 +130,35 @@ public class PsiConcatenationUtil {
return newExpr;
}
@Nullable
private static PsiExpression unwrapExpression(PsiExpression expression) {
while (true) {
if (expression instanceof PsiParenthesizedExpression) {
expression = ((PsiParenthesizedExpression)expression).getExpression();
continue;
}
if (expression instanceof PsiTypeCastExpression) {
final PsiTypeCastExpression typeCastExpression = (PsiTypeCastExpression)expression;
final PsiType castType = typeCastExpression.getType();
if (TypeConversionUtil.isNumericType(castType)) {
final PsiExpression operand = typeCastExpression.getOperand();
if (operand == null) {
return expression;
}
final PsiType operandType = operand.getType();
if (operandType == null) {
return expression;
}
final int castRank = TypeConversionUtil.getTypeRank(castType);
final int operandRank = TypeConversionUtil.getTypeRank(operandType);
if (castRank < operandRank || castRank == TypeConversionUtil.CHAR_RANK && operandRank != castRank) {
return expression;
}
}
expression = typeCastExpression.getOperand();
continue;
}
return expression;
}
}
}
@@ -1984,8 +1984,8 @@ public class InferenceSession {
final PsiSubstitutor sSubstitutor = TypeConversionUtil.getSuperClassSubstitutor(gClass, (PsiClassType)sBound);
final PsiSubstitutor tSubstitutor = TypeConversionUtil.getSuperClassSubstitutor(gClass, (PsiClassType)tBound);
for (PsiTypeParameter typeParameter : gClass.getTypeParameters()) {
final PsiType sType = sSubstitutor.substitute(typeParameter);
final PsiType tType = tSubstitutor.substitute(typeParameter);
final PsiType sType = sSubstitutor.substituteWithBoundsPromotion(typeParameter);
final PsiType tType = tSubstitutor.substituteWithBoundsPromotion(typeParameter);
final Pair<PsiType, PsiType> typePair = Pair.create(sType, tType);
if (!processor.process(typePair)) {
return gClass;
@@ -0,0 +1,5 @@
class Test {
void m(int[] array) {
for (int i = 1; i < array.length; <caret>i++) { }
}
}
@@ -0,0 +1,10 @@
class ListA<Ta> {
public <R extends Ta> Ta foo() { throw new Error(); }
}
class ListB<Tb> extends ListA<Tb> {
public <Rb extends Tb> Rb foo() { throw new Error(); }
}
class ListC<Tc> extends ListB<Tc> {
}
@@ -0,0 +1,5 @@
class A {
{
Runnable r = Syst::setOu<caret>
}
}
@@ -0,0 +1,5 @@
class A {
{
Runnable r = System::setOut;<caret>
}
}
@@ -0,0 +1,11 @@
abstract class Bug {
{
D<?> _m = m();
}
abstract <J extends C<? extends String>> J m();
}
abstract class C<T extends String> { }
abstract class D<T extends String> extends C<T> { }
@@ -81,3 +81,27 @@ class InsideThisRxpression {
public interface Bar extends Foo {}
}
class SameDefaultMethodDifferentInheritors {
interface A { default void a() {} }
interface B extends A { default void a() {} }
interface B1 extends A { }
interface C extends A {}
class Clazz implements B, C {
{
<error descr="Bad type qualifier in default super call: method a is overridden in SameDefaultMethodDifferentInheritors.B">C</error>.super.a();
}
}
class Clazz1 implements B1, C {
{
C.super.a();
}
}
class Clazz2 implements C {
{
C.super.a();
}
}
}
@@ -0,0 +1,10 @@
// "Assign Parameter to Field 'myA'" "false"
class Person {
int myA;
int myId;
void f(int <caret>a, String id) {
this.myA = foo(a);
}
int foo(int a) {return a;}
}
@@ -0,0 +1,10 @@
// "Assign Parameter to Field 'myId'" "false"
class Person {
int a;
int myId;
void f(int a, String id<caret>) {
this.a = foo(a);
}
int foo(int a) {return a;}
}
@@ -0,0 +1,13 @@
// "Assign Parameter to Field 'myStr'" "false"
class Foo1 {
final String myStr;
Foo1(String str, int i) {
myStr = (str);
}
Foo1(String st<caret>r) {
this(str, 2);
}
}
@@ -0,0 +1,9 @@
// "Surround with array initialization" "false"
class A {
void m1(String[] s,
String[] s2,
String[] s3) {}
{
m1( <caret>null, null);
}
}
@@ -1,4 +1,4 @@
// "Fix all 'Unnecessary boxing to compare primitives' problems in file" "true"
// "Fix all ''compare()' method can be used to compare primitives' problems in file" "true"
public class Test {
public void test(int a, int b) {
if(Integer.compare(a, b) > 0) {
@@ -1,4 +1,4 @@
// "Fix all 'Unnecessary boxing to compare primitives' problems in file" "true"
// "Fix all ''compare()' method can be used to compare primitives' problems in file" "true"
public class Test {
public int test(String s1, String s2) {
int res = Integer.compare(s1.length(), s2.length());
@@ -0,0 +1,16 @@
// "Fix all ''compare()' method can be used to compare primitives' problems in file" "true"
class Test {
public void test(String s1, String s2) {
int res;
res = Integer.compare(s2.length(), s1.length())
System.out.println(res);
}
public void testMissingElse(String s1, String s2) {
int res;
if(s1.length() < s2.length()) res = 1;
else if(s1.length() > s2.length()) res = -1;
res = 0;
System.out.println(res);
}
}
@@ -0,0 +1,17 @@
// "Fix all ''compare()' method can be used to compare primitives' problems in file" "true"
class Test {
public int test(String s1, String s2) {
return Integer.compare(s1.length(), s2.length());
/*otherwise bigger*/
}
public int test2(String s1, String s2) {
return Integer.compare(s2.length(), s1.length());
}
public int test3(String s1, String s2) {
if(s1.length() > s2.length()) return -1;
else if(s2.length() > s1.length()) return -1;
else return 0;
}
}
@@ -0,0 +1,18 @@
// "Fix all ''compare()' method can be used to compare primitives' problems in file" "true"
public class Test {
public void test(String s1, String s2) {
System.out.println(Integer.compare(s1.length(), s2.length()));
System.out.println(Integer.compare(s2.length(), s1.length()));
/*greater!*/
/*less!*/
/*equal!*/
System.out.println(Integer.compare(s1.length(), s2.length()));
System.out.println(Integer.compare(s2.length(), s1.length()));
System.out.println(Integer.compare(s2.length(), s1.length()));
System.out.println(s1.length() < s2.length() ? -1 : s1.length() == s2.length() ? 0 : 2);
System.out.println(s1.length() < s2.length() ? 1 : s2.length() < s1.length() ? 0 : 1);
System.out.println(s1.length() == s2.length() ? 1 : s2.length() < s1.length() ? 0 : 1);
System.out.println(s1.length() == s2.length() ? 0 : s2.length() < s2.length() ? -1 : 1);
}
}
@@ -1,4 +1,4 @@
// "Fix all 'Unnecessary boxing to compare primitives' problems in file" "true"
// "Fix all ''compare()' method can be used to compare primitives' problems in file" "true"
public class Test {
public void test(int a, int b) {
if(((Integer)a).compa<caret>reTo(b) > 0) {
@@ -1,4 +1,4 @@
// "Fix all 'Unnecessary boxing to compare primitives' problems in file" "true"
// "Fix all ''compare()' method can be used to compare primitives' problems in file" "true"
public class Test {
public int test(String s1, String s2) {
int res = new Integer(s1.length()).co<caret>mpareTo(s2.length());
@@ -0,0 +1,18 @@
// "Fix all ''compare()' method can be used to compare primitives' problems in file" "true"
class Test {
public void test(String s1, String s2) {
int res;
i<caret>f(s1.length() < s2.length()) res = 1;
else if(s1.length() > s2.length()) res = -1;
else res = 0;
System.out.println(res);
}
public void testMissingElse(String s1, String s2) {
int res;
if(s1.length() < s2.length()) res = 1;
else if(s1.length() > s2.length()) res = -1;
res = 0;
System.out.println(res);
}
}
@@ -0,0 +1,22 @@
// "Fix all ''compare()' method can be used to compare primitives' problems in file" "true"
class Test {
public int test(String s1, String s2) {
if(s1.length() < s2.length()) {
return -1;
}
if((s1.length()) == s2.length()) return 0;
else /*otherwise bigger*/ return +1;
}
public int test2(String s1, String s2) {
i<caret>f(s1.length() > s2.length()) return -1;
else if(s2.length() > s1.length()) return 1;
else return 0;
}
public int test3(String s1, String s2) {
if(s1.length() > s2.length()) return -1;
else if(s2.length() > s1.length()) return -1;
else return 0;
}
}
@@ -0,0 +1,15 @@
// "Fix all ''compare()' method can be used to compare primitives' problems in file" "true"
public class Test {
public void test(String s1, String s2) {
System.out.println(s1.<caret>length() < s2.length() ? -1 : s1.length() == s2.length() ? 0 : 1);
System.out.println((s1.length() > s2.length()) ? -1 : s1.length() == s2.length() ? 0 : 1);
System.out.println((s1.length() > s2.length()) ? /*greater!*/+1 : s1.length() < s2.length() ? /*less!*/-1 : /*equal!*/0);
System.out.println(s1.length() == s2.length() ? 0 : s2.length() < s1.length() ? -1 : 1);
System.out.println(s1.length() < s2.length() ? 1 : s2.length() < s1.length() ? -1 : 0);
System.out.println(s1.length() < s2.length() ? -1 : s1.length() == s2.length() ? 0 : 2);
System.out.println(s1.length() < s2.length() ? 1 : s2.length() < s1.length() ? 0 : 1);
System.out.println(s1.length() == s2.length() ? 1 : s2.length() < s1.length() ? 0 : 1);
System.out.println(s1.length() == s2.length() ? 0 : s2.length() < s2.length() ? -1 : 1);
}
}
@@ -0,0 +1,8 @@
import java.util.*;
public class F<T> {
{
List list<caret>= new ArrayList<T>();
list.add("");
}
}
@@ -0,0 +1,15 @@
import javax.annotation.meta.*;
@TypeQualifierNickname()
@javax.annotation.Nonnull(when = When.MAYBE)
@interface NullableNick {}
interface UnknownInterface {
void foo(String s);
}
class ImplWithNotNull implements UnknownInterface {
public void foo(@NullableNick String s) {
System.out.println(s.<warning descr="Method invocation 'hashCode' may produce 'java.lang.NullPointerException'">hashCode</warning>());
}
}
@@ -31,6 +31,10 @@ import org.jetbrains.annotations.NotNull;
@SuppressWarnings("Annotator")
public class RegExpHighlightingTest extends LightCodeInsightFixtureTestCase {
public void testDuplicateNamedGroup() {
doTest("(?<name>abc)(?<<error descr=\"Group with name 'name' already defined\">name</error>>xyz)");
}
public void testAnonymousCapturingGroupInspection() {
myFixture.enableInspections(new AnonymousGroupInspection());
doTest("<warning descr=\"Anonymous capturing group\">(</warning>moo)<warning descr=\"Numeric back reference\">\\1</warning>");
@@ -212,37 +212,37 @@ class Test88 {
void testCollectorsToList() {
configureByTestName()
selectItem(myItems.find { it.lookupString.contains('toList') })
checkResultByFile(getTestName(false) + "_after.java")
checkResultByFileName()
}
void testStaticallyImportedCollectorsToList() {
configureByTestName()
selectItem(myItems.find { it.lookupString.contains('collect(toList())') })
checkResultByFile(getTestName(false) + "_after.java")
checkResultByFileName()
}
void testAllCollectors() {
configureByTestName()
myFixture.assertPreferredCompletionItems 0, 'collect', 'collect', 'collect(Collectors.toCollection())', 'collect(Collectors.toList())', 'collect(Collectors.toSet())'
selectItem(myItems.find { it.lookupString.contains('toCollection') })
checkResultByFile(getTestName(false) + "_after.java")
checkResultByFileName()
}
void testCollectorsToSet() {
configureByTestName()
selectItem(myItems.find { it.lookupString.contains('toSet') })
checkResultByFile(getTestName(false) + "_after.java")
checkResultByFileName()
}
void testNoExplicitTypeArgsInTernary() {
configureByTestName()
selectItem(myItems.find { it.lookupString.contains('empty') })
checkResultByFile(getTestName(false) + "_after.java")
checkResultByFileName()
}
void testCallBeforeLambda() {
configureByTestName()
checkResultByFile(getTestName(false) + "_after.java")
checkResultByFileName()
}
void testLambdaInAmbiguousCall() {
@@ -264,13 +264,13 @@ class Test88 {
void testNoSemicolonAfterVoidMethodInLambda() {
configureByTestName()
myFixture.type('l\t')
checkResultByFile(getTestName(false) + "_after.java")
checkResultByFileName()
}
void testFinishMethodReferenceWithColon() {
configureByTestName()
myFixture.type(':')
checkResultByFile(getTestName(false) + "_after.java")
checkResultByFileName()
}
void testPreferLocalsOverMethodRefs() {
@@ -286,14 +286,22 @@ class Test88 {
"}")
configureByTestName()
myFixture.type('\n')
checkResultByFile(getTestName(false) + "_after.java")
checkResultByFileName()
}
void testOverrideMethodAsDefault() {
configureByTestName()
assert LookupElementPresentation.renderElement(myFixture.lookupElements[0]).itemText == 'default void run'
myFixture.type('\t')
checkResultByFile(getTestName(false) + "_after.java")
checkResultByFileName()
}
void testChainedMethodReference() {
configureByTestName()
checkResultByFileName()
}
private checkResultByFileName() {
checkResultByFile(getTestName(false) + "_after.java")
}
}
@@ -180,6 +180,7 @@ public class GenericsHighlightingTest extends LightDaemonAnalyzerTestCase {
public void testInaccessibleThroughWildcard() { doTest7Incompatibility(false);}
public void testInconvertibleTypes() { doTest5(false); }
public void testIncompatibleReturnType() { doTest5(false); }
public void testContinueInferenceAfterFirstRawResult() { doTest5(false); }
public void testDoNotAcceptLowerBoundIfRaw() { doTest5(false); }
public void testStaticOverride() { doTest5(false); }
@@ -342,6 +343,7 @@ public class GenericsHighlightingTest extends LightDaemonAnalyzerTestCase {
public void testIDEA71582() { doTest5(false); }
public void testIDEA65377() { doTest5(false); }
public void testIDEA113526() { doTest5(true); }
public void testIncompatibleReturnTypeBounds() { doTest(LanguageLevel.JDK_1_7, JavaSdkVersion.JDK_1_7, false); }
public void testIDEA116493() { doTest(LanguageLevel.JDK_1_7, JavaSdkVersion.JDK_1_7, false); }
public void testIDEA117827() { doTest(LanguageLevel.JDK_1_7, JavaSdkVersion.JDK_1_7, false); }
public void testIDEA118037() { doTest(LanguageLevel.JDK_1_7, JavaSdkVersion.JDK_1_7, false); }
@@ -115,6 +115,7 @@ public class GraphInferenceHighlightingTest extends LightDaemonAnalyzerTestCase
public void testPullUncheckedWarningNotionThroughNestedCalls() { doTest(); }
public void testIDEA149774() { doTest(); }
public void testDisjunctionTypes() { doTest(); }
public void testValidIntersectionTypeWithCapturedBounds() { doTest(); }
public void testPushErasedStateToArguments() { doTest(); }
public void testStopAtStandaloneConditional() { doTest(); }
public void testTransitiveInferenceVariableDependencies() { doTest(); }
@@ -16,17 +16,17 @@
package com.intellij.java.codeInsight.daemon.quickFix;
import com.intellij.codeInsight.daemon.quickFix.LightQuickFixParameterizedTestCase;
import com.intellij.codeInspection.BoxForComparisonInspection;
import com.intellij.codeInspection.LocalInspectionTool;
import com.intellij.codeInspection.UseCompareMethodInspection;
import org.jetbrains.annotations.NotNull;
public class BoxForComparisonInspectionTest extends LightQuickFixParameterizedTestCase {
public class UseCompareMethodInspectionTest extends LightQuickFixParameterizedTestCase {
@NotNull
@Override
protected LocalInspectionTool[] configureLocalInspectionTools() {
return new LocalInspectionTool[]{
new BoxForComparisonInspection(),
new UseCompareMethodInspection(),
};
}
@@ -34,6 +34,6 @@ public class BoxForComparisonInspectionTest extends LightQuickFixParameterizedTe
@Override
protected String getBasePath() {
return "/codeInsight/daemonCodeAnalyzer/quickFix/boxForComparison";
return "/codeInsight/daemonCodeAnalyzer/quickFix/useCompareMethod";
}
}
@@ -55,6 +55,10 @@ public class CreateSwitchTest extends JavaCodeInsightFixtureTestCase {
doTestNotAvailable();
}
public void testNotAvailableInForUpdate() {
doTestNotAvailable();
}
private void doTestString() {
final LanguageLevelProjectExtension languageLevelProjectExtension = LanguageLevelProjectExtension.getInstance(getProject());
final LanguageLevel oldLanguageLevel = languageLevelProjectExtension.getLanguageLevel();
@@ -16,10 +16,13 @@
package com.intellij.java.codeInsight.navigation
import com.intellij.psi.PsiMember
import com.intellij.psi.PsiMethod
import com.intellij.psi.PsiReference
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase
import junit.framework.TestCase
import org.intellij.lang.annotations.Language
import org.intellij.lang.annotations.MagicConstant
import org.jetbrains.annotations.NonNls
/**
* @author Pavel.Dolgov
@@ -74,6 +77,64 @@ class JavaLangInvokeHandleNavigationTest : LightCodeInsightFixtureTestCase() {
fun testStaticSetter6() = doNegativeTest("pf1", STATIC_SETTER)
fun testStaticSetter7() = doNegativeTest("m1", STATIC_SETTER)
fun testOverloadedBothPublic() = doTestOverloaded(
"""public class Overloaded {
public void foo(int n) {}
public void foo(String s) {}
}""", VIRTUAL, "java.lang.String")
fun testOverloadedFirstPublic() = doTestOverloaded(
"""public class Overloaded {
public void foo(int n) {}
void foo(String s) {}
}""", VIRTUAL, "int")
fun testOverloadedSecondPublic() = doTestOverloaded(
"""public class Overloaded {
void foo(int n) {}
public void foo(String s) {}
}""", VIRTUAL, "java.lang.String")
fun testOverloadedInherited() {
myFixture.addClass("""public class OverloadedParent {
public static void foo(String s) {}
}""")
doTestOverloaded(
"""public class Overloaded extends OverloadedParent {
public static void foo(int n) {}
}""", STATIC, "java.lang.String")
}
fun testOverloadedStatic() = doTestOverloaded(
"""public class Overloaded {
public static void foo(int n) {}
public static void foo(String s) {}
}""", STATIC, "java.lang.String")
private fun doTestOverloaded(@NonNls @Language("JAVA") classText: String, function: String, vararg expectedParameterTypes: String) {
myFixture.addClass(classText)
val methodType = arrayOf("void", *expectedParameterTypes).map { "$it.class" }.joinToString(", ")
val member = doTestImpl("foo", """
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
class Main {
void foo() throws ReflectiveOperationException {
MethodHandles.Lookup lookup = MethodHandles.lookup();
lookup.$function(Overloaded.class, "<caret>foo", MethodType.methodType($methodType));
}
}""")
TestCase.assertTrue("Is method", member is PsiMethod)
val parameters = (member as PsiMethod).parameterList.parameters
TestCase.assertEquals("Parameter count", expectedParameterTypes.size, parameters.size)
for (i in 0 until expectedParameterTypes.size) {
TestCase.assertEquals("Parameter $i", expectedParameterTypes[i], parameters[i].type.canonicalText)
}
}
private fun doTest(name: String,
@MagicConstant(stringValues = arrayOf(VIRTUAL, STATIC, SPECIAL,
@@ -83,7 +144,7 @@ class JavaLangInvokeHandleNavigationTest : LightCodeInsightFixtureTestCase() {
doTestImpl(name, getMainClassText(name, function))
}
private fun doTestImpl(name: String, mainClassText: String) {
private fun doTestImpl(name: String, mainClassText: String): PsiMember {
val reference = getReference(mainClassText)
TestCase.assertEquals("Reference text", name, reference.canonicalText)
val resolved = reference.resolve()
@@ -91,6 +152,7 @@ class JavaLangInvokeHandleNavigationTest : LightCodeInsightFixtureTestCase() {
TestCase.assertTrue("Target is a member", resolved is PsiMember)
val member = resolved as PsiMember?
TestCase.assertEquals("Target name", name, member!!.name)
return member
}
private fun doNegativeTest(name: String,
@@ -15,9 +15,7 @@
*/
package com.intellij.java.codeInsight.navigation;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiMember;
import com.intellij.psi.PsiReference;
import com.intellij.psi.*;
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase;
import org.intellij.lang.annotations.Language;
import org.intellij.lang.annotations.MagicConstant;
@@ -93,6 +91,70 @@ public class JavaReflectionNavigationTest extends LightCodeInsightFixtureTestCas
"}");
}
public void testOverloadedMethodBothPublic() {
doTestOverloadedMethod("foo",
"class Overloaded {\n" +
" public void foo() {}\n" +
" public void foo(String s) {}\n" +
"}", false, "java.lang.String");
}
public void testOverloadedMethodPublicFirst() {
doTestOverloadedMethod("foo",
"class Overloaded {\n" +
" public void foo() {}\n" +
" void foo(String s) {}\n" +
"}", false);
}
public void testOverloadedMethodPublicSecond() {
doTestOverloadedMethod("foo",
"class Overloaded {\n" +
" void foo() {}\n" +
" public void foo(String s) {}\n" +
"}", false, "java.lang.String");
}
public void testOverloadedDeclaredMethod() {
doTestOverloadedMethod("foo",
"class Overloaded {\n" +
" public void foo() {}\n" +
" public void foo(String s) {}\n" +
"}", true, "java.lang.String");
}
public void testOverloadedInheritedMethod() {
doTestOverloadedMethod("bar",
"class OverloadedParent {" +
" public void bar(String s) {}\n" +
"}" +
"" +
"class Overloaded extends OverloadedParent {\n" +
" public void bar() {}\n" +
"}", false, "java.lang.String");
}
private void doTestOverloadedMethod(String name,
@NotNull @NonNls @Language("JAVA") String classText,
boolean isDeclared,
String... expectedParameterTypes) {
myFixture.addClass(classText);
PsiMember member = doTestImpl(name,
"class Main {" +
" void main() {" +
" Overloaded.class.get" + (isDeclared?"Declared":"") + "Method(\"<caret>"+name+"\", String.class);" +
" }" +
"}");
assertTrue("Target is a method", member instanceof PsiMethod);
PsiMethod method = (PsiMethod)member;
PsiParameter[] parameters = method.getParameterList().getParameters();
assertEquals("Parameter count", expectedParameterTypes.length, parameters.length);
for (int i = 0; i < expectedParameterTypes.length; i++) {
assertEquals("Parameter type " + i, expectedParameterTypes[i], parameters[0].getType().getCanonicalText());
}
}
private void doTest(String name,
@MagicConstant(stringValues = {FIELD, METHOD, DF, DM}) String type) {
@@ -104,7 +166,7 @@ public class JavaReflectionNavigationTest extends LightCodeInsightFixtureTestCas
doTestImpl(name, mainClassText);
}
private void doTestImpl(String name, String mainClassText) {
private PsiMember doTestImpl(String name, @NotNull @NonNls @Language("JAVA") String mainClassText) {
PsiReference reference = getReference(mainClassText);
assertEquals("Reference text", name, reference.getCanonicalText());
PsiElement resolved = reference.resolve();
@@ -112,6 +174,7 @@ public class JavaReflectionNavigationTest extends LightCodeInsightFixtureTestCas
assertTrue("Target is a member", resolved instanceof PsiMember);
PsiMember member = (PsiMember)resolved;
assertEquals("Target name", name, member.getName());
return member;
}
private void doNegativeTest(String name,
@@ -360,6 +360,17 @@ public class DataFlowInspectionTest extends DataFlowInspectionTestCase {
myFixture.enableInspections(inspection);
myFixture.testHighlighting(true, false, true, getTestName(false) + ".java");
}
public void testTypeQualifierNickname() {
addJavaxNullabilityAnnotations(myFixture);
myFixture.addClass("package bar;" +
"import javax.annotation.meta.*;" +
"@TypeQualifierNickname() @javax.annotation.NonNull(when = Maybe.MAYBE) " +
"public @interface NullableNick {}");
doTest();
}
public static void addJavaxDefaultNullabilityAnnotations(final JavaCodeInsightTestFixture fixture) {
fixture.addClass("package javax.annotation;" +
@@ -371,12 +382,23 @@ public class DataFlowInspectionTest extends DataFlowInspectionTestCase {
}
public static void addJavaxNullabilityAnnotations(final JavaCodeInsightTestFixture fixture) {
fixture.addClass("package javax.annotation;" +
"public @interface Nonnull {}");
fixture.addClass("package javax.annotation;" +
"public @interface Nullable {}");
fixture.addClass("package javax.annotation.meta;" +
"public @interface TypeQualifierDefault { java.lang.annotation.ElementType[] value() default {};}");
fixture.addClass("package javax.annotation.meta;" +
"public enum When { ALWAYS, UNKNOWN, MAYBE, NEVER }");
fixture.addClass("package javax.annotation.meta;" +
"public @interface TypeQualifierNickname {}");
fixture.addClass("package javax.annotation;" +
"import javax.annotation.meta.*;" +
"public @interface Nonnull {" +
" When when() default When.ALWAYS;" +
"}");
fixture.addClass("package javax.annotation;" +
"import javax.annotation.meta.*;" +
"@TypeQualifierNickname " +
"@Nonnull(when = When.UNKNOWN) " +
"public @interface Nullable {}");
}
public void testCustomTypeQualifierDefault() {
@@ -20,8 +20,11 @@ import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.codeInspection.InspectionsBundle;
import com.intellij.codeInspection.miscGenerics.RawTypeCanBeGenericInspection;
import com.intellij.openapi.roots.ModuleRootModificationUtil;
import com.intellij.refactoring.BaseRefactoringProcessor;
import com.intellij.testFramework.IdeaTestUtil;
import com.intellij.testFramework.LightProjectDescriptor;
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase;
import org.jetbrains.annotations.NotNull;
import java.util.List;
@@ -63,6 +66,17 @@ public class RawTypeCanBeGenericTest extends LightCodeInsightFixtureTestCase {
doTest(getMessage("list", "List<String>"));
}
public void testConflict() {
try {
doTest(getMessage("list", "List<T>"));
fail("No conflict detected");
}
catch (BaseRefactoringProcessor.ConflictsInTestsException e) {
assertEquals("Cannot convert type of expression <b>&quot;&quot;</b> from <b>java.lang.String</b> to <b>T</b><br>",
e.getMessage());
}
}
public void testAtInitializer() {
assertIntentionNotAvailable(getMessagePrefix());
}
@@ -92,4 +106,10 @@ public class RawTypeCanBeGenericTest extends LightCodeInsightFixtureTestCase {
String message = InspectionsBundle.message("inspection.raw.variable.type.can.be.generic.quickfix", "@", "@");
return message.substring(0, message.indexOf("@"));
}
@NotNull
@Override
protected LightProjectDescriptor getProjectDescriptor() {
return JAVA_1_6;
}
}
@@ -986,6 +986,22 @@ public class JavadocFormatterTest extends AbstractJavaFormatterTest {
);
}
public void test_JdWithTabs() {
doClassTest(
"\t/**\n" +
"\t \t *\n" +
"\t \t *\n" +
"\t \t */\n" +
"\tvoid check() {\n" +
"\t}",
"/**\n" +
" *\n" +
" *\n" +
" */\n" +
"void check() {\n" +
"}"
);
}
}
@@ -1,403 +0,0 @@
/*
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed 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 com.intellij.java.psi.impl.source.codeStyle;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.impl.LineSet;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.TokenType;
import com.intellij.psi.impl.source.codeStyle.TabPostFormatProcessor;
import com.intellij.psi.tree.IElementType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.jmock.api.Invocation;
import org.jmock.integration.junit4.JMock;
import org.jmock.integration.junit4.JUnit4Mockery;
import org.jmock.lib.action.CustomAction;
import org.jmock.lib.legacy.ClassImposteriser;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.ArrayList;
import java.util.List;
import static org.junit.Assert.assertEquals;
/**
* @author Denis Zhdanov
* @since 08/01/2012
*/
@RunWith(JMock.class)
public class TabPostFormatProcessorTest {
private static final String START_RANGE_MARKER = "<range>";
private static final String END_RANGE_MARKER = "</range>";
private Mockery myMockery;
private Document myDocument;
@Before
public void setUp() {
myMockery = new JUnit4Mockery() {{
setImposteriser(ClassImposteriser.INSTANCE);
}};
myDocument = myMockery.mock(Document.class);
}
@After
public void checkExpectations() {
myMockery.assertIsSatisfied();
}
@Test
public void spacesAndWholeLineInsideRange() {
doTestSpaces(
"line 1<range>\n" +
" \t \tline2\n" +
"line</range> 3",
4,
"line 1\n" +
" line2\n" +
"line 3"
);
}
@Test
public void spacesAndExactRange() {
doTestSpaces(
"line 1\n" +
"<range> \t \tline2</range>\n" +
"line 3",
4,
"line 1\n" +
" line2\n" +
"line 3"
);
}
@Test
public void spacesAndHeadIntersection() {
doTestSpaces(
"line 1<range>\n" +
" \t </range>\tline2\n" +
"line 3",
4,
"line 1\n" +
" \tline2\n" +
"line 3"
);
}
@Test
public void spacesAndTailIntersection() {
doTestSpaces(
"line 1\n" +
" \t <range>\tline2</range>\n" +
"line 3",
4,
"line 1\n" +
" \t line2\n" +
"line 3"
);
}
@Test
public void spacesAndPartialIndentInsideRange() {
doTestSpaces(
"line 1\n" +
" \t <range>\t</range> \t line2\n" +
"line 3",
4,
"line 1\n" +
" \t \t line2\n" +
"line 3"
);
}
@Test
public void tabsAndWholeLineInsideRange() {
doTestTabs(
"line 1<range>\n" +
" \t line2\n" +
"line</range> 3",
4,
"line 1\n" +
"\t \t line2\n" +
"line 3"
);
}
@Test
public void tabsAndHeadIntersection() {
doTestTabs(
"line 1<range>\n" +
" \t </range> line2\n" +
"line 3",
4,
"line 1\n" +
"\t\t line2\n" +
"line 3"
);
}
@Test
public void tabsAndTailIntersection() {
doTestTabs(
"line 1\n" +
" <range> line2\n" +
"</range>line 3",
4,
"line 1\n" +
" \t line2\n" +
"line 3"
);
}
@Test
public void tabsAndPartialIndentInsideRange() {
doTestTabs(
"line 1\n" +
" <range> </range> line2\n" +
"line 3",
4,
"line 1\n" +
" \t line2\n" +
"line 3"
);
}
@Test
public void smartTabsForTheFirstLine() {
doTestSmartTabs(
" line 1\n" +
"\t line 2",
4,
"\t\t line 1\n" +
"\t line 2"
);
}
@Test
public void smartTabsFromUpperLine() {
doTestSmartTabs(
"\t\t line 1<range>\n" +
" 2</range>",
4,
"\t\t line 1\n" +
"\t\t 2"
);
}
@Test
public void smartTabsExactReplacement() {
doTestSmartTabs(
"\tline 1<range>\n" +
" line 2</range>",
4,
"\tline 1\n" +
"\tline 2"
);
}
@Test
public void smartTabsMismatchedIndent() {
doTestSmartTabs(
" \tline 1<range>\n" +
"\t line 2</range>",
4,
" \tline 1\n" +
"\t line 2"
);
}
@Test
public void smartTabsPartialMatchedIndent() {
doTestSmartTabs(
"\t\tline 1\n" +
" <range> line 2</range>",
4,
"\t\tline 1\n" +
" \tline 2"
);
}
@Test
public void smartTabsPartialMisMatchedIndent() {
doTestSmartTabs(
"\t\tline 1\n" +
" <range> line 2</range>",
4,
"\t\tline 1\n" +
" line 2"
);
}
private void doTestSpaces(@NotNull String initial, final int tabWidth, @NotNull String expected) {
doTest(initial, expected, false, false, tabWidth);
}
private void doTestTabs(@NotNull String initial, final int tabWidth, @NotNull String expected) {
doTest(initial, expected, true, false, tabWidth);
}
private void doTestSmartTabs(@NotNull String initial, final int tabWidth, @NotNull String expected) {
doTest(initial, expected, true, true, tabWidth);
}
private void doTest(@NotNull String initial, @NotNull String expected, boolean useTabs, boolean smartTabs, int tabWidth) {
doDocumentTest(initial, expected, useTabs, smartTabs, tabWidth);
doPsiTest(initial, expected, useTabs, smartTabs, tabWidth);
}
private void doDocumentTest(@NotNull String initial, @NotNull String expected, boolean useTabs, boolean smartTabs, int tabWidth) {
Pair<String, TextRange> pair = parse(initial);
final StringBuilder text = new StringBuilder(pair.first);
final TextRange range = pair.second;
myMockery.checking(new Expectations() {{
allowing(myDocument).getCharsSequence(); will(returnValue(text.toString()));
allowing(myDocument).getTextLength(); will(returnValue(text.length()));
}});
final LineSet lines = LineSet.createLineSet(myDocument.getCharsSequence());
myMockery.checking(new Expectations() {{
allowing(myDocument).getLineNumber(with(any(int.class))); will(new CustomAction("getLineNumber()") {
@Override
public Object invoke(Invocation invocation) throws Throwable {
return lines.findLineIndex((Integer)invocation.getParameter(0));
}
});
allowing(myDocument).getLineStartOffset(with(any(int.class))); will(new CustomAction("getLineStartOffset()") {
@Override
public Object invoke(Invocation invocation) throws Throwable {
return lines.getLineStart((Integer)invocation.getParameter(0));
}
});
allowing(myDocument).getLineEndOffset(with(any(int.class))); will(new CustomAction("getLineEndOffset()") {
@Override
public Object invoke(Invocation invocation) throws Throwable {
return lines.getLineEnd((Integer)invocation.getParameter(0));
}
});
allowing(myDocument).replaceString(with(any(int.class)), with(any(int.class)), with(any(String.class)));
will(new CustomAction("replaceString") {
@Nullable
@Override
public Object invoke(Invocation invocation) throws Throwable {
int start = (Integer)invocation.getParameter(0);
int end = (Integer)invocation.getParameter(1);
String newText = (String)invocation.getParameter(2);
text.replace(start, end, newText);
return null;
}
});
}});
TabPostFormatProcessor.processViaDocument(myDocument, range, useTabs, smartTabs, tabWidth);
assertEquals(expected, text.toString());
}
private static Pair<String, TextRange> parse(@NotNull String text) {
int rangeMarkerStart = text.indexOf(START_RANGE_MARKER);
int rangeMarkerEnd = text.indexOf(END_RANGE_MARKER);
final StringBuilder buffer = new StringBuilder();
final TextRange range;
if (rangeMarkerStart >= 0 && rangeMarkerEnd >= 0) {
range = TextRange.create(rangeMarkerStart, rangeMarkerEnd - START_RANGE_MARKER.length());
buffer.append(text.substring(0, rangeMarkerStart))
.append(text.substring(rangeMarkerStart + START_RANGE_MARKER.length(), rangeMarkerEnd))
.append(text.substring(rangeMarkerEnd + END_RANGE_MARKER.length()));
}
else {
range = TextRange.create(0, text.length());
buffer.append(text);
}
return Pair.create(buffer.toString(), range);
}
private void doPsiTest(@NotNull String initial, @NotNull String expected, boolean useTabs, boolean smartTabs, int tabWidth) {
final List<ASTNode> children = new ArrayList<>();
final List<StringBuilder> childrenText = new ArrayList<>();
Pair<String, TextRange> pair = parse(initial);
final String text = pair.first;
int start = 0;
boolean inWhiteSpace = initial.charAt(0) == ' ' || initial.charAt(0) == '\t';
for (int i = 1; i <= text.length(); i++) {
if (i == text.length() || ((StringUtil.isWhiteSpace(text.charAt(i))) ^ inWhiteSpace)) {
final int childIndex = children.size();
final int startOffset = start;
childrenText.add(new StringBuilder(text.substring(start, i)));
final ASTNode child = myMockery.mock(ASTNode.class, "child" + childIndex);
children.add(child);
final IElementType type = inWhiteSpace ? TokenType.WHITE_SPACE : TokenType.CODE_FRAGMENT;
myMockery.checking(new Expectations() {{
allowing(child).getElementType(); will(returnValue(type));
allowing(child).getChars(); will(returnValue(childrenText.get(childIndex)));
allowing(child).getTextLength(); will(returnValue(childrenText.get(childIndex).length()));
allowing(child).getStartOffset(); will(returnValue(startOffset));
}});
inWhiteSpace = !inWhiteSpace;
start = i;
}
}
final ASTNode root = myMockery.mock(ASTNode.class);
myMockery.checking(new Expectations() {{
allowing(root).getFirstChildNode(); will(returnValue(children.get(0)));
allowing(root).getTextLength(); will(returnValue(text.length()));
allowing(root).getStartOffset(); will(returnValue(0));
}});
TabPostFormatProcessor.TreeHelper helper = new TabPostFormatProcessor.TreeHelper() {
@Override
public ASTNode prevLeaf(@NotNull ASTNode current) {
int i = children.indexOf(current);
return i > 0 ? children.get(i - 1) : null;
}
@Override
public ASTNode nextLeaf(@NotNull ASTNode current) {
int i = children.indexOf(current);
return i < children.size() - 1 ? children.get(i + 1) : null;
}
@Override
public ASTNode firstLeaf(@NotNull ASTNode startNode) {
return root == startNode ? children.get(0) : null;
}
@Override
public void replace(@NotNull String newText, @NotNull TextRange range, @NotNull ASTNode leaf) {
int i = children.indexOf(leaf);
childrenText.get(i).replace(range.getStartOffset() - leaf.getStartOffset(), range.getEndOffset() - leaf.getStartOffset(), newText);
}
};
TabPostFormatProcessor.processViaPsi(root, pair.second, helper, useTabs, smartTabs, tabWidth);
StringBuilder actual = new StringBuilder();
for (ASTNode child : children) {
actual.append(child.getChars());
}
assertEquals(expected, actual.toString());
}
}
@@ -115,7 +115,9 @@ public class ConvertFieldToThreadLocalIntention extends PsiElementBaseIntentionA
}
PsiExpression initializer = psiField.getInitializer();
if (initializer == null) {
if (initializer == null &&
!psiField.hasModifierProperty(PsiModifier.FINAL)) {
final PsiType type = psiField.getType();
String initializerText = null;
if (PsiType.BOOLEAN.equals(type)) {
@@ -12,6 +12,8 @@ import com.intellij.refactoring.typeMigration.TypeConversionDescriptor;
import com.intellij.refactoring.typeMigration.TypeConversionDescriptorBase;
import com.intellij.refactoring.typeMigration.TypeEvaluator;
import com.intellij.refactoring.typeMigration.TypeMigrationLabeler;
import com.intellij.util.ObjectUtils;
import com.siyeh.HardcodedMethodConstants;
import com.siyeh.ig.psiutils.ParenthesesUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -38,15 +40,15 @@ public class AtomicConversionRule extends TypeConversionRule {
}
private static boolean isAtomicTypeMigration(PsiType from, PsiClassType to, PsiExpression context) {
if (PsiType.INT.equals(from) && to.getCanonicalText().equals(AtomicInteger.class.getName())) {
if (PsiType.INT.isAssignableFrom(from) && to.getCanonicalText().equals(AtomicInteger.class.getName())) {
return true;
}
if (PsiType.LONG.isAssignableFrom(from) && to.getCanonicalText().equals(AtomicLong.class.getName())) {
return true;
}
if (from.equals(PsiType.INT.createArrayType()) && to.getCanonicalText().equals(AtomicIntegerArray.class.getName())) {
return true;
}
if (PsiType.LONG.equals(from) && to.getCanonicalText().equals(AtomicLong.class.getName())) {
return true;
}
if (from.equals(PsiType.LONG.createArrayType()) && to.getCanonicalText().equals(AtomicLongArray.class.getName())) {
return true;
}
@@ -342,6 +344,9 @@ public class AtomicConversionRule extends TypeConversionRule {
if (context instanceof PsiArrayAccessExpression) {
return new TypeConversionDescriptor("$qualifier$[$idx$]", "$qualifier$.get($idx$)", (PsiExpression)context);
}
if (parent instanceof PsiReferenceExpression && isReferenceToLengthField((PsiReferenceExpression)parent)) {
return new TypeConversionDescriptor("$qualifier$.length", "$qualifier$.length()", (PsiExpression)parent);
}
return null;
}
@@ -438,4 +443,13 @@ public class AtomicConversionRule extends TypeConversionRule {
return null;
}
private static boolean isReferenceToLengthField(@NotNull PsiReferenceExpression refExpr) {
if (!"length".equals(refExpr.getReferenceName())) {
return false;
}
PsiClass aClass = JavaPsiFacade.getElementFactory(refExpr.getProject()).getArrayClass(PsiUtil.getLanguageLevel(refExpr));
PsiField lengthField = ObjectUtils.notNull(aClass.findFieldByName(HardcodedMethodConstants.LENGTH, false));
return refExpr.isReferenceTo(lengthField);
}
}
@@ -1,5 +1,7 @@
package com.intellij.refactoring.typeMigration.rules;
import com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil;
import com.intellij.codeInsight.daemon.impl.quickfix.VariableAccessFromInnerClassFix;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Comparing;
import com.intellij.psi.*;
@@ -8,10 +10,14 @@ import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.refactoring.typeMigration.TypeConversionDescriptor;
import com.intellij.refactoring.typeMigration.TypeConversionDescriptorBase;
import com.intellij.refactoring.typeMigration.TypeEvaluator;
import com.intellij.refactoring.typeMigration.TypeMigrationLabeler;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
public class ThreadLocalConversionRule extends TypeConversionRule {
private static final Logger LOG = Logger.getInstance(ThreadLocalConversionRule.class);
@@ -150,24 +156,39 @@ public class ThreadLocalConversionRule extends TypeConversionRule {
public static TypeConversionDescriptor wrapWithNewExpression(PsiType to, PsiType from, PsiExpression initializer) {
final String boxedTypeName = from instanceof PsiPrimitiveType ? ((PsiPrimitiveType)from).getBoxedTypeName() : from.getCanonicalText();
return new TypeConversionDescriptor("$qualifier$", "new " +
to.getCanonicalText() +
"() {\n" +
"@Override \n" +
"protected " +
boxedTypeName +
" initialValue() {\n" +
" return " +
(PsiUtil.isLanguageLevel5OrHigher(initializer)
? initializer.getText()
: (from instanceof PsiPrimitiveType ? "new " +
((PsiPrimitiveType)from).getBoxedTypeName() +
"(" +
initializer.getText() +
")" : initializer.getText())) +
";\n" +
"}\n" +
"}", initializer);
List<PsiVariable> toMakeFinal = TypeConversionRuleUtil.getVariablesToMakeFinal(initializer);
if (toMakeFinal == null) return null;
return new WrappingWithInnerClassOrLambdaDescriptor("$qualifier$",
createThreadLocalInitializerReplacement(to, from, initializer, boxedTypeName),
initializer,
toMakeFinal);
}
private static String createThreadLocalInitializerReplacement(PsiType to,
PsiType from,
PsiExpression initializer,
String boxedTypeName) {
if (PsiUtil.isLanguageLevel8OrHigher(initializer)) {
return "java.lang.ThreadLocal.withInitial(() -> " + initializer.getText() + ")";
}
return "new " +
to.getCanonicalText() +
"() {\n" +
"@Override \n" +
"protected " +
boxedTypeName +
" initialValue() {\n" +
" return " +
(PsiUtil.isLanguageLevel5OrHigher(initializer)
? initializer.getText()
: (from instanceof PsiPrimitiveType ? "new " +
((PsiPrimitiveType)from).getBoxedTypeName() +
"(" +
initializer.getText() +
")" : initializer.getText())) +
";\n" +
"}\n" +
"}";
}
private static String toPrimitive(String replaceByArg, PsiType from, PsiElement context) {
@@ -221,5 +242,27 @@ public class ThreadLocalConversionRule extends TypeConversionRule {
return toBoxed(arg, from, context);
}
private static class WrappingWithInnerClassOrLambdaDescriptor extends TypeConversionDescriptor {
private final List<PsiVariable> myVariablesToMakeFinal;
private WrappingWithInnerClassOrLambdaDescriptor(@NonNls final String stringToReplace,
@NonNls final String replaceByString,
final PsiExpression expression,
@NotNull List<PsiVariable> toMakeFinal) {
super(stringToReplace, replaceByString, expression);
myVariablesToMakeFinal = toMakeFinal;
}
@Override
public PsiExpression replace(PsiExpression expression, @NotNull TypeEvaluator evaluator) {
PsiExpression replaced = super.replace(expression, evaluator);
boolean atLeastJava8 = PsiUtil.isLanguageLevel8OrHigher(replaced);
for (PsiVariable var : myVariablesToMakeFinal) {
if (!atLeastJava8 || !HighlightControlFlowUtil.isEffectivelyFinal(var, replaced, null)) {
VariableAccessFromInnerClassFix.fixAccess(var, replaced);
}
}
return replaced;
}
}
}
@@ -0,0 +1,74 @@
/*
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed 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 com.intellij.refactoring.typeMigration.rules;
import com.intellij.psi.*;
import com.intellij.psi.controlFlow.*;
import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
class TypeConversionRuleUtil {
static List<PsiVariable> getVariablesToMakeFinal(@NotNull PsiExpression expression) {
final ControlFlow controlFlow;
try {
controlFlow = ControlFlowFactory.getInstance(expression.getProject()).getControlFlow(expression, new MyControlFlowPolicy(expression), false, false);
}
catch (AnalysisCanceledException e) {
return null;
}
Collection<PsiVariable> writtenVariables = ControlFlowUtil.getWrittenVariables(controlFlow, 0, controlFlow.getSize(), false);
if (!writtenVariables.isEmpty()) return null;
return ControlFlowUtil.getUsedVariables(controlFlow, 0, controlFlow.getSize())
.stream()
.filter(v -> !v.hasModifierProperty(PsiModifier.FINAL))
.collect(Collectors.toList());
}
private static class MyControlFlowPolicy implements ControlFlowPolicy {
private final PsiElement myElement;
public MyControlFlowPolicy(PsiElement element) {myElement = element;}
@Override
public PsiVariable getUsedVariable(@NotNull PsiReferenceExpression refExpr) {
if (refExpr.isQualified()) return null;
PsiElement refElement = refExpr.resolve();
if ((refElement instanceof PsiLocalVariable || refElement instanceof PsiParameter) &&
!PsiTreeUtil.isAncestor(myElement, refElement, true)) {
return (PsiVariable) refElement;
}
return null;
}
@Override
public boolean isParameterAccepted(@NotNull PsiParameter psiParameter) {
return true;
}
@Override
public boolean isLocalVariableAccepted(@NotNull PsiLocalVariable psiVariable) {
return true;
}
}
}
@@ -0,0 +1,43 @@
/*
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed 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 com.intellij.codeInsight;
import com.intellij.codeInsight.daemon.quickFix.LightQuickFixParameterizedTestCase;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.testFramework.PlatformTestUtil;
import org.jetbrains.annotations.NotNull;
public class ConvertToThreadLocalIntention6Test extends LightQuickFixParameterizedTestCase {
@Override
protected String getBasePath() {
return "/intentions/threadLocal6";
}
@NotNull
@Override
protected String getTestDataPath() {
return PlatformTestUtil.getCommunityPath() + "/java/typeMigration/testData";
}
public void test() throws Exception {
doAllTests();
}
@Override
protected LanguageLevel getLanguageLevel() {
return LanguageLevel.JDK_1_7;
}
}
@@ -8,11 +8,6 @@ import org.jetbrains.annotations.NotNull;
* @author anna
*/
public class ConvertToThreadLocalIntentionTest extends LightQuickFixParameterizedTestCase {
@Override
protected boolean shouldBeAvailableAfterExecution() {
return true;
}
@Override
protected String getBasePath() {
return "/intentions/threadLocal";
@@ -1,6 +1,7 @@
package com.intellij.refactoring;
import com.intellij.codeInsight.ConvertToAtomicIntentionTest;
import com.intellij.codeInsight.ConvertToThreadLocalIntention6Test;
import com.intellij.codeInsight.ConvertToThreadLocalIntentionTest;
import com.intellij.codeInsight.inspections.GuavaInspectionTest;
import junit.framework.Test;
@@ -19,6 +20,7 @@ public class AllTypeMigrationTests {
suite.addTestSuite(WildcardTypeMigrationTest.class);
suite.addTestSuite(ConvertToAtomicIntentionTest.class);
suite.addTestSuite(ConvertToThreadLocalIntentionTest.class);
suite.addTestSuite(ConvertToThreadLocalIntention6Test.class);
suite.addTestSuite(GuavaInspectionTest.class);
return suite;
}
@@ -100,4 +100,8 @@ public class TypeMigrationByAtomicRuleTest extends TypeMigrationTestBase{
public void testChainedInitialization() {
doTestFieldType("a", myJavaFacade.getElementFactory().createTypeFromText("java.util.concurrent.atomic.AtomicInteger", null));
}
public void testLiteralMigration() {
doTestFieldType("a", myJavaFacade.getElementFactory().createTypeFromText("java.util.concurrent.atomic.AtomicLong", null));
}
}
@@ -0,0 +1,10 @@
import java.util.concurrent.atomic.AtomicIntegerArray;
// "Convert to atomic" "true"
class Test {
final AtomicIntegerArray ii = new AtomicIntegerArray(new int[12]);
void m() {
int k = ii.length();
}
}
@@ -0,0 +1,8 @@
// "Convert to atomic" "true"
class Test {
int[] i<caret>i = new int[12];
void m() {
int k = ii.length;
}
}
@@ -1,11 +1,6 @@
// "Convert to ThreadLocal" "true"
class Test {
final ThreadLocal<Integer> field = new ThreadLocal<Integer>() {
@Override
protected Integer initialValue() {
return 0;
}
};
final ThreadLocal<Integer> field = ThreadLocal.withInitial(() -> 0);
void foo() {
field.set(field.get() + 1);
}
@@ -1,11 +1,6 @@
// "Convert to ThreadLocal" "true"
class Test {
final ThreadLocal<String> field = new ThreadLocal<String>() {
@Override
protected String initialValue() {
return "";
}
};
final ThreadLocal<String> field = ThreadLocal.withInitial(() -> "");
void foo() {
System.out.println(field.get());
}
@@ -1,11 +1,6 @@
// "Convert to ThreadLocal" "true"
class Test {
final ThreadLocal<Integer> field = new ThreadLocal<Integer>() {
@Override
protected Integer initialValue() {
return new Integer(0);
}
};
final ThreadLocal<Integer> field = ThreadLocal.withInitial(() -> new Integer(0));
void foo() {
if (field.get() == null) return;
}
@@ -1,11 +1,6 @@
// "Convert to ThreadLocal" "true"
class Test {
final ThreadLocal<String> field = new ThreadLocal<String>() {
@Override
protected String initialValue() {
return "";
}
};
final ThreadLocal<String> field = ThreadLocal.withInitial(() -> "");
void foo() {
if (field.get().indexOf("a") == -1) return;
}
@@ -1,11 +1,6 @@
// "Convert to ThreadLocal" "true"
class Test {
final ThreadLocal<Integer> field = new ThreadLocal<Integer>() {
@Override
protected Integer initialValue() {
return new Integer(0);
}
};
final ThreadLocal<Integer> field = ThreadLocal.withInitial(() -> new Integer(0));
void foo(Test t) {
if (t.field.get() == null) return;
}
@@ -2,11 +2,6 @@
class Test {
static final ThreadLocal<Integer> field;
static {
field = new ThreadLocal<Integer>() {
@Override
protected Integer initialValue() {
return new Integer(0);
}
};
field = ThreadLocal.withInitial(() -> new Integer(0));
}
}
@@ -1,11 +1,6 @@
// "Convert to ThreadLocal" "true"
class X {
private final ThreadLocal<byte[]> bytes = new ThreadLocal<byte[]>() {
@Override
protected byte[] initialValue() {
return new byte[10];
}
};
private final ThreadLocal<byte[]> bytes = ThreadLocal.withInitial(() -> new byte[10]);
byte foo(byte b) {
bytes.get()[0] = 1;
@@ -1,9 +1,4 @@
// "Convert to ThreadLocal" "true"
class X {
final ThreadLocal<Integer> i = new ThreadLocal<Integer>() {
@Override
protected Integer initialValue() {
return 0;
}
};
final ThreadLocal<Integer> i = ThreadLocal.withInitial(() -> 0);
}
@@ -1,9 +1,4 @@
// "Convert to ThreadLocal" "true"
class Test {
final ThreadLocal<String[]> field = new ThreadLocal<String[]>() {
@Override
protected String[] initialValue() {
return new String[]{};
}
};
final ThreadLocal<String[]> field = ThreadLocal.withInitial(() -> new String[]{});
}
@@ -0,0 +1,12 @@
// "Convert to ThreadLocal" "true"
class Main {
private final ThreadLocal<Boolean> property;
Main3(boolean property) {
if (property) {
property = false;
}
boolean finalProperty = property;
this.property = ThreadLocal.withInitial(() -> finalProperty);
}
}
@@ -0,0 +1,8 @@
// "Convert to ThreadLocal" "true"
class Foo {
private final ThreadLocal<Boolean> property;
Foo(boolean property) {
this.property = ThreadLocal.withInitial(() -> property);
}
}
@@ -1,11 +1,6 @@
// "Convert to ThreadLocal" "true"
class X {
private final ThreadLocal<String> s = new ThreadLocal<String>() {
@Override
protected String initialValue() {
return "";
}
};
private final ThreadLocal<String> s = ThreadLocal.withInitial(() -> "");
private String t;
private String u;
}
@@ -1,9 +1,4 @@
// "Convert to ThreadLocal" "true"
class Test {
final ThreadLocal<Integer> field = new ThreadLocal<Integer>() {
@Override
protected Integer initialValue() {
return 0;
}
};
final ThreadLocal<Integer> field = ThreadLocal.withInitial(() -> 0);
}
@@ -5,10 +5,5 @@ import java.lang.annotation.*;
public @interface TA { int value(); }
class Test {
final ThreadLocal<@TA(42) Integer> field = new ThreadLocal<Integer>() {
@Override
protected Integer initialValue() {
return 0;
}
};
final ThreadLocal<@TA(42) Integer> field = ThreadLocal.withInitial(() -> 0);
}
@@ -0,0 +1,11 @@
// "Convert to ThreadLocal" "true"
class Main {
private final boolean propert<caret>y;
Main3(boolean property) {
if (property) {
property = false;
}
this.property = property;
}
}
@@ -0,0 +1,8 @@
// "Convert to ThreadLocal" "true"
class Foo {
private final boolean prope<caret>rty;
Foo(boolean property) {
this.property = property;
}
}
@@ -0,0 +1,13 @@
// "Convert to ThreadLocal" "true"
class Main {
private final ThreadLocal<Boolean> property;
Main3(final boolean property) {
this.property = new ThreadLocal<Boolean>() {
@Override
protected Boolean initialValue() {
return property;
}
};
}
}
@@ -0,0 +1,8 @@
// "Convert to ThreadLocal" "true"
class Main {
private final boolean propert<caret>y;
Main3(boolean property) {
this.property = property;
}
}

Some files were not shown because too many files have changed in this diff Show More