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:
@@ -17,6 +17,8 @@ and build numbers for older releases of IntelliJ IDEA can be found on the page o
|
||||
If you intend to make open source contributions to the IntelliJ Platform,
|
||||
see [Contributing to the IntelliJ Project](http://www.jetbrains.org/display/IJOS/Contribute) for more information.
|
||||
|
||||
_**Tags:**_ The GitHub repository doesn't contain any tags for performance reasons. If you need to check out a tag, fetch it from [the repository mirror](git://git.jetbrains.org/idea/community.git) which contains all of them. There are scripts `fetch-tag.sh` and `list-tags.sh` which simplify the task.
|
||||
|
||||
_**Speed Tip:**_ If the complete repository history isn't needed then using a shallow clone (`git clone --depth 1`) will save significant time.
|
||||
|
||||
These Git operations can also be done through the [IntelliJ IDEA user interface](https://www.jetbrains.com/help/idea/using-git-integration.html).
|
||||
|
||||
@@ -563,7 +563,7 @@ update_install_dir:
|
||||
${LogText} ""
|
||||
${LogText} " NOTE: Specified install dir: $INSTDIR is required administrative rights."
|
||||
${LogText} " It is corresponding with the admin mode in silent config file."
|
||||
${LogText} " But installation has been run with user mode. So install dir has been changed to: "
|
||||
${LogText} " But installation has been run with user mode. So install folder has been changed to the default: "
|
||||
StrCpy $INSTDIR "$LOCALAPPDATA\${MANUFACTURER}\${PRODUCT_WITH_VER}"
|
||||
${LogText} " $INSTDIR "
|
||||
${LogText} ""
|
||||
@@ -1313,7 +1313,10 @@ uac_elevate:
|
||||
uac_err:
|
||||
Abort
|
||||
uac_elevation_aborted:
|
||||
IfSilent done set_install_dir
|
||||
IfSilent 0 set_install_dir
|
||||
${LogText} ""
|
||||
${LogText} " NOTE: UAC elevation has been aborted. Installation dir will be changed."
|
||||
${LogText} ""
|
||||
set_install_dir:
|
||||
StrCpy $INSTDIR "$LOCALAPPDATA\${MANUFACTURER}\${PRODUCT_WITH_VER}"
|
||||
goto done
|
||||
|
||||
Executable
+25
@@ -0,0 +1,25 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Fetches the given tag and creates it locally.
|
||||
# Usage: ./fetch-tag.sh <name-of-tag>
|
||||
# Example: ./fetch-tag.sh idea/183.4139.22
|
||||
|
||||
|
||||
if [[ -z "$1" ]] ; then
|
||||
echo "
|
||||
Usage: ./fetch-tag.sh <name-of-tag>
|
||||
Example: ./fetch-tag.sh idea/183.4139.22"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
TAG="$1"
|
||||
|
||||
set -e # Any command which returns non-zero exit code will cause this shell script to exit immediately
|
||||
|
||||
git fetch -q git://git.jetbrains.org/idea/community.git $TAG
|
||||
git tag $TAG FETCH_HEAD
|
||||
|
||||
HASH=`git rev-parse $TAG`
|
||||
|
||||
echo "The tag $TAG has been created at $HASH"
|
||||
|
||||
@@ -14,7 +14,7 @@ import com.intellij.debugger.engine.DebugProcessImpl;
|
||||
import com.intellij.debugger.engine.JavaStackFrame;
|
||||
import com.intellij.debugger.engine.SuspendContextImpl;
|
||||
import com.intellij.debugger.impl.DebuggerContextImpl;
|
||||
import com.intellij.debugger.impl.DebuggerUtilsImpl;
|
||||
import com.intellij.debugger.impl.DebuggerUtilsEx;
|
||||
import com.intellij.debugger.jdi.StackFrameProxyImpl;
|
||||
import com.intellij.debugger.settings.DebuggerSettings;
|
||||
import com.intellij.debugger.ui.impl.watch.DebuggerTreeNodeImpl;
|
||||
@@ -328,9 +328,8 @@ public class PopFrameAction extends DebuggerAction implements DumbAware {
|
||||
enable = proxy.getVirtualMachine().canPopFrames();
|
||||
}
|
||||
|
||||
Project project = e.getProject();
|
||||
if((ActionPlaces.isMainMenuOrActionSearch(e.getPlace()) || ActionPlaces.DEBUGGER_TOOLBAR.equals(e.getPlace()))
|
||||
&& project != null && DebuggerUtilsImpl.isCurrentJavaDebugProcess(project)) {
|
||||
&& DebuggerUtilsEx.isInJavaSession(e)) {
|
||||
e.getPresentation().setEnabled(enable);
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -733,6 +733,7 @@ public class DebuggerSession implements AbstractDebuggerSession {
|
||||
|
||||
@Override
|
||||
protected void hyperlinkActivated(@NotNull Notification notification, @NotNull HyperlinkEvent e) {
|
||||
notification.hideBalloon();
|
||||
SuspendContextImpl suspendContext = SoftReference.dereference(myContextRef);
|
||||
if (suspendContext != null) {
|
||||
switchContext(suspendContext);
|
||||
|
||||
@@ -5,7 +5,6 @@ import com.intellij.debugger.DebuggerBundle;
|
||||
import com.intellij.debugger.actions.DebuggerAction;
|
||||
import com.intellij.debugger.engine.DebugProcess;
|
||||
import com.intellij.debugger.engine.DebugProcessImpl;
|
||||
import com.intellij.debugger.engine.JavaDebugProcess;
|
||||
import com.intellij.debugger.engine.StackFrameContext;
|
||||
import com.intellij.debugger.engine.evaluation.CodeFragmentKind;
|
||||
import com.intellij.debugger.engine.evaluation.EvaluateException;
|
||||
@@ -35,8 +34,6 @@ import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.util.net.NetUtils;
|
||||
import com.intellij.util.xmlb.SkipDefaultValuesSerializationFilters;
|
||||
import com.intellij.util.xmlb.XmlSerializer;
|
||||
import com.intellij.xdebugger.XDebugSession;
|
||||
import com.intellij.xdebugger.XDebuggerManager;
|
||||
import com.intellij.xdebugger.XExpression;
|
||||
import com.intellij.xdebugger.impl.breakpoints.XExpressionState;
|
||||
import com.sun.jdi.*;
|
||||
@@ -290,9 +287,4 @@ public class DebuggerUtilsImpl extends DebuggerUtilsEx{
|
||||
}
|
||||
return StreamEx.empty();
|
||||
}
|
||||
|
||||
public static boolean isCurrentJavaDebugProcess(@NotNull Project project) {
|
||||
XDebugSession session = XDebuggerManager.getInstance(project).getCurrentSession();
|
||||
return session != null && session.getDebugProcess() instanceof JavaDebugProcess;
|
||||
}
|
||||
}
|
||||
+33
-2
@@ -16,9 +16,9 @@
|
||||
package com.intellij.codeInspection.dataFlow;
|
||||
|
||||
import com.intellij.codeInspection.dataFlow.value.*;
|
||||
import com.intellij.psi.PsiPrimitiveType;
|
||||
import com.intellij.psi.PsiType;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.util.Function;
|
||||
import com.siyeh.ig.psiutils.MethodCallUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.OptionalInt;
|
||||
@@ -30,6 +30,37 @@ public abstract class ContractValue {
|
||||
}
|
||||
|
||||
abstract DfaValue makeDfaValue(DfaValueFactory factory, DfaCallArguments arguments);
|
||||
|
||||
public DfaValue fromCall(DfaValueFactory factory, PsiCallExpression call) {
|
||||
PsiMethod method = call.resolveMethod();
|
||||
if (method == null) return DfaUnknownValue.getInstance();
|
||||
PsiExpressionList argumentList = call.getArgumentList();
|
||||
if (argumentList == null) return DfaUnknownValue.getInstance();
|
||||
DfaValue qualifierValue = null;
|
||||
if (call instanceof PsiMethodCallExpression) {
|
||||
PsiExpression qualifier = ((PsiMethodCallExpression)call).getMethodExpression().getQualifierExpression();
|
||||
qualifierValue = factory.createValue(qualifier);
|
||||
}
|
||||
if (qualifierValue == null) {
|
||||
qualifierValue = DfaUnknownValue.getInstance();
|
||||
}
|
||||
boolean varArgCall = MethodCallUtils.isVarArgCall(call);
|
||||
PsiExpression[] args = argumentList.getExpressions();
|
||||
PsiParameter[] parameters = method.getParameterList().getParameters();
|
||||
DfaValue[] argValues = new DfaValue[parameters.length];
|
||||
for (int i = 0; i < parameters.length; i++) {
|
||||
PsiParameter parameter = parameters[i];
|
||||
DfaValue argValue = null;
|
||||
if (i < args.length && (!varArgCall || i < parameters.length - 1)) {
|
||||
argValue = factory.createValue(args[i]);
|
||||
}
|
||||
if (argValue == null) {
|
||||
argValue = DfaUnknownValue.getInstance();
|
||||
}
|
||||
argValues[i] = argValue;
|
||||
}
|
||||
return makeDfaValue(factory, new DfaCallArguments(qualifierValue, argValues, JavaMethodContractUtil.isPure(method)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param other other contract condition
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.codeInsight;
|
||||
|
||||
import com.intellij.psi.PsiModifierListOwner;
|
||||
import com.intellij.psi.PsiNameValuePair;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Wrapper for info about external annotation.
|
||||
*/
|
||||
public class ExternalAnnotation {
|
||||
|
||||
/**
|
||||
* Annotation owner
|
||||
*/
|
||||
@NotNull
|
||||
private final PsiModifierListOwner owner;
|
||||
|
||||
/**
|
||||
* Annotation name
|
||||
*/
|
||||
@NotNull
|
||||
private final String annotationFQName;
|
||||
|
||||
/**
|
||||
* Annotation content
|
||||
*/
|
||||
@Nullable
|
||||
private final PsiNameValuePair[] values;
|
||||
|
||||
public ExternalAnnotation(@NotNull PsiModifierListOwner owner,
|
||||
@NotNull String annotationFQName,
|
||||
@Nullable PsiNameValuePair[] values) {
|
||||
this.owner = owner;
|
||||
this.annotationFQName = annotationFQName;
|
||||
this.values = values;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public PsiModifierListOwner getOwner() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getAnnotationFQName() {
|
||||
return annotationFQName;
|
||||
}
|
||||
|
||||
public PsiNameValuePair[] getValues() {
|
||||
return values;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ExternalAnnotation{" +
|
||||
"owner=" + owner +
|
||||
", annotationFQName='" + annotationFQName + '\'' +
|
||||
", values=" + Arrays.toString(values) +
|
||||
'}';
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
ExternalAnnotation that = (ExternalAnnotation)o;
|
||||
return Objects.equals(owner, that.owner) &&
|
||||
Objects.equals(annotationFQName, that.annotationFQName) &&
|
||||
Arrays.equals(values, that.values);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = Objects.hash(owner, annotationFQName);
|
||||
result = 31 * result + Arrays.hashCode(values);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -46,17 +46,18 @@ import com.intellij.openapi.vfs.*;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
|
||||
import com.intellij.psi.codeStyle.JavaCodeStyleSettings;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.xml.XmlDocument;
|
||||
import com.intellij.psi.xml.XmlFile;
|
||||
import com.intellij.psi.xml.XmlTag;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import com.intellij.util.Processor;
|
||||
import com.intellij.util.SmartList;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.messages.MessageBus;
|
||||
import com.intellij.util.ui.OptionsMessageDialog;
|
||||
import gnu.trove.THashSet;
|
||||
import one.util.streamex.StreamEx;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -65,10 +66,10 @@ import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author anna
|
||||
@@ -117,7 +118,6 @@ public class ExternalAnnotationsManagerImpl extends ReadableExternalAnnotationsM
|
||||
notifyAfterAnnotationChanging(listOwner, annotationFQName, false);
|
||||
return;
|
||||
}
|
||||
final String packageName = ((PsiJavaFile)containingFile).getPackageName();
|
||||
final VirtualFile containingVirtualFile = containingFile.getVirtualFile();
|
||||
LOG.assertTrue(containingVirtualFile != null);
|
||||
final List<OrderEntry> entries = ProjectRootManager.getInstance(project).getFileIndex().getOrderEntriesForFile(containingVirtualFile);
|
||||
@@ -125,13 +125,14 @@ public class ExternalAnnotationsManagerImpl extends ReadableExternalAnnotationsM
|
||||
notifyAfterAnnotationChanging(listOwner, annotationFQName, false);
|
||||
return;
|
||||
}
|
||||
ExternalAnnotation annotation = new ExternalAnnotation(listOwner, annotationFQName, value);
|
||||
for (final OrderEntry entry : entries) {
|
||||
if (entry instanceof ModuleOrderEntry) continue;
|
||||
VirtualFile[] roots = AnnotationOrderRootType.getFiles(entry);
|
||||
roots = filterByReadOnliness(roots);
|
||||
|
||||
if (roots.length > 0) {
|
||||
chooseRootAndAnnotateExternally(listOwner, annotationFQName, fromFile, project, packageName, roots, value);
|
||||
chooseRootAndAnnotateExternally(roots, annotation);
|
||||
}
|
||||
else {
|
||||
if (application.isUnitTestMode() || application.isHeadlessEnvironment()) {
|
||||
@@ -140,7 +141,7 @@ public class ExternalAnnotationsManagerImpl extends ReadableExternalAnnotationsM
|
||||
}
|
||||
DumbService.getInstance(project).setAlternativeResolveEnabled(true);
|
||||
try {
|
||||
if (!setupRootAndAnnotateExternally(entry, project, listOwner, annotationFQName, fromFile, packageName, value)) {
|
||||
if (!setupRootAndAnnotateExternally(entry, project, annotation)) {
|
||||
throw new CanceledConfigurationException();
|
||||
}
|
||||
}
|
||||
@@ -152,6 +153,244 @@ public class ExternalAnnotationsManagerImpl extends ReadableExternalAnnotationsM
|
||||
}
|
||||
}
|
||||
|
||||
private void annotateExternally(@NotNull VirtualFile root, @NotNull ExternalAnnotation annotation) {
|
||||
annotateExternally(root, Collections.singletonList(annotation));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to add external annotations into given root if possible.
|
||||
* Notifies about each addition result separately.
|
||||
*/
|
||||
public void annotateExternally(@NotNull VirtualFile root, @NotNull List<ExternalAnnotation> annotations) {
|
||||
Project project = myPsiManager.getProject();
|
||||
|
||||
Map<Optional<XmlFile>, List<ExternalAnnotation>> annotationsByFiles = annotations.stream()
|
||||
.collect(Collectors.groupingBy(annotation -> Optional.ofNullable(getFileForAnnotations(root, annotation.getOwner(), project))));
|
||||
|
||||
WriteCommandAction.writeCommandAction(project).run(() -> {
|
||||
try {
|
||||
for (Map.Entry<Optional<XmlFile>, List<ExternalAnnotation>> entry : annotationsByFiles.entrySet()) {
|
||||
XmlFile annotationsFile = entry.getKey().orElse(null);
|
||||
List<ExternalAnnotation> fileAnnotations = entry.getValue();
|
||||
annotateExternally(annotationsFile, fileAnnotations);
|
||||
}
|
||||
|
||||
UndoManager.getInstance(project).undoableActionPerformed(new BasicUndoableAction() {
|
||||
@Override
|
||||
public void undo() {
|
||||
dropCache();
|
||||
notifyChangedExternally();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void redo() {
|
||||
dropCache();
|
||||
notifyChangedExternally();
|
||||
}
|
||||
});
|
||||
} finally {
|
||||
dropCache();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void annotateExternally(@Nullable XmlFile annotationsFile, @NotNull List<ExternalAnnotation> annotations) {
|
||||
XmlTag rootTag = extractRootTag(annotationsFile);
|
||||
|
||||
TreeMap<String, List<ExternalAnnotation>> ownerToAnnotations = StreamEx.of(annotations)
|
||||
.mapToEntry(annotation -> StringUtil.escapeXml(getExternalName(annotation.getOwner())), Function.identity())
|
||||
.distinct()
|
||||
.grouping(() -> new TreeMap<>(Comparator.nullsFirst(Comparator.naturalOrder())));
|
||||
|
||||
if (rootTag == null) {
|
||||
ownerToAnnotations.values().stream().flatMap(List::stream).forEach(annotation ->
|
||||
notifyAfterAnnotationChanging(annotation.getOwner(), annotation.getAnnotationFQName(), false));
|
||||
return;
|
||||
}
|
||||
|
||||
List<ExternalAnnotation> savedAnnotations = new ArrayList<>();
|
||||
XmlTag startTag = null;
|
||||
|
||||
for (Map.Entry<String, List<ExternalAnnotation>> entry : ownerToAnnotations.entrySet()) {
|
||||
@NonNls String ownerName = entry.getKey();
|
||||
List<ExternalAnnotation> annotationList = entry.getValue();
|
||||
for (ExternalAnnotation annotation : annotationList) {
|
||||
|
||||
if (ownerName == null) {
|
||||
notifyAfterAnnotationChanging(annotation.getOwner(), annotation.getAnnotationFQName(), false);
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
startTag = addAnnotation(rootTag, ownerName, annotation, startTag);
|
||||
savedAnnotations.add(annotation);
|
||||
}
|
||||
catch (IncorrectOperationException e) {
|
||||
LOG.error(e);
|
||||
notifyAfterAnnotationChanging(annotation.getOwner(), annotation.getAnnotationFQName(), false);
|
||||
}
|
||||
finally {
|
||||
dropCache();
|
||||
markForUndo(annotation.getOwner().getContainingFile());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
commitChanges(annotationsFile);
|
||||
savedAnnotations.forEach(annotation ->
|
||||
notifyAfterAnnotationChanging(annotation.getOwner(), annotation.getAnnotationFQName(), true));
|
||||
}
|
||||
|
||||
@Contract("null -> null")
|
||||
private static XmlTag extractRootTag(XmlFile annotationsFile) {
|
||||
if (annotationsFile == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
XmlDocument document = annotationsFile.getDocument();
|
||||
if (document == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return document.getRootTag();
|
||||
}
|
||||
|
||||
private static void markForUndo(@Nullable PsiFile containingFile) {
|
||||
if (containingFile == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
VirtualFile virtualFile = containingFile.getVirtualFile();
|
||||
if (virtualFile != null && virtualFile.isInLocalFileSystem()) {
|
||||
UndoUtil.markPsiFileForUndo(containingFile);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds annotation sub tag after startTag.
|
||||
* If startTag is {@code null} searches for all sub tags of rootTag and starts from the first.
|
||||
*
|
||||
* @param rootTag root tag to insert subtag into
|
||||
* @param ownerName annotations owner name
|
||||
* @param annotation external annotation
|
||||
* @param startTag start tag
|
||||
* @return added sub tag
|
||||
*/
|
||||
@NotNull
|
||||
private XmlTag addAnnotation(@NotNull XmlTag rootTag, @NotNull String ownerName,
|
||||
@NotNull ExternalAnnotation annotation, @Nullable XmlTag startTag) {
|
||||
if (startTag == null) {
|
||||
startTag = PsiTreeUtil.findChildOfType(rootTag, XmlTag.class);
|
||||
}
|
||||
|
||||
XmlTag prevItem = null;
|
||||
XmlTag curItem = startTag;
|
||||
|
||||
while (curItem != null) {
|
||||
XmlTag addedItem = addAnnotation(rootTag, ownerName, annotation, curItem, prevItem);
|
||||
if (addedItem != null) {
|
||||
return addedItem;
|
||||
}
|
||||
|
||||
prevItem = curItem;
|
||||
curItem = PsiTreeUtil.getNextSiblingOfType(curItem, XmlTag.class);
|
||||
}
|
||||
|
||||
return addItemTag(rootTag, prevItem, ownerName, annotation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds annotation sub tag into curItem or between prevItem and curItem.
|
||||
* Adds into curItem if curItem contains external annotations for owner.
|
||||
* Adds between curItem and prevItem if owner's external name < cur item owner external name.
|
||||
* Otherwise does nothing, returns null.
|
||||
*
|
||||
* @param rootTag root tag to insert sub tag into
|
||||
* @param ownerName annotation owner
|
||||
* @param annotation external annotation
|
||||
* @param curItem current item with annotations
|
||||
* @param prevItem previous item with annotations
|
||||
* @return added tag
|
||||
*/
|
||||
@Nullable
|
||||
private XmlTag addAnnotation(@NotNull XmlTag rootTag, @NotNull String ownerName, @NotNull ExternalAnnotation annotation,
|
||||
@NotNull XmlTag curItem, @Nullable XmlTag prevItem) {
|
||||
|
||||
@NonNls String curItemName = curItem.getAttributeValue("name");
|
||||
if (curItemName == null) {
|
||||
curItem.delete();
|
||||
return null;
|
||||
}
|
||||
|
||||
int compare = ownerName.compareTo(curItemName);
|
||||
|
||||
if (compare == 0) {
|
||||
//already have external annotations for owner
|
||||
return appendItemAnnotation(curItem, annotation);
|
||||
}
|
||||
|
||||
if (compare < 0) {
|
||||
return addItemTag(rootTag, prevItem, ownerName, annotation);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private XmlTag addItemTag(@NotNull XmlTag rootTag,
|
||||
@Nullable XmlTag anchor,
|
||||
@NotNull String ownerName,
|
||||
@NotNull ExternalAnnotation annotation) {
|
||||
XmlElementFactory elementFactory = XmlElementFactory.getInstance(myPsiManager.getProject());
|
||||
XmlTag newItemTag = elementFactory.createTagFromText(createItemTag(ownerName, annotation));
|
||||
|
||||
PsiElement addedElement = rootTag.addAfter(newItemTag, anchor);
|
||||
if (!(addedElement instanceof XmlTag)) {
|
||||
throw new IncorrectOperationException("Failed to add annotation " + annotation + " after " + anchor);
|
||||
}
|
||||
|
||||
return (XmlTag)addedElement;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends annotation sub tag into itemTag. It can happen only if item tag belongs to annotation owner.
|
||||
*
|
||||
* @param itemTag item tag with annotations
|
||||
* @param annotation external annotation
|
||||
*/
|
||||
private XmlTag appendItemAnnotation(@NotNull XmlTag itemTag, @NotNull ExternalAnnotation annotation) {
|
||||
@NonNls String annotationFQName = annotation.getAnnotationFQName();
|
||||
PsiNameValuePair[] values = annotation.getValues();
|
||||
|
||||
XmlElementFactory elementFactory = XmlElementFactory.getInstance(myPsiManager.getProject());
|
||||
|
||||
XmlTag anchor = null;
|
||||
for (XmlTag itemAnnotation : itemTag.getSubTags()) {
|
||||
String curAnnotationName = itemAnnotation.getAttributeValue("name");
|
||||
if (curAnnotationName == null) {
|
||||
itemAnnotation.delete();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (annotationFQName.equals(curAnnotationName)) {
|
||||
// found tag for same annotation, replacing
|
||||
itemAnnotation.delete();
|
||||
break;
|
||||
}
|
||||
|
||||
anchor = itemAnnotation;
|
||||
}
|
||||
|
||||
XmlTag newAnnotationTag = elementFactory.createTagFromText(createAnnotationTag(annotationFQName, values));
|
||||
|
||||
PsiElement addedElement = itemTag.addAfter(newAnnotationTag, anchor);
|
||||
if (!(addedElement instanceof XmlTag)) {
|
||||
throw new IncorrectOperationException("Failed to add annotation " + annotation + " after " + anchor);
|
||||
}
|
||||
|
||||
return itemTag;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private List<XmlFile> findExternalAnnotationsXmlFiles(@NotNull PsiModifierListOwner listOwner) {
|
||||
List<PsiFile> psiFiles = findExternalAnnotationsFiles(listOwner);
|
||||
@@ -169,39 +408,17 @@ public class ExternalAnnotationsManagerImpl extends ReadableExternalAnnotationsM
|
||||
|
||||
private boolean setupRootAndAnnotateExternally(@NotNull final OrderEntry entry,
|
||||
@NotNull final Project project,
|
||||
@NotNull final PsiModifierListOwner listOwner,
|
||||
@NotNull final String annotationFQName,
|
||||
@NotNull final PsiFile fromFile,
|
||||
@NotNull final String packageName,
|
||||
@Nullable final PsiNameValuePair[] value) {
|
||||
@NotNull final ExternalAnnotation annotation) {
|
||||
final FileChooserDescriptor descriptor = FileChooserDescriptorFactory.createSingleFolderDescriptor();
|
||||
descriptor.setTitle(ProjectBundle.message("external.annotations.root.chooser.title", entry.getPresentableName()));
|
||||
descriptor.setDescription(ProjectBundle.message("external.annotations.root.chooser.description"));
|
||||
final VirtualFile newRoot = FileChooser.chooseFile(descriptor, project, null);
|
||||
if (newRoot == null) {
|
||||
notifyAfterAnnotationChanging(listOwner, annotationFQName, false);
|
||||
notifyAfterAnnotationChanging(annotation.getOwner(), annotation.getAnnotationFQName(), false);
|
||||
return false;
|
||||
}
|
||||
String externalName = getExternalName(listOwner);
|
||||
WriteCommandAction.writeCommandAction(project).run(() -> {
|
||||
appendChosenAnnotationsRoot(entry, newRoot);
|
||||
XmlFile xmlFileInRoot = findXmlFileInRoot(findExternalAnnotationsXmlFiles(listOwner), newRoot);
|
||||
if (xmlFileInRoot != null) { //file already exists under appeared content root
|
||||
if (!FileModificationService.getInstance().preparePsiElementForWrite(xmlFileInRoot)) {
|
||||
notifyAfterAnnotationChanging(listOwner, annotationFQName, false);
|
||||
return;
|
||||
}
|
||||
annotateExternally(listOwner, annotationFQName, xmlFileInRoot, fromFile, value, externalName);
|
||||
}
|
||||
else {
|
||||
final XmlFile annotationsXml = createAnnotationsXml(newRoot, packageName);
|
||||
if (annotationsXml != null) {
|
||||
List<PsiFile> createdFiles = new SmartList<>(annotationsXml);
|
||||
cacheExternalAnnotations(packageName, fromFile, createdFiles);
|
||||
}
|
||||
annotateExternally(listOwner, annotationFQName, annotationsXml, fromFile, value, externalName);
|
||||
}
|
||||
});
|
||||
WriteCommandAction.writeCommandAction(project).run(() -> appendChosenAnnotationsRoot(entry, newRoot));
|
||||
annotateExternally(newRoot, annotation);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -220,23 +437,17 @@ public class ExternalAnnotationsManagerImpl extends ReadableExternalAnnotationsM
|
||||
return null;
|
||||
}
|
||||
|
||||
private void chooseRootAndAnnotateExternally(@NotNull final PsiModifierListOwner listOwner,
|
||||
@NotNull final String annotationFQName,
|
||||
@NotNull final PsiFile fromFile,
|
||||
@NotNull final Project project,
|
||||
@NotNull final String packageName,
|
||||
@NotNull VirtualFile[] roots,
|
||||
@Nullable final PsiNameValuePair[] value) {
|
||||
private void chooseRootAndAnnotateExternally(@NotNull VirtualFile[] roots, @NotNull ExternalAnnotation annotation) {
|
||||
if (roots.length > 1) {
|
||||
JBPopupFactory.getInstance().createListPopup(new BaseListPopupStep<VirtualFile>("Annotation Roots", roots) {
|
||||
@Override
|
||||
public void canceled() {
|
||||
notifyAfterAnnotationChanging(listOwner, annotationFQName, false);
|
||||
notifyAfterAnnotationChanging(annotation.getOwner(), annotation.getAnnotationFQName(), false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PopupStep onChosen(@NotNull final VirtualFile file, final boolean finalChoice) {
|
||||
annotateExternally(file, listOwner, project, packageName, annotationFQName, fromFile, value);
|
||||
annotateExternally(file, annotation);
|
||||
return FINAL_CHOICE;
|
||||
}
|
||||
|
||||
@@ -253,7 +464,7 @@ public class ExternalAnnotationsManagerImpl extends ReadableExternalAnnotationsM
|
||||
}).showInBestPositionFor(DataManager.getInstance().getDataContext());
|
||||
}
|
||||
else {
|
||||
annotateExternally(roots[0], listOwner, project, packageName, annotationFQName, fromFile, value);
|
||||
annotateExternally(roots[0], annotation);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -263,55 +474,6 @@ public class ExternalAnnotationsManagerImpl extends ReadableExternalAnnotationsM
|
||||
return VfsUtilCore.toVirtualFileArray(result);
|
||||
}
|
||||
|
||||
private void annotateExternally(@NotNull final VirtualFile root,
|
||||
@NotNull final PsiModifierListOwner listOwner,
|
||||
@NotNull final Project project,
|
||||
@NotNull final String packageName,
|
||||
@NotNull final String annotationFQName,
|
||||
@NotNull final PsiFile fromFile,
|
||||
@Nullable final PsiNameValuePair[] value) {
|
||||
List<XmlFile> xmlFiles = findExternalAnnotationsXmlFiles(listOwner);
|
||||
|
||||
final XmlFile existingXml = findXmlFileInRoot(xmlFiles, root);
|
||||
if (existingXml != null && !FileModificationService.getInstance().preparePsiElementForWrite(existingXml)) {
|
||||
notifyAfterAnnotationChanging(listOwner, annotationFQName, false);
|
||||
return;
|
||||
}
|
||||
|
||||
Set<PsiFile> annotationFiles = xmlFiles == null ? new THashSet<>() : new THashSet<>(xmlFiles);
|
||||
String externalName = getExternalName(listOwner);
|
||||
WriteCommandAction.writeCommandAction(project).run(() -> {
|
||||
if (existingXml != null) {
|
||||
annotateExternally(listOwner, annotationFQName, existingXml, fromFile, value, externalName);
|
||||
}
|
||||
else {
|
||||
XmlFile newXml = createAnnotationsXml(root, packageName);
|
||||
if (newXml == null) {
|
||||
notifyAfterAnnotationChanging(listOwner, annotationFQName, false);
|
||||
}
|
||||
else {
|
||||
annotationFiles.add(newXml);
|
||||
cacheExternalAnnotations(packageName, fromFile, new SmartList<>(annotationFiles));
|
||||
annotateExternally(listOwner, annotationFQName, newXml, fromFile, value, externalName);
|
||||
}
|
||||
}
|
||||
|
||||
UndoManager.getInstance(project).undoableActionPerformed(new BasicUndoableAction() {
|
||||
@Override
|
||||
public void undo() {
|
||||
dropCache();
|
||||
notifyChangedExternally();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void redo() {
|
||||
dropCache();
|
||||
notifyChangedExternally();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deannotate(@NotNull final PsiModifierListOwner listOwner, @NotNull final String annotationFQN) {
|
||||
ApplicationManager.getApplication().assertIsDispatchThread();
|
||||
@@ -544,71 +706,6 @@ public class ExternalAnnotationsManagerImpl extends ReadableExternalAnnotationsM
|
||||
dropCache();
|
||||
}
|
||||
|
||||
private void annotateExternally(@NotNull final PsiModifierListOwner listOwner,
|
||||
@NotNull final String annotationFQName,
|
||||
@Nullable final XmlFile xmlFile,
|
||||
@NotNull final PsiFile codeUsageFile,
|
||||
@Nullable final PsiNameValuePair[] values,
|
||||
@Nullable final String externalName) {
|
||||
if (xmlFile == null) {
|
||||
notifyAfterAnnotationChanging(listOwner, annotationFQName, false);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
final XmlDocument document = xmlFile.getDocument();
|
||||
if (document != null) {
|
||||
final XmlTag rootTag = document.getRootTag();
|
||||
if (externalName == null) {
|
||||
LOG.info("member without external name: " + listOwner);
|
||||
}
|
||||
if (rootTag != null && externalName != null) {
|
||||
XmlTag anchor = null;
|
||||
for (XmlTag item : rootTag.getSubTags()) {
|
||||
int compare = Comparing.compare(externalName, StringUtil.unescapeXml(item.getAttributeValue("name")));
|
||||
if (compare == 0) {
|
||||
anchor = null;
|
||||
for (XmlTag annotation : item.getSubTags()) {
|
||||
compare = Comparing.compare(annotationFQName, annotation.getAttributeValue("name"));
|
||||
if (compare == 0) {
|
||||
annotation.delete();
|
||||
break;
|
||||
}
|
||||
anchor = annotation;
|
||||
}
|
||||
XmlTag newTag = XmlElementFactory.getInstance(myPsiManager.getProject()).createTagFromText(
|
||||
createAnnotationTag(annotationFQName, values));
|
||||
item.addAfter(newTag, anchor);
|
||||
commitChanges(xmlFile);
|
||||
notifyAfterAnnotationChanging(listOwner, annotationFQName, true);
|
||||
return;
|
||||
}
|
||||
if (compare < 0) break;
|
||||
anchor = item;
|
||||
}
|
||||
@NonNls String text =
|
||||
"<item name=\'" + StringUtil.escapeXml(externalName) + "\'>\n";
|
||||
text += createAnnotationTag(annotationFQName, values);
|
||||
text += "</item>";
|
||||
rootTag.addAfter(XmlElementFactory.getInstance(myPsiManager.getProject()).createTagFromText(text), anchor);
|
||||
commitChanges(xmlFile);
|
||||
notifyAfterAnnotationChanging(listOwner, annotationFQName, true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
notifyAfterAnnotationChanging(listOwner, annotationFQName, false);
|
||||
}
|
||||
catch (IncorrectOperationException e) {
|
||||
LOG.error(e);
|
||||
notifyAfterAnnotationChanging(listOwner, annotationFQName, false);
|
||||
}
|
||||
finally {
|
||||
dropCache();
|
||||
if (codeUsageFile.getVirtualFile().isInLocalFileSystem()) {
|
||||
UndoUtil.markPsiFileForUndo(codeUsageFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void sortItems(@NotNull XmlFile xmlFile) {
|
||||
XmlDocument document = xmlFile.getDocument();
|
||||
if (document == null) {
|
||||
@@ -653,6 +750,13 @@ public class ExternalAnnotationsManagerImpl extends ReadableExternalAnnotationsM
|
||||
FileDocumentManager.getInstance().saveDocument(doc);
|
||||
}
|
||||
|
||||
@NonNls
|
||||
@NotNull
|
||||
private static String createItemTag(@NotNull String ownerName, @NotNull ExternalAnnotation annotation) {
|
||||
String annotationTag = createAnnotationTag(annotation.getAnnotationFQName(), annotation.getValues());
|
||||
return String.format("<item name=\'%s\'>%s</item>", ownerName, annotationTag);
|
||||
}
|
||||
|
||||
@NonNls
|
||||
@NotNull
|
||||
@VisibleForTesting
|
||||
@@ -689,6 +793,7 @@ public class ExternalAnnotationsManagerImpl extends ReadableExternalAnnotationsM
|
||||
}
|
||||
catch (IOException e) {
|
||||
LOG.error(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
root = subdir;
|
||||
@@ -711,6 +816,32 @@ public class ExternalAnnotationsManagerImpl extends ReadableExternalAnnotationsM
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private XmlFile getFileForAnnotations(@NotNull VirtualFile root, @NotNull PsiModifierListOwner owner, Project project) {
|
||||
return WriteCommandAction.writeCommandAction(project).compute(() -> {
|
||||
final PsiFile containingFile = owner.getOriginalElement().getContainingFile();
|
||||
if (!(containingFile instanceof PsiJavaFile)) {
|
||||
return null;
|
||||
}
|
||||
String packageName = ((PsiJavaFile)containingFile).getPackageName();
|
||||
|
||||
List<XmlFile> annotationsFiles = findExternalAnnotationsXmlFiles(owner);
|
||||
|
||||
XmlFile fileInRoot = findXmlFileInRoot(annotationsFiles, root);
|
||||
if (fileInRoot != null && FileModificationService.getInstance().preparePsiElementForWrite(fileInRoot)) {
|
||||
return fileInRoot;
|
||||
}
|
||||
|
||||
XmlFile newAnnotationsFile = createAnnotationsXml(root, packageName);
|
||||
if (newAnnotationsFile == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
registerExternalAnnotations(containingFile, newAnnotationsFile);
|
||||
return newAnnotationsFile;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasAnnotationRootsForFile(@NotNull VirtualFile file) {
|
||||
if (hasAnyAnnotationsRoots()) {
|
||||
|
||||
+3
-2
@@ -28,6 +28,7 @@ import com.intellij.psi.*;
|
||||
import com.intellij.refactoring.util.RefactoringUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import com.siyeh.ig.psiutils.ControlFlowUtils;
|
||||
import com.siyeh.ig.psiutils.ExpressionUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
@@ -43,7 +44,7 @@ public class SurroundWithTryCatchFix implements IntentionAction {
|
||||
element instanceof PsiResourceVariable ||
|
||||
(element instanceof PsiExpression &&
|
||||
!(element instanceof PsiMethodReferenceExpression) &&
|
||||
ControlFlowUtils.canExtractStatement((PsiExpression)element, false))) {
|
||||
ControlFlowUtils.canExtractStatement(ExpressionUtils.getTopLevelExpression((PsiExpression)element)))) {
|
||||
myElement = element;
|
||||
}
|
||||
}
|
||||
@@ -78,7 +79,7 @@ public class SurroundWithTryCatchFix implements IntentionAction {
|
||||
editor.getCaretModel().moveToLogicalPosition(new LogicalPosition(0, 0));
|
||||
|
||||
if (myElement instanceof PsiExpression) {
|
||||
myElement = RefactoringUtil.ensureCodeBlock((PsiExpression)myElement);
|
||||
myElement = RefactoringUtil.ensureCodeBlock(ExpressionUtils.getTopLevelExpression((PsiExpression)myElement));
|
||||
}
|
||||
myElement = RefactoringUtil.getParentStatement(myElement, false);
|
||||
if (myElement == null) return;
|
||||
|
||||
+1
-1
@@ -43,7 +43,7 @@ public class ReadWriteStringCanBeUsedInspection extends AbstractBaseJavaLocalIns
|
||||
ProblemHighlightType highlight;
|
||||
String message = "Can be replaced with 'Files.writeString()'";
|
||||
if (level.isAtLeast(LanguageLevel.JDK_12) || isNonUtf8Charset(bytesExpression.getArgumentList().getExpressions()[0])) {
|
||||
highlight = ProblemHighlightType.WARNING;
|
||||
highlight = ProblemHighlightType.GENERIC_ERROR_OR_WARNING;
|
||||
} else {
|
||||
highlight = ProblemHighlightType.INFORMATION;
|
||||
}
|
||||
|
||||
@@ -62,8 +62,8 @@ public class ReplaceNullCheckInspection extends AbstractBaseJavaLocalInspectionT
|
||||
PsiStatement nextToDelete = context.myNextToDelete;
|
||||
int maybeImplicitElseLength = nextToDelete != null ? nextToDelete.getTextLength() : 0;
|
||||
boolean isInfoLevel = noWarningReplacementBigger && ifStatement.getTextLength() + maybeImplicitElseLength - context.getLenAfterReplace() < MINIMAL_WARN_DELTA_SIZE;
|
||||
if (!isOnTheFly && isInfoLevel) return;
|
||||
ProblemHighlightType highlight = getHighlight(context, isInfoLevel);
|
||||
if (!isOnTheFly && highlight == ProblemHighlightType.INFORMATION) return;
|
||||
holder.registerProblem(ifStatement.getFirstChild(), InspectionsBundle.message("inspection.require.non.null.message", method), highlight,
|
||||
new ReplaceWithRequireNonNullFix(method, false));
|
||||
}
|
||||
|
||||
@@ -378,12 +378,17 @@ public abstract class BaseExternalAnnotationsManager extends ExternalAnnotations
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
void cacheExternalAnnotations(@SuppressWarnings("UnusedParameters") @NotNull String packageName,
|
||||
@NotNull PsiFile fromFile,
|
||||
@NotNull List<PsiFile> annotationFiles) {
|
||||
protected void registerExternalAnnotations(@NotNull PsiFile fromFile, @NotNull PsiFile annotationsFile) {
|
||||
VirtualFile virtualFile = fromFile.getVirtualFile();
|
||||
if (virtualFile != null) {
|
||||
myExternalAnnotationsCache.put(virtualFile, annotationFiles);
|
||||
myExternalAnnotationsCache.compute(virtualFile, (k, v) -> {
|
||||
if (v == null || v == NULL_LIST) {
|
||||
return new SmartList<>(annotationsFile);
|
||||
}
|
||||
|
||||
v.add(annotationsFile);
|
||||
return v;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -59,7 +59,6 @@ public class StubBuildingVisitor<T> extends ClassVisitor {
|
||||
private final Function<String, String> myMapping;
|
||||
private final boolean myAnonymousInner;
|
||||
private final boolean myLocalClassInner;
|
||||
private boolean myAsm6Mode;
|
||||
private String myInternalName;
|
||||
private PsiClassStub<?> myResult;
|
||||
private PsiModifierListStub myModList;
|
||||
@@ -79,8 +78,6 @@ public class StubBuildingVisitor<T> extends ClassVisitor {
|
||||
myMapping = createMapping(classSource);
|
||||
myAnonymousInner = anonymousInner;
|
||||
myLocalClassInner = localClassInner;
|
||||
//noinspection ConstantConditions
|
||||
myAsm6Mode = ASM_API <= Opcodes.ASM6 || classSource == null && innersStrategy.getClass().getName().startsWith("org.jetbrains.kotlin.");
|
||||
}
|
||||
|
||||
public PsiClassStub<?> getResult() {
|
||||
@@ -277,7 +274,6 @@ public class StubBuildingVisitor<T> extends ClassVisitor {
|
||||
if (innerClass != null) {
|
||||
StubBuildingVisitor<T> visitor =
|
||||
new StubBuildingVisitor<>(innerClass, myInnersStrategy, myResult, access, innerName, isAnonymousInner, isLocalClassInner);
|
||||
visitor.myAsm6Mode = myAsm6Mode;
|
||||
myInnersStrategy.accept(innerClass, visitor);
|
||||
}
|
||||
}
|
||||
@@ -391,8 +387,7 @@ public class StubBuildingVisitor<T> extends ClassVisitor {
|
||||
|
||||
newReferenceList(JavaStubElementTypes.THROWS_LIST, stub, ArrayUtil.toStringArray(info.throwTypes));
|
||||
|
||||
boolean noSynthetics = myAsm6Mode && isConstructor && hasSignature && Type.getArgumentTypes(desc).length == info.argTypes.size();
|
||||
int paramIgnoreCount = noSynthetics ? 0 : isEnumConstructor ? 2 : isInnerClassConstructor ? 1 : 0;
|
||||
int paramIgnoreCount = isEnumConstructor ? 2 : isInnerClassConstructor ? 1 : 0;
|
||||
int localVarIgnoreCount = isEnumConstructor ? 3 : isInnerClassConstructor ? 2 : !isStatic ? 1 : 0;
|
||||
return new MethodAnnotationCollectingVisitor(stub, modList, paramStubs, paramIgnoreCount, localVarIgnoreCount, myMapping);
|
||||
}
|
||||
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
// "Surround with try/catch" "true"
|
||||
class C {
|
||||
native boolean foo() throws Exception;
|
||||
|
||||
void test() {
|
||||
try {
|
||||
if(foo() && foo())
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// "Surround with try/catch" "true"
|
||||
class C {
|
||||
native boolean foo() throws Exception;
|
||||
|
||||
void test() {
|
||||
if(foo() && f<caret>oo())
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// "Collapse if statement " "true"
|
||||
// "Collapse 'if' statement" "true"
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// "Extract common part with variables from if " "true"
|
||||
// "Extract common part with variables from 'if'" "true"
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// "Extract common part with variables from if " "true"
|
||||
// "Extract common part with variables from 'if'" "true"
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// "Extract common part with variables from if (may change semantics)" "INFORMATION"
|
||||
// "Extract common part with variables from 'if' (may change semantics)" "INFORMATION"
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// "Extract common part with variables from if " "true"
|
||||
// "Extract common part with variables from 'if'" "true"
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// "Extract common part with variables from if " "true"
|
||||
// "Extract common part with variables from 'if'" "true"
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// "Collapse if statement " "true"
|
||||
// "Collapse 'if' statement" "true"
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// "Collapse if statement " "true"
|
||||
// "Collapse 'if' statement" "true"
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// "Collapse if statement " "true"
|
||||
// "Collapse 'if' statement" "true"
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// "Collapse if statement " "true"
|
||||
// "Collapse 'if' statement" "true"
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// "Collapse if statement " "true"
|
||||
// "Collapse 'if' statement" "true"
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// "Collapse if statement " "true"
|
||||
// "Collapse 'if' statement" "true"
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// "Collapse if statement " "true"
|
||||
// "Collapse 'if' statement" "true"
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// "Extract common part removing branch " "true"
|
||||
// "Extract common part removing branch" "true"
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// "Extract common part with variables from if " "true"
|
||||
// "Extract common part with variables from 'if'" "true"
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// "Collapse if statement " "true"
|
||||
// "Collapse 'if' statement" "true"
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// "Collapse if statement " "true"
|
||||
// "Collapse 'if' statement" "true"
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// "Collapse if statement " "true"
|
||||
// "Collapse 'if' statement" "true"
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// "Extract common part from if (may change semantics)" "INFORMATION"
|
||||
// "Extract common part from 'if' (may change semantics)" "INFORMATION"
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// "Extract common part from if (may change semantics)" "INFORMATION"
|
||||
// "Extract common part from 'if' (may change semantics)" "INFORMATION"
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// "Extract common part removing branch " "true"
|
||||
// "Extract common part removing branch" "true"
|
||||
|
||||
import java.io.*;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// "Extract common part removing branch " "true"
|
||||
// "Extract common part removing branch" "true"
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// "Extract variables from if " "true"
|
||||
// "Extract variables from 'if'" "true"
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// "Collapse if statement " "true"
|
||||
// "Collapse 'if' statement" "true"
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// "Extract common part with variables from if " "true"
|
||||
// "Extract common part with variables from 'if'" "true"
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// "Extract common part with variables from if " "true"
|
||||
// "Extract common part with variables from 'if'" "true"
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// "Extract common part with variables from if (may change semantics)" "INFORMATION"
|
||||
// "Extract common part with variables from 'if' (may change semantics)" "INFORMATION"
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// "Extract common part with variables from if " "false"
|
||||
// "Extract common part with variables from 'if'" "false"
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// "Extract common part with variables from if " "true"
|
||||
// "Extract common part with variables from 'if'" "true"
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// "Extract common part with variables from if " "true"
|
||||
// "Extract common part with variables from 'if'" "true"
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// "Collapse if statement " "true"
|
||||
// "Collapse 'if' statement" "true"
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// "Collapse if statement " "true"
|
||||
// "Collapse 'if' statement" "true"
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// "Collapse if statement " "true"
|
||||
// "Collapse 'if' statement" "true"
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// "Collapse if statement " "true"
|
||||
// "Collapse 'if' statement" "true"
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// "Collapse if statement " "true"
|
||||
// "Collapse 'if' statement" "true"
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// "Collapse if statement " "true"
|
||||
// "Collapse 'if' statement" "true"
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// "Fix all 'Common parts of if statement branches can be extracted' problems in file" "false"
|
||||
// "Fix all 'Common parts of 'if' statement branches can be extracted' problems in file" "false"
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// "Collapse if statement " "true"
|
||||
// "Collapse 'if' statement" "true"
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// "Extract common part removing branch " "false"
|
||||
// "Extract common part removing branch" "false"
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// "Extract common part removing branch " "false"
|
||||
// "Extract common part removing branch" "false"
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// "Extract common part removing branch " "true"
|
||||
// "Extract common part removing branch" "true"
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// "Extract common part with variables from if " "true"
|
||||
// "Extract common part with variables from 'if'" "true"
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// "Collapse if statement " "false"
|
||||
// "Collapse 'if' statement" "false"
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// "Collapse if statement " "false"
|
||||
// "Collapse 'if' statement" "false"
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// "Collapse if statement " "true"
|
||||
// "Collapse 'if' statement" "true"
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// "Collapse if statement " "false"
|
||||
// "Collapse 'if' statement" "false"
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// "Collapse if statement " "true"
|
||||
// "Collapse 'if' statement" "true"
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// "Collapse if statement " "true"
|
||||
// "Collapse 'if' statement" "true"
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// "Extract common part from if (may change semantics)" "INFORMATION"
|
||||
// "Extract common part from 'if' (may change semantics)" "INFORMATION"
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// "Extract common part from if (may change semantics)" "INFORMATION"
|
||||
// "Extract common part from 'if' (may change semantics)" "INFORMATION"
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// "Extract common part from if " "false"
|
||||
// "Extract common part from 'if'" "false"
|
||||
|
||||
public class Main {
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// "Extract common part from if " "false"
|
||||
// "Extract common part from 'if'" "false"
|
||||
|
||||
public class Main {
|
||||
public static void main(String[] args) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// "Extract common part removing branch " "true"
|
||||
// "Extract common part removing branch" "true"
|
||||
|
||||
import java.io.*;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// "Extract common part removing branch " "true"
|
||||
// "Extract common part removing branch" "true"
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// "Extract variables from if " "true"
|
||||
// "Extract variables from 'if'" "true"
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// "Common parts of if statement can be extracted" "false"
|
||||
// "Common parts of 'if' statement can be extracted" "false"
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// "Replace with 'Files.writeString()'" "WARNING"
|
||||
// "Replace with 'Files.writeString()'" "GENERIC_ERROR_OR_WARNING"
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// "Replace with 'Files.writeString()'" "WARNING"
|
||||
// "Replace with 'Files.writeString()'" "GENERIC_ERROR_OR_WARNING"
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// "Replace with 'Files.writeString()'" "WARNING"
|
||||
// "Replace with 'Files.writeString()'" "GENERIC_ERROR_OR_WARNING"
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// "Replace with 'Files.writeString()'" "WARNING"
|
||||
// "Replace with 'Files.writeString()'" "GENERIC_ERROR_OR_WARNING"
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
@@ -22,7 +22,6 @@ import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.testFramework.propertyBased.IntentionPolicy;
|
||||
import com.siyeh.ig.psiutils.ExpressionUtils;
|
||||
import com.siyeh.ipp.psiutils.ErrorUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -195,7 +194,6 @@ class JavaParenthesesPolicy extends JavaIntentionPolicy {
|
||||
break;
|
||||
}
|
||||
if (parent instanceof PsiVariable && expression instanceof PsiArrayInitializerExpression) break;
|
||||
if (ErrorUtil.containsDeepError(parent)) break;
|
||||
result.add(expression);
|
||||
element = expression.getParent();
|
||||
}
|
||||
|
||||
+13
-1
@@ -3,11 +3,15 @@ package com.intellij.codeInspection;
|
||||
|
||||
import com.intellij.analysis.JvmAnalysisBundle;
|
||||
import com.intellij.codeInspection.ui.SingleCheckboxOptionsPanel;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.roots.ProjectFileIndex;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiUtilCore;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.uast.UImportStatement;
|
||||
@@ -31,7 +35,7 @@ public abstract class AnnotatedElementInspectionBase extends LocalInspectionTool
|
||||
protected abstract void createProblem(@NotNull PsiReference reference, @NotNull ProblemsHolder holder);
|
||||
|
||||
protected boolean shouldProcessElement(@NotNull PsiModifierListOwner element) {
|
||||
return true;
|
||||
return isLibraryElement(element);
|
||||
}
|
||||
|
||||
|
||||
@@ -133,4 +137,12 @@ public abstract class AnnotatedElementInspectionBase extends LocalInspectionTool
|
||||
// references are not PsiQualifiedReference for annotation attributes
|
||||
return StringUtil.getShortName(reference.getCanonicalText());
|
||||
}
|
||||
|
||||
private static boolean isLibraryElement(@NotNull PsiElement element) {
|
||||
if (ApplicationManager.getApplication().isUnitTestMode()) {
|
||||
return true;
|
||||
}
|
||||
VirtualFile containingVirtualFile = PsiUtilCore.getVirtualFile(element);
|
||||
return containingVirtualFile != null && ProjectFileIndex.getInstance(element.getProject()).isInLibraryClasses(containingVirtualFile);
|
||||
}
|
||||
}
|
||||
-1
@@ -2,7 +2,6 @@
|
||||
package com.intellij.codeInspection;
|
||||
|
||||
import com.intellij.analysis.JvmAnalysisBundle;
|
||||
import com.intellij.psi.PsiModifierListOwner;
|
||||
import com.intellij.psi.PsiReference;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@@ -3,13 +3,7 @@ package com.intellij.codeInspection;
|
||||
|
||||
import com.intellij.analysis.JvmAnalysisBundle;
|
||||
import com.intellij.codeInspection.util.SpecialAnnotationsUtil;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.roots.ProjectFileIndex;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiModifierListOwner;
|
||||
import com.intellij.psi.PsiReference;
|
||||
import com.intellij.psi.util.PsiUtilCore;
|
||||
import com.siyeh.ig.ui.ExternalizableStringSet;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@@ -41,11 +35,6 @@ public class UnstableApiUsageInspection extends AnnotatedElementInspectionBase {
|
||||
holder.registerProblem(reference, message, ProblemHighlightType.GENERIC_ERROR_OR_WARNING);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean shouldProcessElement(@NotNull PsiModifierListOwner element) {
|
||||
return isLibraryElement(element);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public JPanel createOptionsPanel() {
|
||||
@@ -60,12 +49,4 @@ public class UnstableApiUsageInspection extends AnnotatedElementInspectionBase {
|
||||
panel.add(annotationsListControl, BorderLayout.CENTER);
|
||||
return panel;
|
||||
}
|
||||
|
||||
private static boolean isLibraryElement(@NotNull PsiElement element) {
|
||||
if (ApplicationManager.getApplication().isUnitTestMode()) {
|
||||
return true;
|
||||
}
|
||||
VirtualFile containingVirtualFile = PsiUtilCore.getVirtualFile(element);
|
||||
return containingVirtualFile != null && ProjectFileIndex.getInstance(element.getProject()).isInLibraryClasses(containingVirtualFile);
|
||||
}
|
||||
}
|
||||
|
||||
Executable
+6
@@ -0,0 +1,6 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Lists all tags available in the repository.
|
||||
# Usage: ./list-tags.sh
|
||||
|
||||
git ls-remote --tags git://git.jetbrains.org/idea/community.git
|
||||
+4
-2
@@ -43,8 +43,10 @@ public class ExternalSystemRunConfigurationMenu extends DefaultActionGroup imple
|
||||
ProjectSystemId projectSystemId = ExternalSystemDataKeys.EXTERNAL_SYSTEM_ID.getData(e.getDataContext());
|
||||
Executor[] executors = ExecutorRegistry.getInstance().getRegisteredExecutors();
|
||||
for (int i = executors.length; --i >= 0; ) {
|
||||
final ProgramRunner runner = ProgramRunner.getRunner(executors[i].getId(), settings.getConfiguration());
|
||||
AnAction action = new ExecuteExternalSystemRunConfigurationAction(executors[i], runner != null, project, projectSystemId, settings);
|
||||
Executor executor = executors[i];
|
||||
if(!executor.isApplicable(project)) continue;
|
||||
final ProgramRunner runner = ProgramRunner.getRunner(executor.getId(), settings.getConfiguration());
|
||||
AnAction action = new ExecuteExternalSystemRunConfigurationAction(executor, runner != null, project, projectSystemId, settings);
|
||||
addAction(action, Constraints.FIRST);
|
||||
}
|
||||
|
||||
|
||||
@@ -595,6 +595,11 @@ abstract class LineLayout {
|
||||
this.endOffset = endOffset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return startOffset * 31 + endOffset;
|
||||
}
|
||||
|
||||
private void ensureLayout(@NotNull EditorView view, BidiRun run, int line) {
|
||||
if (isReal()) {
|
||||
view.getTextLayoutCache().onChunkAccess(this);
|
||||
|
||||
+12
@@ -17,6 +17,7 @@ package com.intellij.openapi.updateSettings;
|
||||
|
||||
import com.intellij.openapi.components.ServiceManager;
|
||||
import com.intellij.openapi.updateSettings.impl.ChannelStatus;
|
||||
import com.intellij.openapi.util.BuildNumber;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
@@ -34,4 +35,15 @@ public class UpdateStrategyCustomization {
|
||||
public boolean isChannelActive(@NotNull ChannelStatus channel) {
|
||||
return channel != ChannelStatus.MILESTONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the both passed builds correspond to the same major version of the IDE. The platform uses this method when several
|
||||
* new builds are available, to suggest updating to the build from the same major version, i.e. IntelliJ IDEA 2018.2.5 will suggest to
|
||||
* update to 2018.2.6, not to 2018.3.
|
||||
* <br>
|
||||
* Override this method if major versions of your IDE doesn't directly correspond to major version of the IntelliJ platform.
|
||||
*/
|
||||
public boolean haveSameMajorVersion(@NotNull BuildNumber build1, @NotNull BuildNumber build2) {
|
||||
return build1.getBaselineVersion() == build2.getBaselineVersion();
|
||||
}
|
||||
}
|
||||
+6
-6
@@ -1,6 +1,7 @@
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.openapi.updateSettings.impl
|
||||
|
||||
import com.intellij.openapi.updateSettings.UpdateStrategyCustomization
|
||||
import com.intellij.openapi.util.BuildNumber
|
||||
import com.intellij.util.containers.MultiMap
|
||||
import com.intellij.util.graph.GraphAlgorithms
|
||||
@@ -14,8 +15,6 @@ class UpdateStrategy(private val currentBuild: BuildNumber, private val updates:
|
||||
LOADED, CONNECTION_ERROR, NOTHING_LOADED
|
||||
}
|
||||
|
||||
private val lineage = currentBuild.baselineVersion
|
||||
|
||||
fun checkForUpdates(): CheckForUpdateResult {
|
||||
val product = updates[currentBuild.productCode]
|
||||
if (product == null || product.channels.isEmpty()) {
|
||||
@@ -43,10 +42,11 @@ class UpdateStrategy(private val currentBuild: BuildNumber, private val updates:
|
||||
candidate.number.asStringWithoutProductCode() !in ignoredBuilds &&
|
||||
candidate.target?.inRange(currentBuild) ?: true
|
||||
|
||||
private fun compareBuilds(n1: BuildNumber, n2: BuildNumber) =
|
||||
if (n1.baselineVersion == lineage && n2.baselineVersion != lineage) 1
|
||||
else if (n2.baselineVersion == lineage && n1.baselineVersion != lineage) -1
|
||||
else n1.compareTo(n2)
|
||||
private fun compareBuilds(n1: BuildNumber, n2: BuildNumber): Int {
|
||||
val customization = UpdateStrategyCustomization.getInstance()
|
||||
val preferSameMajorVersion = customization.haveSameMajorVersion(currentBuild, n1).compareTo(customization.haveSameMajorVersion(currentBuild, n2))
|
||||
return if (preferSameMajorVersion != 0) preferSameMajorVersion else n1.compareTo(n2)
|
||||
}
|
||||
|
||||
private fun patches(newBuild: BuildInfo, product: Product, from: BuildNumber): UpdateChain? {
|
||||
val single = newBuild.patches.find { it.isAvailable && it.fromBuild.compareTo(from) == 0 }
|
||||
|
||||
@@ -216,8 +216,8 @@ public class LightweightHint extends UserDataHolderBase implements Hint {
|
||||
}
|
||||
|
||||
myPopup = JBPopupFactory.getInstance().createComponentPopupBuilder(actualComponent, myFocusRequestor)
|
||||
.setRequestFocus(hintHint.isRequestFocus())
|
||||
.setFocusable(hintHint.isRequestFocus())
|
||||
.setRequestFocus(myFocusRequestor != null || hintHint.isRequestFocus())
|
||||
.setFocusable(myFocusRequestor != null || hintHint.isRequestFocus())
|
||||
.setResizable(myResizable)
|
||||
.setMovable(myTitle != null)
|
||||
.setTitle(myTitle)
|
||||
|
||||
@@ -60,7 +60,7 @@ public class SheetController implements Disposable {
|
||||
|
||||
private static final int GAP_BETWEEN_BUTTONS = 5;
|
||||
|
||||
private static final String SPACE_OR_LINE_SEPARATOR_PATTERN = "[\\s" + System.getProperty("line.separator") + "]+";
|
||||
private static final String SPACE_OR_LINE_SEPARATOR_PATTERN = "([\\s" + System.getProperty("line.separator") + "]|(<br ?/?>))+";
|
||||
|
||||
// SHEET
|
||||
public int SHEET_WIDTH = 400;
|
||||
|
||||
@@ -260,11 +260,11 @@ inspection.export.error.writing.to=Error writing to {0}: {1}
|
||||
|
||||
inspection.common.if.parts.family=Extract common parts of 'if' statement
|
||||
inspection.common.if.parts.family.else.if=Merge 'else if' statement
|
||||
inspection.common.if.parts.message.variables.only=Extract variables from 'if' {0}
|
||||
inspection.common.if.parts.message.with.variables.extract=Extract common part with variables from 'if' {0}
|
||||
inspection.common.if.parts.message.without.variables.extract=Extract common part from 'if' {0}
|
||||
inspection.common.if.parts.message.whole.branch=Extract common part removing branch {0}
|
||||
inspection.common.if.parts.message.complete.duplicate=Collapse 'if' statement {0}
|
||||
inspection.common.if.parts.message.variables.only=Extract variables from ''if''{0}
|
||||
inspection.common.if.parts.message.with.variables.extract=Extract common part with variables from ''if''{0}
|
||||
inspection.common.if.parts.message.without.variables.extract=Extract common part from ''if''{0}
|
||||
inspection.common.if.parts.message.whole.branch=Extract common part removing branch{0}
|
||||
inspection.common.if.parts.message.complete.duplicate=Collapse ''if'' statement{0}
|
||||
inspection.common.if.parts.message.complete.duplicate.side.effect=Collapse 'if' statement and extract side effect
|
||||
inspection.common.if.parts.description='if' statement with common parts
|
||||
|
||||
|
||||
+25
-3
@@ -13,10 +13,15 @@ import com.intellij.psi.PsiFile;
|
||||
import com.intellij.testFramework.TestModeFlagListener;
|
||||
import com.intellij.testFramework.TestModeFlags;
|
||||
import com.intellij.testFramework.UsefulTestCase;
|
||||
import com.intellij.testFramework.fixtures.impl.LightTempDirTestFixtureImpl;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Allows to customize the test execution environment for the entire test execution without modifying the source code
|
||||
* of tests. To specify a test execution policy, set the system property "idea.test.execution.policy" to the FQ name
|
||||
* of a class implementing this interface.
|
||||
*
|
||||
* @author yole
|
||||
*/
|
||||
public abstract class IdeaTestExecutionPolicy implements TestModeFlagListener {
|
||||
@@ -24,9 +29,26 @@ public abstract class IdeaTestExecutionPolicy implements TestModeFlagListener {
|
||||
TestModeFlags.addListener(this);
|
||||
}
|
||||
|
||||
public abstract void setUp(Project project, Disposable testRootDisposable, String testDataPath);
|
||||
public abstract TempDirTestFixture createTempDirTestFixture();
|
||||
public abstract boolean runInDispatchThread();
|
||||
/**
|
||||
* Performs the setup required in this test execution mode.
|
||||
*/
|
||||
public void setUp(Project project, Disposable testRootDisposable, String testDataPath) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the fixture for working with temporary files.
|
||||
*/
|
||||
public TempDirTestFixture createTempDirTestFixture() {
|
||||
return new LightTempDirTestFixtureImpl(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* If true, the test method is invoked in the EDT. Otherwise, it runs on the test runner thread.
|
||||
*/
|
||||
public boolean runInDispatchThread() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void testFileConfigured(@NotNull PsiFile file) {
|
||||
}
|
||||
|
||||
|
||||
+4
@@ -6,6 +6,10 @@ import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Marks a test as not applicable to a given test execution policy. If the value of this annotation
|
||||
* matches the {@link IdeaTestExecutionPolicy#getName()} of the current test execution policy, the test will be skipped.
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface SkipWithExecutionPolicy {
|
||||
|
||||
+7
-7
@@ -107,7 +107,9 @@ public class InvokeIntention extends ActionOnFile {
|
||||
List<IntentionAction> intentions = getAvailableIntentions(editor, file);
|
||||
// Do not reuse originally passed offset here, sometimes it's adjusted by Editor
|
||||
PsiElement currentElement = file.findElementAt(editor.getCaretModel().getOffset());
|
||||
intentions = wrapAndCheck(env, editor, currentElement, containsErrorElements, hasErrors, intentions);
|
||||
if (!containsErrorElements) {
|
||||
intentions = wrapAndCheck(env, editor, currentElement, hasErrors, intentions);
|
||||
}
|
||||
IntentionAction intention = chooseIntention(env, intentions);
|
||||
if (intention == null) return;
|
||||
|
||||
@@ -195,7 +197,6 @@ public class InvokeIntention extends ActionOnFile {
|
||||
private List<IntentionAction> wrapAndCheck(Environment env,
|
||||
Editor editor,
|
||||
PsiElement currentElement,
|
||||
boolean containsErrorElements,
|
||||
boolean hasErrors,
|
||||
List<IntentionAction> intentions) {
|
||||
if (currentElement == null) return intentions;
|
||||
@@ -228,14 +229,13 @@ public class InvokeIntention extends ActionOnFile {
|
||||
List<String> messages = new ArrayList<>();
|
||||
|
||||
boolean newContainsErrorElements = MadTestingUtil.containsErrorElements(getFile().getViewProvider());
|
||||
if (newContainsErrorElements != containsErrorElements) {
|
||||
messages.add(newContainsErrorElements ? "File contains parse errors after wrapping" : "File parse errors were fixed after wrapping");
|
||||
if (newContainsErrorElements) {
|
||||
messages.add("File contains parse errors after wrapping");
|
||||
}
|
||||
else {
|
||||
boolean newHasErrors = !highlightErrors(project, editor).isEmpty() || containsErrorElements;
|
||||
boolean newHasErrors = !highlightErrors(project, editor).isEmpty();
|
||||
if (newHasErrors != hasErrors) {
|
||||
messages
|
||||
.add(newHasErrors ? "File contains errors after wrapping" : "File errors were fixed after wrapping");
|
||||
messages.add(newHasErrors ? "File contains errors after wrapping" : "File errors were fixed after wrapping");
|
||||
}
|
||||
}
|
||||
intentions = getAvailableIntentions(editor, file);
|
||||
|
||||
@@ -60,6 +60,9 @@ public class CompressedAppendableFile {
|
||||
|
||||
static final String INCOMPLETE_CHUNK_LENGTH_FILE_EXTENSION = ".s";
|
||||
|
||||
private static int ourFilesCount;
|
||||
private final int myCount = ourFilesCount++;
|
||||
|
||||
public CompressedAppendableFile(File file) {
|
||||
this(file, 32768);
|
||||
}
|
||||
@@ -590,4 +593,9 @@ public class CompressedAppendableFile {
|
||||
return buf[0] & 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return myCount;
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -61,7 +61,7 @@ public class IfStatementWithIdenticalBranchesInspection extends AbstractBaseJava
|
||||
if (!isOnTheFly) return;
|
||||
highlightType = ProblemHighlightType.INFORMATION;
|
||||
}
|
||||
holder.registerProblem(result.myElementToHighlight, InspectionsBundle.message("inspection.common.if.parts.description"), highlightType, result.myFix);
|
||||
holder.registerProblem(result.myElementToHighlight, result.myMessage, highlightType, result.myFix);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -455,7 +455,7 @@ public class IfStatementWithIdenticalBranchesInspection extends AbstractBaseJava
|
||||
|
||||
@NotNull
|
||||
private String getMessage(boolean mayChangeSemantics) {
|
||||
String mayChangeSemanticsText = mayChangeSemantics ? "(may change semantics)" : "";
|
||||
String mayChangeSemanticsText = mayChangeSemantics ? " (may change semantics)" : "";
|
||||
return InspectionsBundle.message(myBundleKey, mayChangeSemanticsText);
|
||||
}
|
||||
|
||||
|
||||
+5
-15
@@ -899,27 +899,17 @@ public class ControlFlowUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param expression expression to check
|
||||
* @return true if given expression is always executed and can be converted to a statement
|
||||
* @param expression expression to check
|
||||
* @return true if given expression can be extracted to a statement
|
||||
*/
|
||||
public static boolean canExtractStatement(PsiExpression expression) {
|
||||
return canExtractStatement(expression, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param expression expression to check
|
||||
* @param checkExecuted if true, expression will be considered non-extractable if it is not always executed within its topmost expression
|
||||
* (e.g. appears in then/else branches in ?: expression)
|
||||
* @return true if given expression can be converted to a statement
|
||||
*/
|
||||
public static boolean canExtractStatement(PsiExpression expression, boolean checkExecuted) {
|
||||
PsiElement cur = expression;
|
||||
PsiElement parent = cur.getParent();
|
||||
while(parent instanceof PsiExpression || parent instanceof PsiExpressionList) {
|
||||
if(parent instanceof PsiLambdaExpression) {
|
||||
return true;
|
||||
}
|
||||
if (checkExecuted && parent instanceof PsiPolyadicExpression) {
|
||||
if (parent instanceof PsiPolyadicExpression) {
|
||||
PsiPolyadicExpression polyadicExpression = (PsiPolyadicExpression)parent;
|
||||
IElementType type = polyadicExpression.getOperationTokenType();
|
||||
if (type.equals(JavaTokenType.ANDAND) && polyadicExpression.getOperands()[0] != cur) {
|
||||
@@ -937,7 +927,7 @@ public class ControlFlowUtils {
|
||||
return (polyParent instanceof PsiReturnStatement) || (polyParent instanceof PsiLambdaExpression);
|
||||
}
|
||||
}
|
||||
if (checkExecuted && parent instanceof PsiConditionalExpression && ((PsiConditionalExpression)parent).getCondition() != cur) {
|
||||
if (parent instanceof PsiConditionalExpression && ((PsiConditionalExpression)parent).getCondition() != cur) {
|
||||
return false;
|
||||
}
|
||||
if(parent instanceof PsiMethodCallExpression) {
|
||||
@@ -951,7 +941,7 @@ public class ControlFlowUtils {
|
||||
}
|
||||
if (parent instanceof PsiStatement) {
|
||||
PsiElement grandParent = parent.getParent();
|
||||
if (checkExecuted && grandParent instanceof PsiForStatement && ((PsiForStatement)grandParent).getUpdate() == parent) {
|
||||
if (grandParent instanceof PsiForStatement && ((PsiForStatement)grandParent).getUpdate() == parent) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
+20
@@ -1394,4 +1394,24 @@ public class ExpressionUtils {
|
||||
public static boolean isArrayCreationExpression(@NotNull PsiNewExpression expression) {
|
||||
return expression.getArrayInitializer() != null || expression.getArrayDimensions().length > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns ancestor expression for given subexpression which parent is not an expression anymore (except lambda)
|
||||
*
|
||||
* @param expression an expression to find its ancestor
|
||||
* @return a top-level expression for given expression (may return an expression itself)
|
||||
*/
|
||||
@NotNull
|
||||
public static PsiExpression getTopLevelExpression(@NotNull PsiExpression expression) {
|
||||
while(true) {
|
||||
PsiElement parent = expression.getParent();
|
||||
if (parent instanceof PsiExpression && !(parent instanceof PsiLambdaExpression)) {
|
||||
expression = (PsiExpression)parent;
|
||||
} else if (parent instanceof PsiExpressionList && parent.getParent() instanceof PsiExpression) {
|
||||
expression = (PsiExpression)parent.getParent();
|
||||
} else {
|
||||
return expression;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+263
-186
@@ -4,9 +4,10 @@ package com.siyeh.ig.psiutils;
|
||||
import com.intellij.codeInsight.Nullability;
|
||||
import com.intellij.codeInsight.NullableNotNullManager;
|
||||
import com.intellij.codeInspection.dataFlow.*;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaConstValue;
|
||||
import com.intellij.codeInspection.dataFlow.ContractReturnValue.BooleanReturnValue;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaRelationValue;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaValue;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaValueFactory;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.psi.util.PsiLiteralUtil;
|
||||
@@ -15,11 +16,14 @@ import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
import com.intellij.util.ThreeState;
|
||||
import one.util.streamex.StreamEx;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class ReorderingUtils {
|
||||
@@ -68,9 +72,21 @@ public class ReorderingUtils {
|
||||
}
|
||||
}
|
||||
if (parent instanceof PsiConditionalExpression) {
|
||||
if (((PsiConditionalExpression)parent).getCondition() == expression) {
|
||||
PsiConditionalExpression ternary = (PsiConditionalExpression)parent;
|
||||
PsiExpression condition = ternary.getCondition();
|
||||
if (condition == expression) {
|
||||
return canExtract(ancestor, parent);
|
||||
}
|
||||
ThreeState result;
|
||||
if (isSideEffectFree(condition, false) &&
|
||||
isSideEffectFree(expression, false)) {
|
||||
result = ThreeState.YES;
|
||||
} else {
|
||||
boolean isNecessary =
|
||||
areConditionsNecessaryFor(new PsiExpression[]{condition}, expression, ternary.getElseExpression() == expression);
|
||||
result = isNecessary ? ThreeState.NO : ThreeState.UNSURE;
|
||||
}
|
||||
return and(result, () -> canExtract(ancestor, parent));
|
||||
}
|
||||
if (parent instanceof PsiLambdaExpression) {
|
||||
return ThreeState.NO;
|
||||
@@ -120,153 +136,272 @@ public class ReorderingUtils {
|
||||
if (operandIndex == 0) return ThreeState.YES;
|
||||
IElementType tokenType = polyadicExpression.getOperationTokenType();
|
||||
if (tokenType != JavaTokenType.ANDAND && tokenType != JavaTokenType.OROR) return ThreeState.UNSURE;
|
||||
PsiExpression[] expressionOperands = polyadicExpression.getOperands();
|
||||
if (operandIndex < 0 || operandIndex >= expressionOperands.length) {
|
||||
PsiExpression[] operands = polyadicExpression.getOperands();
|
||||
if (operandIndex < 0 || operandIndex >= operands.length) {
|
||||
throw new IndexOutOfBoundsException("operandIndex = "+operandIndex);
|
||||
}
|
||||
PsiExpression[] operands = Arrays.copyOfRange(expressionOperands, 0, operandIndex + 1);
|
||||
if (Arrays.stream(operands).allMatch(expression -> isSideEffectFree(expression, false))) {
|
||||
if (Arrays.stream(operands, 0, operandIndex + 1).allMatch(expression -> isSideEffectFree(expression, false))) {
|
||||
return ThreeState.YES;
|
||||
}
|
||||
if (lastOperandImpliesPrevious(polyadicExpression, operands)) {
|
||||
boolean and = polyadicExpression.getOperationTokenType() == JavaTokenType.ANDAND;
|
||||
PsiExpression lastOperand = operands[operandIndex];
|
||||
if (areConditionsNecessaryFor(Arrays.copyOf(operands, operandIndex), lastOperand, !and)) {
|
||||
return ThreeState.NO;
|
||||
}
|
||||
return ThreeState.UNSURE;
|
||||
}
|
||||
|
||||
private enum ExceptionKind {
|
||||
NullDereference {
|
||||
@Override
|
||||
boolean isNecessaryCheck(PsiExpression operand, PsiExpression condition, boolean negated) {
|
||||
if (condition instanceof PsiBinaryExpression) {
|
||||
IElementType tokenType = ((PsiBinaryExpression)condition).getOperationTokenType();
|
||||
if (tokenType.equals(negated ? JavaTokenType.EQEQ : JavaTokenType.NE)) {
|
||||
PsiExpression left = ((PsiBinaryExpression)condition).getLOperand();
|
||||
PsiExpression right = ((PsiBinaryExpression)condition).getROperand();
|
||||
if (ExpressionUtils.isNullLiteral(left)) {
|
||||
return EquivalenceChecker.getCanonicalPsiEquivalence().expressionsAreEquivalent(right, operand);
|
||||
}
|
||||
if (ExpressionUtils.isNullLiteral(right)) {
|
||||
return EquivalenceChecker.getCanonicalPsiEquivalence().expressionsAreEquivalent(left, operand);
|
||||
private static boolean hasContract(PsiExpression expression, PsiExpression operand, ContractReturnValue value) {
|
||||
expression = PsiUtil.skipParenthesizedExprDown(expression);
|
||||
if (value.equals(ContractReturnValue.returnNull()) &&
|
||||
EquivalenceChecker.getCanonicalPsiEquivalence().expressionsAreEquivalent(operand, expression)) {
|
||||
return true;
|
||||
}
|
||||
if (expression instanceof PsiMethodCallExpression) {
|
||||
PsiMethodCallExpression call = (PsiMethodCallExpression)expression;
|
||||
PsiExpressionList argumentList = call.getArgumentList();
|
||||
if (argumentList.isEmpty()) return false;
|
||||
List<? extends MethodContract> contracts = JavaMethodContractUtil.getMethodCallContracts(call);
|
||||
for (MethodContract contract : contracts) {
|
||||
if (contract.getReturnValue().equals(value)) {
|
||||
List<ContractValue> conditions = contract.getConditions();
|
||||
if (conditions.size() == 1) {
|
||||
ContractValue condition = conditions.get(0);
|
||||
int argIndex = condition.getNullCheckedArgument(true).orElse(-1);
|
||||
if (argIndex >= 0) {
|
||||
PsiExpression[] args = argumentList.getExpressions();
|
||||
if (argIndex < args.length) {
|
||||
PsiExpression arg = args[argIndex];
|
||||
if (hasContract(arg, operand, ContractReturnValue.returnNull())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
PsiExpression extractOperand(PsiExpression expression) {
|
||||
if (expression instanceof PsiLiteralExpression ||
|
||||
expression instanceof PsiParenthesizedExpression ||
|
||||
expression instanceof PsiTypeCastExpression ||
|
||||
expression instanceof PsiConditionalExpression ||
|
||||
NullabilityUtil.getExpressionNullability(expression) == Nullability.NOT_NULL) {
|
||||
return null;
|
||||
}
|
||||
PsiExpression realExpression = expression;
|
||||
while (realExpression.getParent() instanceof PsiParenthesizedExpression ||
|
||||
realExpression.getParent() instanceof PsiTypeCastExpression ||
|
||||
(realExpression.getParent() instanceof PsiConditionalExpression &&
|
||||
realExpression != ((PsiConditionalExpression)realExpression.getParent()).getCondition())) {
|
||||
realExpression = (PsiExpression)realExpression.getParent();
|
||||
}
|
||||
PsiElement parent = realExpression.getParent();
|
||||
if (parent instanceof PsiReferenceExpression || parent instanceof PsiArrayAccessExpression) {
|
||||
return expression;
|
||||
}
|
||||
if (parent instanceof PsiPolyadicExpression) {
|
||||
IElementType tokenType = ((PsiPolyadicExpression)parent).getOperationTokenType();
|
||||
if (tokenType.equals(JavaTokenType.PLUS)) {
|
||||
if (TypeUtils.isJavaLangString(((PsiPolyadicExpression)parent).getType())) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return expression;
|
||||
}
|
||||
PsiParameter parameter = MethodCallUtils.getParameterForArgument(realExpression);
|
||||
if (parameter != null && NullableNotNullManager.isNotNull(parameter)) {
|
||||
return expression;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
},
|
||||
ClassCast {
|
||||
@Override
|
||||
boolean isNecessaryCheck(PsiExpression operand, PsiExpression condition, boolean negated) {
|
||||
if (negated) return false;
|
||||
if (condition instanceof PsiInstanceOfExpression) {
|
||||
PsiExpression op = ((PsiInstanceOfExpression)condition).getOperand();
|
||||
return EquivalenceChecker.getCanonicalPsiEquivalence().expressionsAreEquivalent(op, operand);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
PsiExpression extractOperand(PsiExpression expression) {
|
||||
if (expression instanceof PsiTypeCastExpression) {
|
||||
return ((PsiTypeCastExpression)expression).getOperand();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
},
|
||||
ArrayIndex {
|
||||
@Override
|
||||
boolean isNecessaryCheck(PsiExpression operand, PsiExpression condition, boolean negated) {
|
||||
if (condition instanceof PsiBinaryExpression) {
|
||||
IElementType token = ((PsiBinaryExpression)condition).getOperationTokenType();
|
||||
if (ComparisonUtils.isComparisonOperation(token) && !token.equals(JavaTokenType.EQEQ) && !token.equals(JavaTokenType.NE)) {
|
||||
PsiExpression left = ((PsiBinaryExpression)condition).getLOperand();
|
||||
PsiExpression right = ((PsiBinaryExpression)condition).getROperand();
|
||||
return EquivalenceChecker.getCanonicalPsiEquivalence().expressionsAreEquivalent(left, operand) ||
|
||||
EquivalenceChecker.getCanonicalPsiEquivalence().expressionsAreEquivalent(right, operand);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
PsiExpression extractOperand(PsiExpression expression) {
|
||||
if (expression instanceof PsiArrayAccessExpression) {
|
||||
return ((PsiArrayAccessExpression)expression).getIndexExpression();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
abstract boolean isNecessaryCheck(PsiExpression operand, PsiExpression condition, boolean negated);
|
||||
|
||||
abstract PsiExpression extractOperand(PsiExpression expression);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private abstract static class ExceptionProblem {
|
||||
final PsiExpression myOperand;
|
||||
|
||||
private static class Problem {
|
||||
private final ExceptionKind myKind;
|
||||
private final PsiExpression myOperand;
|
||||
|
||||
private Problem(ExceptionKind kind, PsiExpression operand) {
|
||||
myKind = kind;
|
||||
ExceptionProblem(PsiExpression operand) {
|
||||
myOperand = operand;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
static List<Problem> fromExpression(PsiExpression expression) {
|
||||
List<Problem> problems = new ArrayList<>();
|
||||
for (ExceptionKind kind : ExceptionKind.values()) {
|
||||
PsiExpression operand = kind.extractOperand(expression);
|
||||
if (operand != null) {
|
||||
problems.add(new Problem(kind, operand));
|
||||
abstract boolean isNecessaryCheck(PsiExpression condition, boolean negated);
|
||||
}
|
||||
|
||||
static final class NullDereferenceExceptionProblem extends ExceptionProblem {
|
||||
private NullDereferenceExceptionProblem(PsiExpression operand) {
|
||||
super(operand);
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isNecessaryCheck(PsiExpression condition, boolean negated) {
|
||||
if (condition instanceof PsiBinaryExpression) {
|
||||
IElementType tokenType = ((PsiBinaryExpression)condition).getOperationTokenType();
|
||||
if (tokenType.equals(JavaTokenType.EQEQ) || tokenType.equals(JavaTokenType.NE)) {
|
||||
boolean notNull = negated != tokenType.equals(JavaTokenType.EQEQ);
|
||||
ContractReturnValue returnValue = notNull ? ContractReturnValue.returnNotNull() : ContractReturnValue.returnNull();
|
||||
PsiExpression left = ((PsiBinaryExpression)condition).getLOperand();
|
||||
PsiExpression right = ((PsiBinaryExpression)condition).getROperand();
|
||||
if (ExpressionUtils.isNullLiteral(left)) {
|
||||
return hasContract(right, myOperand, returnValue);
|
||||
}
|
||||
if (ExpressionUtils.isNullLiteral(right)) {
|
||||
return hasContract(left, myOperand, returnValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
return problems;
|
||||
return hasContract(condition, myOperand, ContractReturnValue.returnBoolean(negated));
|
||||
}
|
||||
|
||||
static NullDereferenceExceptionProblem from(PsiExpression expression) {
|
||||
if (expression instanceof PsiLiteralExpression ||
|
||||
expression instanceof PsiParenthesizedExpression ||
|
||||
expression instanceof PsiTypeCastExpression ||
|
||||
expression instanceof PsiConditionalExpression ||
|
||||
NullabilityUtil.getExpressionNullability(expression) == Nullability.NOT_NULL) {
|
||||
return null;
|
||||
}
|
||||
PsiExpression realExpression = expression;
|
||||
while (realExpression.getParent() instanceof PsiParenthesizedExpression ||
|
||||
realExpression.getParent() instanceof PsiTypeCastExpression ||
|
||||
(realExpression.getParent() instanceof PsiConditionalExpression &&
|
||||
realExpression != ((PsiConditionalExpression)realExpression.getParent()).getCondition())) {
|
||||
realExpression = (PsiExpression)realExpression.getParent();
|
||||
}
|
||||
PsiElement parent = realExpression.getParent();
|
||||
if (parent instanceof PsiReferenceExpression || parent instanceof PsiArrayAccessExpression) {
|
||||
return new NullDereferenceExceptionProblem(expression);
|
||||
}
|
||||
if (parent instanceof PsiPolyadicExpression) {
|
||||
IElementType tokenType = ((PsiPolyadicExpression)parent).getOperationTokenType();
|
||||
if (tokenType.equals(JavaTokenType.PLUS)) {
|
||||
if (TypeUtils.isJavaLangString(((PsiPolyadicExpression)parent).getType())) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return new NullDereferenceExceptionProblem(expression);
|
||||
}
|
||||
PsiParameter parameter = MethodCallUtils.getParameterForArgument(realExpression);
|
||||
if (parameter != null && NullableNotNullManager.isNotNull(parameter)) {
|
||||
return new NullDereferenceExceptionProblem(expression);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static final class ClassCastExceptionProblem extends ExceptionProblem {
|
||||
private ClassCastExceptionProblem(PsiExpression operand) {
|
||||
super(operand);
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isNecessaryCheck(PsiExpression condition, boolean negated) {
|
||||
return myKind.isNecessaryCheck(myOperand, condition, negated);
|
||||
if (negated) return false;
|
||||
if (condition instanceof PsiInstanceOfExpression) {
|
||||
PsiExpression op = ((PsiInstanceOfExpression)condition).getOperand();
|
||||
return EquivalenceChecker.getCanonicalPsiEquivalence().expressionsAreEquivalent(op, myOperand);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static ClassCastExceptionProblem from(PsiExpression expression) {
|
||||
if (expression instanceof PsiTypeCastExpression) {
|
||||
return new ClassCastExceptionProblem(((PsiTypeCastExpression)expression).getOperand());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static final class ArrayIndexExceptionProblem extends ExceptionProblem {
|
||||
private ArrayIndexExceptionProblem(PsiExpression operand) {
|
||||
super(operand);
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isNecessaryCheck(PsiExpression condition, boolean negated) {
|
||||
if (condition instanceof PsiBinaryExpression) {
|
||||
IElementType token = ((PsiBinaryExpression)condition).getOperationTokenType();
|
||||
if (ComparisonUtils.isComparisonOperation(token) && !token.equals(JavaTokenType.EQEQ) && !token.equals(JavaTokenType.NE)) {
|
||||
PsiExpression left = ((PsiBinaryExpression)condition).getLOperand();
|
||||
PsiExpression right = ((PsiBinaryExpression)condition).getROperand();
|
||||
return EquivalenceChecker.getCanonicalPsiEquivalence().expressionsAreEquivalent(left, myOperand) ||
|
||||
EquivalenceChecker.getCanonicalPsiEquivalence().expressionsAreEquivalent(right, myOperand);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static ArrayIndexExceptionProblem from(PsiExpression expression) {
|
||||
if (expression instanceof PsiArrayAccessExpression) {
|
||||
return new ArrayIndexExceptionProblem(((PsiArrayAccessExpression)expression).getIndexExpression());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static final class ContractFailExceptionProblem extends ExceptionProblem {
|
||||
private final DfaValueFactory myFactory;
|
||||
private final List<DfaRelationValue> myConditions;
|
||||
|
||||
ContractFailExceptionProblem(DfaValueFactory factory, List<DfaRelationValue> conditions) {
|
||||
super(null);
|
||||
myFactory = factory;
|
||||
myConditions = conditions;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isNecessaryCheck(PsiExpression condition, boolean negated) {
|
||||
if (condition instanceof PsiMethodCallExpression) {
|
||||
PsiMethodCallExpression call = (PsiMethodCallExpression)condition;
|
||||
List<? extends MethodContract> contracts = JavaMethodContractUtil.getMethodCallContracts(call);
|
||||
if (contracts.isEmpty()) return false;
|
||||
for (MethodContract contract : contracts) {
|
||||
if (!(contract.getReturnValue() instanceof BooleanReturnValue)) continue;
|
||||
boolean retValue = ((BooleanReturnValue)contract.getReturnValue()).getValue();
|
||||
List<ContractValue> conditions = contract.getConditions();
|
||||
if (conditions.size() != 1) continue;
|
||||
ContractValue cond = conditions.get(0);
|
||||
DfaValue value = cond.fromCall(myFactory, call);
|
||||
if (value instanceof DfaRelationValue) {
|
||||
if (myConditions.contains(retValue == negated ? value : value.createNegated())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (condition instanceof PsiBinaryExpression) {
|
||||
PsiBinaryExpression binOp = (PsiBinaryExpression)condition;
|
||||
DfaRelationValue.RelationType relationType = DfaRelationValue.RelationType.fromElementType(binOp.getOperationTokenType());
|
||||
if (relationType != null) {
|
||||
PsiExpression left = binOp.getLOperand();
|
||||
PsiExpression right = binOp.getROperand();
|
||||
DfaValue leftVal = myFactory.createValue(left);
|
||||
DfaValue rightVal = myFactory.createValue(right);
|
||||
DfaValue value1 = myFactory.createCondition(leftVal, relationType, rightVal);
|
||||
DfaValue value2 = myFactory.createCondition(rightVal, relationType.getFlipped(), leftVal);
|
||||
if (value1 instanceof DfaRelationValue) {
|
||||
if (myConditions.contains(negated ? value1 : value1.createNegated())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (value2 instanceof DfaRelationValue) {
|
||||
if (myConditions.contains(negated ? value2 : value2.createNegated())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static ContractFailExceptionProblem from(PsiExpression expression) {
|
||||
if (expression instanceof PsiCallExpression) {
|
||||
PsiCallExpression call = (PsiCallExpression)expression;
|
||||
List<? extends MethodContract> contracts = JavaMethodContractUtil.getMethodCallContracts(call);
|
||||
contracts = ContainerUtil.filter(contracts, c -> c.getReturnValue().isFail() && c.getConditions().size() == 1);
|
||||
if (contracts.isEmpty()) return null;
|
||||
DfaValueFactory factory = new DfaValueFactory(null, false);
|
||||
List<DfaRelationValue> conditions = new ArrayList<>();
|
||||
for (MethodContract contract : contracts) {
|
||||
ContractValue condition = contract.getConditions().get(0);
|
||||
DfaValue conditionValue = condition.fromCall(factory, call);
|
||||
if (conditionValue instanceof DfaRelationValue) {
|
||||
conditions.add((DfaRelationValue)conditionValue);
|
||||
}
|
||||
}
|
||||
return new ContractFailExceptionProblem(factory, conditions);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static final List<Function<PsiExpression, ExceptionProblem>> PROBLEM_EXTRACTORS = Arrays.asList(
|
||||
NullDereferenceExceptionProblem::from, ClassCastExceptionProblem::from, ArrayIndexExceptionProblem::from,
|
||||
ContractFailExceptionProblem::from
|
||||
);
|
||||
|
||||
@NotNull
|
||||
static List<ExceptionProblem> fromExpression(PsiExpression expression) {
|
||||
List<ExceptionProblem> problems = new ArrayList<>();
|
||||
for (Function<PsiExpression, ExceptionProblem> extractor : PROBLEM_EXTRACTORS) {
|
||||
ExceptionProblem exceptionProblem = extractor.apply(expression);
|
||||
if (exceptionProblem != null) {
|
||||
problems.add(exceptionProblem);
|
||||
}
|
||||
}
|
||||
return problems;
|
||||
}
|
||||
|
||||
private static boolean areConditionsNecessaryFor(PsiExpression[] conditions, PsiExpression operand, boolean negated) {
|
||||
List<Problem> problems = SyntaxTraverser.psiTraverser(operand)
|
||||
.traverse().filter(PsiExpression.class).flatMap(Problem::fromExpression).filter(Objects::nonNull)
|
||||
List<ExceptionProblem> problems = SyntaxTraverser.psiTraverser(operand)
|
||||
.traverse().filter(PsiExpression.class).flatMap(ReorderingUtils::fromExpression).filter(Objects::nonNull)
|
||||
.toList();
|
||||
if (problems.isEmpty()) return false;
|
||||
for (PsiExpression condition : conditions) {
|
||||
@@ -275,7 +410,7 @@ public class ReorderingUtils {
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean isConditionNecessary(PsiExpression condition, List<Problem> problems, boolean negated) {
|
||||
private static boolean isConditionNecessary(PsiExpression condition, List<ExceptionProblem> problems, boolean negated) {
|
||||
condition = PsiUtil.skipParenthesizedExprDown(condition);
|
||||
if (condition == null) return false;
|
||||
if (BoolUtils.isNegation(condition)) {
|
||||
@@ -300,7 +435,7 @@ public class ReorderingUtils {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
for (Problem problem : problems) {
|
||||
for (ExceptionProblem problem : problems) {
|
||||
if (problem.isNecessaryCheck(condition, negated)) {
|
||||
return true;
|
||||
}
|
||||
@@ -308,29 +443,6 @@ public class ReorderingUtils {
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean lastOperandImpliesPrevious(PsiPolyadicExpression expression, PsiExpression[] operands) {
|
||||
assert operands.length > 1;
|
||||
boolean and = expression.getOperationTokenType() == JavaTokenType.ANDAND;
|
||||
if (Arrays.stream(operands).anyMatch(e -> !PsiTreeUtil.processElements(e, element -> !isErroneous(element)))) return false;
|
||||
PsiExpression lastOperand = operands[operands.length - 1];
|
||||
if (areConditionsNecessaryFor(Arrays.copyOf(operands, operands.length - 1), lastOperand, !and)) return true;
|
||||
String expressionText = StreamEx.of(operands, 0, operands.length - 1).prepend(lastOperand)
|
||||
.map(PsiExpression::getText).joining(and ? " && " : " || ");
|
||||
PsiElementFactory factory = JavaPsiFacade.getElementFactory(expression.getProject());
|
||||
PsiPolyadicExpression expressionToAnalyze = (PsiPolyadicExpression)factory.createExpressionFromText(expressionText, expression);
|
||||
PsiExpression[] newOperands = expressionToAnalyze.getOperands();
|
||||
Map<PsiExpression, ThreeState> map = computeOperandValues(expressionToAnalyze, false);
|
||||
ThreeState state = ThreeState.fromBoolean(and);
|
||||
Set<PsiExpression> redundantOperands = StreamEx.of(newOperands).skip(1).filterBy(map::get, state).toSet();
|
||||
if (redundantOperands.isEmpty()) return false;
|
||||
if (operands.length == 2) {
|
||||
return !Boolean.valueOf(and).equals(DfaUtil.computeValue(operands[0]));
|
||||
}
|
||||
expressionText = StreamEx.of(operands, 0, operands.length - 1).map(PsiExpression::getText).joining(and ? " && " : " || ");
|
||||
expressionToAnalyze = (PsiPolyadicExpression)factory.createExpressionFromText(expressionText, expression);
|
||||
return !computeOperandValues(expressionToAnalyze, false).values().contains(state);
|
||||
}
|
||||
|
||||
private static boolean isErroneous(PsiElement element) {
|
||||
return element instanceof PsiErrorElement ||
|
||||
element instanceof PsiLiteralExpression &&
|
||||
@@ -379,39 +491,4 @@ public class ReorderingUtils {
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static Map<PsiExpression, ThreeState> computeOperandValues(PsiPolyadicExpression expressionToAnalyze, boolean skipNullityUpdate) {
|
||||
DataFlowRunner runner = new StandardDataFlowRunner(false, expressionToAnalyze);
|
||||
Map<PsiExpression, ThreeState> values = new HashMap<>();
|
||||
StandardInstructionVisitor visitor = new StandardInstructionVisitor() {
|
||||
@Override
|
||||
protected boolean checkNotNullable(DfaMemoryState state,
|
||||
DfaValue value,
|
||||
@Nullable NullabilityProblemKind.NullabilityProblem<?> problem) {
|
||||
return skipNullityUpdate || super.checkNotNullable(state, value, problem);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void beforeExpressionPush(@NotNull DfaValue value,
|
||||
@NotNull PsiExpression expression,
|
||||
@Nullable TextRange range,
|
||||
@NotNull DfaMemoryState state) {
|
||||
super.beforeExpressionPush(value, expression, range, state);
|
||||
if (PsiUtil.skipParenthesizedExprUp(expression.getParent()) != expressionToAnalyze) return;
|
||||
ThreeState old = values.get(expression);
|
||||
if (old == ThreeState.UNSURE) return;
|
||||
ThreeState result = ThreeState.UNSURE;
|
||||
if (value instanceof DfaConstValue) {
|
||||
Object bool = ((DfaConstValue)value).getValue();
|
||||
if (bool instanceof Boolean) {
|
||||
result = ThreeState.fromBoolean((Boolean)bool);
|
||||
}
|
||||
}
|
||||
values.put(expression, old == null || old == result ? result : ThreeState.UNSURE);
|
||||
}
|
||||
};
|
||||
RunnerResult result = runner.analyzeMethod(expressionToAnalyze, visitor);
|
||||
return result == RunnerResult.OK ? values : Collections.emptyMap();
|
||||
}
|
||||
}
|
||||
|
||||
+57
-8
@@ -3,7 +3,14 @@ package com.siyeh.ig.controlflow;
|
||||
|
||||
import com.intellij.codeInspection.AbstractBaseJavaLocalInspectionTool;
|
||||
import com.intellij.codeInspection.ProblemsHolder;
|
||||
import com.intellij.codeInspection.dataFlow.DfaUtil;
|
||||
import com.intellij.codeInspection.dataFlow.*;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaConstValue;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaValue;
|
||||
import com.intellij.openapi.application.Application;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.diagnostic.Attachment;
|
||||
import com.intellij.openapi.diagnostic.RuntimeExceptionWithAttachments;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.psi.util.PsiExpressionTrimRenderer;
|
||||
@@ -16,11 +23,9 @@ import com.siyeh.ig.psiutils.ReorderingUtils;
|
||||
import one.util.streamex.IntStreamEx;
|
||||
import one.util.streamex.StreamEx;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
|
||||
public class ConditionCoveredByFurtherConditionInspection extends AbstractBaseJavaLocalInspectionTool {
|
||||
@NotNull
|
||||
@@ -97,10 +102,19 @@ public class ConditionCoveredByFurtherConditionInspection extends AbstractBaseJa
|
||||
Object value = DfaUtil.computeValue(operands.get(0));
|
||||
return Boolean.valueOf(and).equals(value) ? new int[]{0} : ArrayUtil.EMPTY_INT_ARRAY;
|
||||
}
|
||||
PsiPolyadicExpression expressionToAnalyze = (PsiPolyadicExpression)JavaPsiFacade.getElementFactory(context.getProject())
|
||||
.createExpressionFromText(StreamEx.ofReversed(operands).map(PsiElement::getText).joining(and ? " && " : " || "), context);
|
||||
String text = StreamEx.ofReversed(operands).map(PsiElement::getText).joining(and ? " && " : " || ");
|
||||
PsiExpression expression = JavaPsiFacade.getElementFactory(context.getProject()).createExpressionFromText(text, context);
|
||||
if (!(expression instanceof PsiPolyadicExpression)) {
|
||||
Application application = ApplicationManager.getApplication();
|
||||
if (application.isEAP() || application.isInternal()) {
|
||||
throw new RuntimeExceptionWithAttachments("Unexpected expression type: " + expression.getClass().getName(),
|
||||
new Attachment("reversed.txt", text));
|
||||
}
|
||||
return ArrayUtil.EMPTY_INT_ARRAY;
|
||||
}
|
||||
PsiPolyadicExpression expressionToAnalyze = (PsiPolyadicExpression)expression;
|
||||
List<PsiExpression> reversedOperands = Arrays.asList(expressionToAnalyze.getOperands());
|
||||
Map<PsiExpression, ThreeState> values = ReorderingUtils.computeOperandValues(expressionToAnalyze, true);
|
||||
Map<PsiExpression, ThreeState> values = computeOperandValues(expressionToAnalyze);
|
||||
return StreamEx.ofKeys(values, ThreeState.fromBoolean(and)::equals)
|
||||
.mapToInt(operand -> IntStreamEx.ofIndices(reversedOperands, op -> PsiTreeUtil.isAncestor(op, operand, false))
|
||||
.findFirst().orElse(0))
|
||||
@@ -108,4 +122,39 @@ public class ConditionCoveredByFurtherConditionInspection extends AbstractBaseJa
|
||||
.toArray();
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static Map<PsiExpression, ThreeState> computeOperandValues(PsiPolyadicExpression expressionToAnalyze) {
|
||||
DataFlowRunner runner = new StandardDataFlowRunner(false, expressionToAnalyze);
|
||||
Map<PsiExpression, ThreeState> values = new HashMap<>();
|
||||
StandardInstructionVisitor visitor = new StandardInstructionVisitor() {
|
||||
@Override
|
||||
protected boolean checkNotNullable(DfaMemoryState state,
|
||||
DfaValue value,
|
||||
@Nullable NullabilityProblemKind.NullabilityProblem<?> problem) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void beforeExpressionPush(@NotNull DfaValue value,
|
||||
@NotNull PsiExpression expression,
|
||||
@Nullable TextRange range,
|
||||
@NotNull DfaMemoryState state) {
|
||||
super.beforeExpressionPush(value, expression, range, state);
|
||||
if (PsiUtil.skipParenthesizedExprUp(expression.getParent()) != expressionToAnalyze) return;
|
||||
ThreeState old = values.get(expression);
|
||||
if (old == ThreeState.UNSURE) return;
|
||||
ThreeState result = ThreeState.UNSURE;
|
||||
if (value instanceof DfaConstValue) {
|
||||
Object bool = ((DfaConstValue)value).getValue();
|
||||
if (bool instanceof Boolean) {
|
||||
result = ThreeState.fromBoolean((Boolean)bool);
|
||||
}
|
||||
}
|
||||
values.put(expression, old == null || old == result ? result : ThreeState.UNSURE);
|
||||
}
|
||||
};
|
||||
RunnerResult result = runner.analyzeMethod(expressionToAnalyze, visitor);
|
||||
return result == RunnerResult.OK ? values : Collections.emptyMap();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
<html>
|
||||
<body>
|
||||
Reports <b>.equals()</b> being called to compare a String with an empty string.
|
||||
Using <b>.isEmpty()</b> is more clear and could be slightly more performant.
|
||||
Reports cases when <b>.equals()</b> is called to compare a String with an empty string. In this case, using <b>.isEmpty()</b> is better as
|
||||
it shows you exactly what you're checking.
|
||||
<!-- tooltip end -->
|
||||
<p>
|
||||
The check like <b>"".equals(str)</b> returns false when <b>str</b> is null, so
|
||||
an explicit null-check could be inserted when replacing with <b>isEmpty()</b>.
|
||||
Use a checkbox below to suppress warning when value could be nullable.
|
||||
Some checks, for example, the <b>"".equals(str)</b> check, return false when <b>str</b> is null. It means that an explicit null-check
|
||||
could be inserted when you are replacing <b>.equals()</b> with <b>isEmpty()</b>. Use a checkbox below to suppress the warning when a
|
||||
string value could be nullable.
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.siyeh.ig.psiutils;
|
||||
|
||||
import com.intellij.codeInsight.CodeInsightUtil;
|
||||
import com.intellij.ide.highlighter.JavaFileType;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.testFramework.LightCodeInsightTestCase;
|
||||
@@ -9,56 +10,97 @@ import org.intellij.lang.annotations.Language;
|
||||
|
||||
public class ReorderingUtilsTest extends LightCodeInsightTestCase {
|
||||
private static final String PREFIX = "import java.util.Optional;\n" +
|
||||
"import java.util.List;\n" +
|
||||
"/** @noinspection all*/\n" +
|
||||
"class X {boolean test(Object obj, String str, int x, int y, String[] arr, Optional<String> opt) { return ";
|
||||
"class X {Object test(Object obj, String str, int x, int y, String[] arr, " +
|
||||
"Optional<String> opt, List<String> list) { return ";
|
||||
@SuppressWarnings("UnnecessarySemicolon")
|
||||
private static final String SUFFIX = ";}}";
|
||||
private static final String SUFFIX = ";} static Object nullNull(Object obj) {return obj == null ? null : obj.hashCode();}}";
|
||||
private static final String SELECTION_START = "/*<*/";
|
||||
private static final String SELECTION_END = "/*>*/";
|
||||
|
||||
public void testSimple() {
|
||||
checkCanBeReordered("x > 0 && x < 10", 1, ThreeState.YES);
|
||||
checkCanBeReordered("arr == null && x > 10 && y < 5", 2, ThreeState.YES);
|
||||
checkCanBeReordered("x > 0 && /*<*/x < 10/*>*/", ThreeState.YES);
|
||||
checkCanBeReordered("arr == null && x > 10 && /*<*/y < 5/*>*/", ThreeState.YES);
|
||||
}
|
||||
|
||||
public void testTrueFirst() {
|
||||
checkCanBeReordered("x > 10 && x > 0 && x > arr.length", 2, ThreeState.UNSURE);
|
||||
checkCanBeReordered("(x > 10 || x < 20) && x > arr.length", 1, ThreeState.UNSURE);
|
||||
checkCanBeReordered("x > 10 && x > 0 && /*<*/x > arr.length/*>*/", ThreeState.UNSURE);
|
||||
checkCanBeReordered("(x > 10 || x < 20) && /*<*/x > arr.length/*>*/", ThreeState.UNSURE);
|
||||
}
|
||||
|
||||
public void testNpe() {
|
||||
checkCanBeReordered("obj != null && obj.hashCode() > 10", 1, ThreeState.NO);
|
||||
checkCanBeReordered("obj == null || obj.hashCode() > 10", 1, ThreeState.NO);
|
||||
checkCanBeReordered("arr != null && obj.hashCode() > 10", 1, ThreeState.UNSURE);
|
||||
checkCanBeReordered("obj != null && /*<*/obj.hashCode() > 10/*>*/", ThreeState.NO);
|
||||
checkCanBeReordered("obj == null || /*<*/obj.hashCode() > 10/*>*/", ThreeState.NO);
|
||||
checkCanBeReordered("arr != null && /*<*/obj.hashCode() > 10/*>*/", ThreeState.UNSURE);
|
||||
}
|
||||
|
||||
public void testCast() {
|
||||
checkCanBeReordered("obj instanceof String && ((String)obj).isEmpty()", 1, ThreeState.NO);
|
||||
checkCanBeReordered("obj instanceof String && test(null, (String)obj, 0,0, null, Optional.empty())", 1, ThreeState.NO);
|
||||
checkCanBeReordered("obj instanceof Integer && ((Number)obj).intValue() == 0", 1, ThreeState.NO);
|
||||
checkCanBeReordered("obj instanceof String && /*<*/((String)obj).isEmpty()/*>*/", ThreeState.NO);
|
||||
checkCanBeReordered("obj instanceof String && /*<*/nullNull((String)obj) == null/*>*/", ThreeState.NO);
|
||||
checkCanBeReordered("obj instanceof Integer && /*<*/((Number)obj).intValue() == 0/*>*/", ThreeState.NO);
|
||||
}
|
||||
|
||||
public void testContract() {
|
||||
checkCanBeReordered("new Object().equals(obj) && /*<*/obj.hashCode() == 0/*>*/", ThreeState.NO);
|
||||
checkCanBeReordered("nullNull(obj) != null && /*<*/obj.hashCode() == 0/*>*/", ThreeState.NO);
|
||||
checkCanBeReordered("nullNull(obj) == null || /*<*/obj.hashCode() == 0/*>*/", ThreeState.NO);
|
||||
checkCanBeReordered("nullNull(nullNull(obj)) == null || /*<*/obj.hashCode() == 0/*>*/", ThreeState.NO);
|
||||
}
|
||||
|
||||
public void testArrayBounds() {
|
||||
checkCanBeReordered("x >= 0 && arr[x].isEmpty()", 1, ThreeState.NO);
|
||||
checkCanBeReordered("x < arr.length && arr[x].isEmpty()", 1, ThreeState.NO);
|
||||
checkCanBeReordered("x >= 0 && x < arr.length && arr[x].isEmpty()", 2, ThreeState.NO);
|
||||
checkCanBeReordered("y >= 0 && arr[x].isEmpty()", 1, ThreeState.UNSURE);
|
||||
checkCanBeReordered("x > 0 && arr[x].isEmpty()", 1, ThreeState.NO);
|
||||
checkCanBeReordered("x >= 0 && /*<*/arr[x].isEmpty()/*>*/", ThreeState.NO);
|
||||
checkCanBeReordered("x < arr.length && /*<*/arr[x].isEmpty()/*>*/", ThreeState.NO);
|
||||
checkCanBeReordered("x >= 0 && x < arr.length && /*<*/arr[x].isEmpty()/*>*/", ThreeState.NO);
|
||||
checkCanBeReordered("y >= 0 && /*<*/arr[x].isEmpty()/*>*/", ThreeState.UNSURE);
|
||||
checkCanBeReordered("x > 0 && /*<*/arr[x].isEmpty()/*>*/", ThreeState.NO);
|
||||
// Not supported
|
||||
checkCanBeReordered("x > 0 && arr[x-1].isEmpty()", 1, ThreeState.UNSURE);
|
||||
checkCanBeReordered("x > 0 && /*<*/arr[x-1].isEmpty()/*>*/", ThreeState.UNSURE);
|
||||
}
|
||||
|
||||
public void testStringBounds() {
|
||||
checkCanBeReordered("x >= 0 && /*<*/str.charAt(x) == 'a'/*>*/", ThreeState.NO);
|
||||
checkCanBeReordered("y >= 0 && /*<*/str.charAt(x) == 'a'/*>*/", ThreeState.UNSURE);
|
||||
checkCanBeReordered("x < str.length() && /*<*/str.charAt(x) == 'a'/*>*/", ThreeState.NO);
|
||||
checkCanBeReordered("x <= str.length() && /*<*/str.substring(x) == 'a'/*>*/", ThreeState.NO);
|
||||
// Not supported
|
||||
checkCanBeReordered("x < str.length() && /*<*/str.substring(x) == 'a'/*>*/", ThreeState.UNSURE);
|
||||
}
|
||||
|
||||
public void testListBounds() {
|
||||
checkCanBeReordered("x >= 0 && /*<*/list.get(x).isEmpty()/*>*/", ThreeState.NO);
|
||||
checkCanBeReordered("x < list.size() && /*<*/list.get(x).isEmpty()/*>*/", ThreeState.NO);
|
||||
checkCanBeReordered("list.size() > x && /*<*/list.get(x).isEmpty()/*>*/", ThreeState.NO);
|
||||
}
|
||||
|
||||
public void testOptional() {
|
||||
checkCanBeReordered("opt.isPresent() && opt.get().isEmpty()", 1, ThreeState.NO);
|
||||
checkCanBeReordered("opt.isPresent() && /*<*/opt.get().isEmpty()/*>*/", ThreeState.NO);
|
||||
}
|
||||
|
||||
public void testTernary() {
|
||||
checkCanBeReordered("x > y ? /*<*/y/*>*/ : x", ThreeState.YES);
|
||||
checkCanBeReordered("str == null ? \"\" : /*<*/str.trim()/*>*/", ThreeState.NO);
|
||||
checkCanBeReordered("str != null ? /*<*/str.trim()/*>*/ : \"\"", ThreeState.NO);
|
||||
checkCanBeReordered("str != null ? \"\" : /*<*/str.trim()/*>*/", ThreeState.UNSURE);
|
||||
checkCanBeReordered("str == null ? /*<*/str.trim()/*>*/ : \"\"", ThreeState.UNSURE);
|
||||
}
|
||||
|
||||
private static void checkCanBeReordered(@Language(value = "JAVA", prefix = PREFIX, suffix = SUFFIX) String expressionText,
|
||||
int operand,
|
||||
ThreeState expectedResult) {
|
||||
String file = PREFIX + expressionText + SUFFIX;
|
||||
PsiJavaFile javaFile = (PsiJavaFile)PsiFileFactory.getInstance(getProject()).createFileFromText("X.java", JavaFileType.INSTANCE, file);
|
||||
PsiCodeBlock body = javaFile.getClasses()[0].getMethods()[0].getBody();
|
||||
assertNotNull(body);
|
||||
PsiPolyadicExpression expression = (PsiPolyadicExpression)((PsiReturnStatement)body.getStatements()[0]).getReturnValue();
|
||||
PsiExpression expression = ((PsiReturnStatement)body.getStatements()[0]).getReturnValue();
|
||||
assertNotNull(expression);
|
||||
assertSame(expressionText, expectedResult, ReorderingUtils.canExtract(expression, expression.getOperands()[operand]));
|
||||
int startOffset = expressionText.indexOf(SELECTION_START);
|
||||
assertTrue(startOffset >= 0);
|
||||
int endOffset = expressionText.indexOf(SELECTION_END);
|
||||
assertTrue(endOffset >= 0);
|
||||
int expressionStart = expression.getTextRange().getStartOffset();
|
||||
PsiExpression subExpression = CodeInsightUtil.findExpressionInRange(javaFile, startOffset + SELECTION_START.length() + expressionStart,
|
||||
endOffset + expressionStart);
|
||||
assertNotNull(subExpression);
|
||||
assertSame(expressionText, expectedResult, ReorderingUtils.canExtract(expression, subExpression));
|
||||
}
|
||||
}
|
||||
@@ -85,6 +85,7 @@ public final class AntBuildMessageView extends JPanel implements DataProvider, O
|
||||
private AntBuildFileBase myBuildFile;
|
||||
private final List<String> myTargets;
|
||||
private final List<BuildFileProperty> myAdditionalProperties;
|
||||
@AntMessage.Priority
|
||||
private int myPriorityThreshold = PRIORITY_INFO;
|
||||
private volatile int myErrorCount;
|
||||
private volatile int myWarningCount;
|
||||
@@ -181,7 +182,7 @@ public final class AntBuildMessageView extends JPanel implements DataProvider, O
|
||||
return myPriorityThreshold == PRIORITY_DEBUG;
|
||||
}
|
||||
|
||||
private synchronized void changeDetalizationLevel(int priorityThreshold) {
|
||||
private synchronized void changeDetalizationLevel(@AntMessage.Priority int priorityThreshold) {
|
||||
myPriorityThreshold = priorityThreshold;
|
||||
|
||||
TreeView.TreeSelection selection = myTreeView.getSelection();
|
||||
@@ -393,7 +394,7 @@ public final class AntBuildMessageView extends JPanel implements DataProvider, O
|
||||
addCommand(new StartTaskCommand(taskName));
|
||||
}
|
||||
|
||||
public void outputMessage(final String text, final int priority) {
|
||||
public void outputMessage(final String text, @AntMessage.Priority int priority) {
|
||||
final AntMessage customizedMessage = getCustomizedMessage(text, priority);
|
||||
final AntMessage message = customizedMessage != null
|
||||
? customizedMessage
|
||||
@@ -403,7 +404,7 @@ public final class AntBuildMessageView extends JPanel implements DataProvider, O
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private AntMessage getCustomizedMessage(final String text, final int priority) {
|
||||
private AntMessage getCustomizedMessage(final String text, @AntMessage.Priority int priority) {
|
||||
AntMessage customizedMessage = null;
|
||||
|
||||
for (AntMessageCustomizer customizer : myMessageCustomizers) {
|
||||
@@ -416,7 +417,7 @@ public final class AntBuildMessageView extends JPanel implements DataProvider, O
|
||||
return customizedMessage;
|
||||
}
|
||||
|
||||
public void outputError(String error, int priority) {
|
||||
public void outputError(String error, @AntMessage.Priority int priority) {
|
||||
updateErrorAndWarningCounters(priority);
|
||||
final AntMessage message = createErrorMessage(priority, error);
|
||||
addCommand(new AddMessageCommand(message));
|
||||
@@ -433,7 +434,7 @@ public final class AntBuildMessageView extends JPanel implements DataProvider, O
|
||||
}
|
||||
|
||||
|
||||
private void updateErrorAndWarningCounters(int priority) {
|
||||
private void updateErrorAndWarningCounters(@AntMessage.Priority int priority) {
|
||||
if (priority == PRIORITY_ERR) {
|
||||
myErrorCount++;
|
||||
}
|
||||
@@ -465,7 +466,7 @@ public final class AntBuildMessageView extends JPanel implements DataProvider, O
|
||||
return null;
|
||||
}
|
||||
|
||||
private static AntMessage createErrorMessage(int priority, String text) {
|
||||
private static AntMessage createErrorMessage(@AntMessage.Priority int priority, String text) {
|
||||
text = StringUtil.trimStart(text, FILE_PREFIX);
|
||||
|
||||
int afterLineNumberIndex = text.indexOf(": "); // end of file_name_and_line_number sequence
|
||||
@@ -623,12 +624,14 @@ public final class AntBuildMessageView extends JPanel implements DataProvider, O
|
||||
}
|
||||
|
||||
private abstract static class LogCommand {
|
||||
@AntMessage.Priority
|
||||
private final int myPriority;
|
||||
|
||||
LogCommand(int priority) {
|
||||
LogCommand(@AntMessage.Priority int priority) {
|
||||
myPriority = priority;
|
||||
}
|
||||
|
||||
@AntMessage.Priority
|
||||
final int getPriority() {
|
||||
return myPriority;
|
||||
}
|
||||
@@ -652,7 +655,7 @@ public final class AntBuildMessageView extends JPanel implements DataProvider, O
|
||||
|
||||
private static final class StartBuildCommand extends MessageCommand {
|
||||
StartBuildCommand(String buildName) {
|
||||
super(new AntMessage(MessageType.BUILD, 0, buildName, null, 0, 0));
|
||||
super(new AntMessage(MessageType.BUILD, PRIORITY_ERR, buildName, null, 0, 0));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -663,7 +666,7 @@ public final class AntBuildMessageView extends JPanel implements DataProvider, O
|
||||
|
||||
private static final class BuildFailedCommand extends MessageCommand {
|
||||
BuildFailedCommand(String buildName) {
|
||||
super(new AntMessage(MessageType.ERROR, 0, AntBundle.message("cannot.start.build.name.error.message", buildName), null, 0, 0));
|
||||
super(new AntMessage(MessageType.ERROR, PRIORITY_ERR, AntBundle.message("cannot.start.build.name.error.message", buildName), null, 0, 0));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -676,7 +679,7 @@ public final class AntBuildMessageView extends JPanel implements DataProvider, O
|
||||
private final String myFinishStatusText;
|
||||
|
||||
FinishBuildCommand(String finishStatusText) {
|
||||
super(0);
|
||||
super(PRIORITY_ERR);
|
||||
myFinishStatusText = finishStatusText;
|
||||
}
|
||||
|
||||
@@ -688,7 +691,7 @@ public final class AntBuildMessageView extends JPanel implements DataProvider, O
|
||||
|
||||
private static final class StartTargetCommand extends MessageCommand {
|
||||
StartTargetCommand(String targetName) {
|
||||
super(new AntMessage(MessageType.TARGET, 0, targetName, null, 0, 0));
|
||||
super(new AntMessage(MessageType.TARGET, PRIORITY_ERR, targetName, null, 0, 0));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -699,7 +702,7 @@ public final class AntBuildMessageView extends JPanel implements DataProvider, O
|
||||
|
||||
private static final class FinishTargetCommand extends LogCommand {
|
||||
FinishTargetCommand() {
|
||||
super(0);
|
||||
super(PRIORITY_ERR);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -710,7 +713,7 @@ public final class AntBuildMessageView extends JPanel implements DataProvider, O
|
||||
|
||||
private static final class StartTaskCommand extends MessageCommand {
|
||||
StartTaskCommand(String taskName) {
|
||||
super(new AntMessage(MessageType.TASK, 0, taskName, null, 0, 0));
|
||||
super(new AntMessage(MessageType.TASK, PRIORITY_ERR, taskName, null, 0, 0));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -721,7 +724,7 @@ public final class AntBuildMessageView extends JPanel implements DataProvider, O
|
||||
|
||||
private static final class FinishTaskCommand extends LogCommand {
|
||||
FinishTaskCommand() {
|
||||
super(0);
|
||||
super(PRIORITY_ERR);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user