mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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,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);
|
||||
}
|
||||
|
||||
+3
-1
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
+5
-3
@@ -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) {
|
||||
|
||||
+9
-1
@@ -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));
|
||||
}
|
||||
|
||||
-157
@@ -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) + ")");
|
||||
}
|
||||
}
|
||||
}
|
||||
+380
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+8
-3
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -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;
|
||||
|
||||
+33
-17
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+19
-1
@@ -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;
|
||||
|
||||
+13
-20
@@ -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;
|
||||
}
|
||||
|
||||
+3
-3
@@ -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();
|
||||
}
|
||||
|
||||
+14
-8
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -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;
|
||||
|
||||
+24
-11
@@ -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) {
|
||||
|
||||
+5
@@ -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) {
|
||||
|
||||
-534
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+35
-12
@@ -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();
|
||||
|
||||
+22
-3
@@ -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
|
||||
|
||||
+10
-5
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -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++) { }
|
||||
}
|
||||
}
|
||||
+10
@@ -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> {
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
class A {
|
||||
{
|
||||
Runnable r = Syst::setOu<caret>
|
||||
}
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
class A {
|
||||
{
|
||||
Runnable r = System::setOut;<caret>
|
||||
}
|
||||
}
|
||||
+11
@@ -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> { }
|
||||
+24
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+10
@@ -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;}
|
||||
}
|
||||
+10
@@ -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;}
|
||||
}
|
||||
+13
@@ -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);
|
||||
}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// "Surround with array initialization" "false"
|
||||
class A {
|
||||
void m1(String[] s,
|
||||
String[] s2,
|
||||
String[] s3) {}
|
||||
{
|
||||
m1( <caret>null, null);
|
||||
}
|
||||
}
|
||||
+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.compare(a, b) > 0) {
|
||||
+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 int test(String s1, String s2) {
|
||||
int res = Integer.compare(s1.length(), s2.length());
|
||||
+16
@@ -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);
|
||||
}
|
||||
}
|
||||
+17
@@ -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;
|
||||
}
|
||||
}
|
||||
+18
@@ -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
-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
-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 int test(String s1, String s2) {
|
||||
int res = new Integer(s1.length()).co<caret>mpareTo(s2.length());
|
||||
+18
@@ -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);
|
||||
}
|
||||
}
|
||||
+22
@@ -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;
|
||||
}
|
||||
}
|
||||
+15
@@ -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>");
|
||||
|
||||
+18
-10
@@ -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")
|
||||
}
|
||||
}
|
||||
+2
@@ -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); }
|
||||
|
||||
+1
@@ -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(); }
|
||||
|
||||
+4
-4
@@ -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();
|
||||
|
||||
+63
-1
@@ -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,
|
||||
|
||||
+67
-4
@@ -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,
|
||||
|
||||
+26
-4
@@ -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>""</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;
|
||||
}
|
||||
}
|
||||
|
||||
+16
@@ -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" +
|
||||
"}"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
-403
@@ -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());
|
||||
}
|
||||
}
|
||||
+3
-1
@@ -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)) {
|
||||
|
||||
+18
-4
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+61
-18
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+74
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+43
@@ -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;
|
||||
}
|
||||
}
|
||||
-5
@@ -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[]{});
|
||||
}
|
||||
+12
@@ -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);
|
||||
}
|
||||
+11
@@ -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
Reference in New Issue
Block a user