Merge remote-tracking branch 'origin/master'

This commit is contained in:
Eldar Abusalimov
2018-11-07 15:08:12 +03:00
128 changed files with 1460 additions and 636 deletions
+2
View File
@@ -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).
+5 -2
View File
@@ -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
View File
@@ -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;
}
}
@@ -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()) {
@@ -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;
@@ -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);
}
@@ -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();
}
}
}
@@ -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,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,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,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,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,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,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 @@
// "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 @@
// "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,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,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,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,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,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,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,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,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 @@
// "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 @@
// "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,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 " "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,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,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 " "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,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,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;
@@ -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();
}
@@ -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);
}
}
@@ -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
View File
@@ -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
@@ -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);
@@ -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();
}
}
@@ -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
@@ -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) {
}
@@ -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 {
@@ -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;
}
}
@@ -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);
}
@@ -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;
}
}
@@ -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;
}
}
}
}
@@ -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();
}
}
@@ -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