Merge remote-tracking branch 'origin/master'

This commit is contained in:
Dmitry Trofimov
2016-09-29 16:13:44 +02:00
561 changed files with 3251 additions and 3217 deletions
@@ -330,9 +330,11 @@ final class ImageEditorUI extends JPanel implements DataProvider, CopyProvider,
EditorOptions editorOptions = options.getEditorOptions();
ZoomOptions zoomOptions = editorOptions.getZoomOptions();
if (zoomOptions.isWheelZooming() && e.isControlDown()) {
if (e.getWheelRotation() < 0) {
int rotation = e.getWheelRotation();
if (rotation < 0) {
zoomModel.zoomOut();
} else {
}
else if (rotation > 0) {
zoomModel.zoomIn();
}
e.consume();
@@ -52,8 +52,9 @@ import org.jetbrains.org.objectweb.asm.MethodVisitor;
import org.jetbrains.org.objectweb.asm.Opcodes;
import java.util.Collections;
import java.util.Deque;
import java.util.LinkedList;
import java.util.List;
import java.util.Stack;
/**
* User: Alexander Podkhalyuzin
@@ -144,14 +145,14 @@ public class JavaSmartStepIntoHandler extends JvmSmartStepIntoHandler {
final Ref<TextRange> textRange = new Ref<>(lineRange);
final PsiElementVisitor methodCollector = new JavaRecursiveElementVisitor() {
final Stack<PsiMethod> myContextStack = new Stack<>();
final Stack<String> myParamNameStack = new Stack<>();
final Deque<PsiMethod> myContextStack = new LinkedList<>();
final Deque<String> myParamNameStack = new LinkedList<>();
private int myNextLambdaExpressionOrdinal = 0;
private boolean myInsideLambda = false;
@Nullable
private String getCurrentParamName() {
return myParamNameStack.isEmpty() ? null : myParamNameStack.peek();
return myParamNameStack.peekFirst();
}
@Override
@@ -257,7 +258,7 @@ public class JavaSmartStepIntoHandler extends JvmSmartStepIntoHandler {
}
public void visitExpressionList(PsiExpressionList expressionList) {
final PsiMethod psiMethod = myContextStack.isEmpty()? null : myContextStack.peek();
PsiMethod psiMethod = myContextStack.peekFirst();
if (psiMethod != null) {
final String methodName = psiMethod.getName();
final PsiExpression[] expressions = expressionList.getExpressions();
@@ -360,15 +360,13 @@ public class LocalVariablesUtil {
private final MultiMap<Integer, String> myNames;
private int myCurrentSlotIndex;
private final PsiElement myElement;
private final Stack<Integer> myIndexStack;
private final Deque<Integer> myIndexStack = new LinkedList<>();
private boolean myReached = false;
public LocalVariableNameFinder(int startSlot, MultiMap<Integer, String> names, PsiElement element) {
myNames = names;
myCurrentSlotIndex = startSlot;
myElement = element;
myIndexStack = new Stack<>();
}
private boolean shouldVisit(PsiElement scope) {
@@ -165,11 +165,12 @@ public abstract class AbstractPatternBasedConfigurationProducer<T extends Module
}
else {
final Editor editor = CommonDataKeys.EDITOR.getData(dataContext);
PsiElement element = null;
if (editor != null) {
final PsiFile editorFile = CommonDataKeys.PSI_FILE.getData(dataContext);
final List<Caret> allCarets = editor.getCaretModel().getAllCarets();
if (allCarets.size() > 1) {
final PsiFile editorFile = CommonDataKeys.PSI_FILE.getData(dataContext);
if (editorFile != null) {
if (editorFile != null) {
if (allCarets.size() > 1) {
final Set<PsiMethod> methods = new LinkedHashSet<>();
for (Caret caret : allCarets) {
ContainerUtil.addIfNotNull(methods, PsiTreeUtil.getParentOfType(editorFile.findElementAt(caret.getOffset()), PsiMethod.class));
@@ -178,9 +179,16 @@ public abstract class AbstractPatternBasedConfigurationProducer<T extends Module
return collectTestMembers(methods.toArray(PsiElement.EMPTY_ARRAY), checkAbstract, checkIsTest, processor, classes);
}
}
else {
element = editorFile.findElementAt(editor.getCaretModel().getOffset());
}
}
}
final PsiElement element = CommonDataKeys.PSI_ELEMENT.getData(dataContext);
if (element == null) {
element = CommonDataKeys.PSI_ELEMENT.getData(dataContext);
}
final VirtualFile[] files = CommonDataKeys.VIRTUAL_FILE_ARRAY.getData(dataContext);
if (files != null) {
Project project = CommonDataKeys.PROJECT.getData(dataContext);
@@ -65,6 +65,7 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
private JavaSdkVersion myJavaSdkVersion;
@SuppressWarnings("StatefulEp") private PsiFile myFile;
@SuppressWarnings("StatefulEp") private PsiJavaModule myJavaModule;
// map codeBlock->List of PsiReferenceExpression of uninitialized final variables
private final Map<PsiElement, Collection<PsiReferenceExpression>> myUninitializedVarProblems = new THashMap<>();
@@ -155,13 +156,9 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
@Override
public boolean analyze(@NotNull PsiFile file, boolean updateWholeFile, @NotNull HighlightInfoHolder holder, @NotNull Runnable highlight) {
myFile = file;
myHolder = Holder.CHECK_ELEMENT_LEVEL ? new CheckLevelHighlightInfoHolder(file, holder) : holder;
boolean success = true;
try {
myLanguageLevel = PsiUtil.getLanguageLevel(file);
myJavaSdkVersion = notNull(JavaVersionService.getInstance().getJavaSdkVersion(file), JavaSdkVersion.fromLanguageLevel(myLanguageLevel));
prepare(Holder.CHECK_ELEMENT_LEVEL ? new CheckLevelHighlightInfoHolder(file, holder) : holder, file);
if (updateWholeFile) {
ProgressIndicator progress = ProgressManager.getInstance().getProgressIndicator();
if (progress == null) throw new IllegalStateException("Must be run under progress");
@@ -193,6 +190,7 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
myReassignedParameters.clear();
myRefCountHolder = null;
myJavaModule = null;
myFile = null;
myHolder = null;
myDuplicateMethods.clear();
@@ -202,6 +200,18 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
return success;
}
protected void prepareToRunAsInspection(@NotNull HighlightInfoHolder holder) {
prepare(holder, holder.getContextFile());
}
private void prepare(HighlightInfoHolder holder, PsiFile file) {
myHolder = holder;
myFile = file;
myLanguageLevel = PsiUtil.getLanguageLevel(file);
myJavaSdkVersion = notNull(JavaVersionService.getInstance().getJavaSdkVersion(file), JavaSdkVersion.fromLanguageLevel(myLanguageLevel));
myJavaModule = myLanguageLevel.isAtLeast(LanguageLevel.JDK_1_9) ? ModuleHighlightUtil.getModuleDescriptor(file) : null;
}
@Override
public void visitElement(PsiElement element) {
if (myRefCountHolder != null && myFile instanceof ServerPageFile) {
@@ -1030,8 +1040,14 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
@Override
public void visitReferenceElement(PsiJavaCodeReferenceElement ref) {
JavaResolveResult resolveResult = doVisitReferenceElement(ref);
if (resolveResult != null && !myHolder.hasErrorResults()) myHolder.add(GenericsHighlightUtil.checkRawOnParameterizedType(ref, resolveResult.getElement()));
JavaResolveResult result = doVisitReferenceElement(ref);
if (result != null) {
PsiElement resolved = result.getElement();
if (!myHolder.hasErrorResults()) myHolder.add(GenericsHighlightUtil.checkRawOnParameterizedType(ref, resolved));
if (!myHolder.hasErrorResults() && resolved != null && myJavaModule != null) {
myHolder.add(ModuleHighlightUtil.checkPackageAccessibility(ref, resolved, myJavaModule));
}
}
}
private JavaResolveResult doVisitReferenceElement(@NotNull PsiJavaCodeReferenceElement ref) {
@@ -1688,14 +1704,4 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
private HighlightInfo checkFeature(@NotNull PsiElement element, @NotNull Feature feature) {
return HighlightUtil.checkFeature(element, feature, myLanguageLevel, myFile);
}
protected void prepareToRunAsInspection(@NotNull HighlightInfoHolder holder) {
PsiFile file = holder.getContextFile();
JavaSdkVersion sdkVersion = JavaVersionService.getInstance().getJavaSdkVersion(file);
myHolder = holder;
myFile = file;
myLanguageLevel = PsiUtil.getLanguageLevel(file);
myJavaSdkVersion = sdkVersion != null ? sdkVersion : JavaSdkVersion.fromLanguageLevel(myLanguageLevel);
}
}
@@ -1,137 +0,0 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.codeInsight.daemon.impl.analysis;
import com.intellij.openapi.application.ReadAction;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.search.FilenameIndex;
import com.intellij.psi.util.CachedValueProvider.Result;
import com.intellij.psi.util.CachedValuesManager;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.MultiMap;
import com.intellij.util.graph.DFSTBuilder;
import com.intellij.util.graph.Graph;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import static com.intellij.psi.PsiJavaModule.MODULE_INFO_FILE;
import static com.intellij.psi.SyntaxTraverser.psiTraverser;
import static com.intellij.psi.util.PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT;
public class JavaModuleGraphBuilder {
private JavaModuleGraphBuilder() { }
@Nullable
public static Graph<PsiJavaModule> getOrBuild(@NotNull Project project) {
return CachedValuesManager.getManager(project).getCachedValue(project, () -> {
Graph<PsiJavaModule> graph = ReadAction.compute(() -> build(project));
return Result.create(graph, OUT_OF_CODE_BLOCK_MODIFICATION_COUNT);
});
}
@Nullable
public static Collection<PsiJavaModule> findCycle(@NotNull Graph<PsiJavaModule> graph, @NotNull PsiJavaModule module) {
return ((JavaModuleGraph)graph).myCycles.stream().filter(set -> set.contains(module)).findFirst().orElse(null);
}
// Discovers relationships between Java modules in the project.
// Library/JDK modules are excluded - in assumption there can't be any lib -> src dependencies.
private static Graph<PsiJavaModule> build(Project project) {
Set<PsiJavaModule> projectModules = ContainerUtil.newHashSet();
for (Module module : ModuleManager.getInstance(project).getModules()) {
Collection<VirtualFile> files = FilenameIndex.getVirtualFilesByName(project, MODULE_INFO_FILE, module.getModuleScope(false));
if (files.size() > 1) return null; // aborts the process when there are incorrect modules in the project
VirtualFile vFile = ContainerUtil.getFirstItem(files);
if (vFile != null) {
PsiFile psiFile = PsiManager.getInstance(project).findFile(vFile);
if (psiFile instanceof PsiJavaFile) {
PsiJavaModule moduleDeclaration = ((PsiJavaFile)psiFile).getModuleDeclaration();
if (moduleDeclaration != null) {
projectModules.add(moduleDeclaration);
}
}
}
}
MultiMap<PsiJavaModule, PsiJavaModule> relations = MultiMap.create();
for (PsiJavaModule moduleDeclaration : projectModules) {
for (PsiRequiresStatement statement : psiTraverser().children(moduleDeclaration).filter(PsiRequiresStatement.class)) {
PsiJavaModule dependency = resolveDependency(statement);
if (dependency != null && projectModules.contains(dependency)) {
relations.putValue(moduleDeclaration, dependency);
}
}
}
return new JavaModuleGraph(relations);
}
private static PsiJavaModule resolveDependency(PsiRequiresStatement statement) {
PsiJavaModuleReferenceElement refElement = statement.getReferenceElement();
if (refElement != null) {
PsiPolyVariantReference ref = refElement.getReference();
if (ref != null) {
ResolveResult[] results = ref.multiResolve(true);
if (results.length == 1) {
PsiElement target = results[0].getElement();
if (target instanceof PsiJavaModule) {
return (PsiJavaModule)target;
}
}
}
}
return null;
}
private static class JavaModuleGraph implements Graph<PsiJavaModule> {
private final MultiMap<PsiJavaModule, PsiJavaModule> myMap;
private final List<Set<PsiJavaModule>> myCycles;
public JavaModuleGraph(MultiMap<PsiJavaModule, PsiJavaModule> map) {
myMap = map;
DFSTBuilder<PsiJavaModule> builder = new DFSTBuilder<>(this);
myCycles = builder.getComponents().stream()
.map(ContainerUtil::newLinkedHashSet)
.collect(Collectors.toList());
}
@Override
public Collection<PsiJavaModule> getNodes() {
return myMap.keySet();
}
@Override
public Iterator<PsiJavaModule> getIn(PsiJavaModule n) {
return ContainerUtil.emptyIterator();
}
@Override
public Iterator<PsiJavaModule> getOut(PsiJavaModule n) {
return myMap.get(n).iterator();
}
}
}
@@ -0,0 +1,211 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.codeInsight.daemon.impl.analysis;
import com.intellij.openapi.application.ReadAction;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.search.FilenameIndex;
import com.intellij.psi.util.CachedValueProvider.Result;
import com.intellij.psi.util.CachedValuesManager;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.MultiMap;
import com.intellij.util.graph.DFSTBuilder;
import com.intellij.util.graph.Graph;
import com.intellij.util.graph.GraphGenerator;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.stream.Collectors;
import static com.intellij.psi.PsiJavaModule.MODULE_INFO_FILE;
import static com.intellij.psi.SyntaxTraverser.psiTraverser;
import static com.intellij.psi.util.PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT;
public class JavaModuleGraphUtil {
private JavaModuleGraphUtil() { }
@Nullable
public static Collection<PsiJavaModule> findCycle(@NotNull PsiJavaModule module) {
Project project = module.getProject();
List<Set<PsiJavaModule>> cycles = CachedValuesManager.getManager(project).getCachedValue(project, () ->
Result.create(ReadAction.compute(() -> findCycles(project)), OUT_OF_CODE_BLOCK_MODIFICATION_COUNT));
return ContainerUtil.find(cycles, set -> set.contains(module));
}
public static boolean reads(@NotNull PsiJavaModule source, @NotNull PsiJavaModule destination) {
Project project = source.getProject();
RequiresGraph graph = CachedValuesManager.getManager(project).getCachedValue(project, () ->
Result.create(ReadAction.compute(() -> buildRequiresGraph(project)), OUT_OF_CODE_BLOCK_MODIFICATION_COUNT));
return graph.reads(source, destination);
}
// Looks for cycles between Java modules in the project sources.
// Library/JDK modules are excluded - in assumption there can't be any lib -> src dependencies.
// Module references are resolved "globally" (i.e., without taking project dependencies into account).
private static List<Set<PsiJavaModule>> findCycles(Project project) {
Set<PsiJavaModule> projectModules = ContainerUtil.newHashSet();
for (Module module : ModuleManager.getInstance(project).getModules()) {
Collection<VirtualFile> files = FilenameIndex.getVirtualFilesByName(project, MODULE_INFO_FILE, module.getModuleScope(false));
if (files.size() > 1) return Collections.emptyList(); // aborts the process when there are incorrect modules in the project
Optional.ofNullable(ContainerUtil.getFirstItem(files))
.map(PsiManager.getInstance(project)::findFile)
.map(f -> f instanceof PsiJavaFile ? ((PsiJavaFile)f).getModuleDeclaration() : null)
.ifPresent(projectModules::add);
}
if (!projectModules.isEmpty()) {
MultiMap<PsiJavaModule, PsiJavaModule> relations = MultiMap.create();
for (PsiJavaModule module : projectModules) {
for (PsiRequiresStatement statement : psiTraverser().children(module).filter(PsiRequiresStatement.class)) {
Optional.ofNullable(statement.getReferenceElement())
.map(PsiJavaModuleReferenceElement::getReference)
.map(ref -> ref.multiResolve(true))
.map(a -> a.length == 1 ? a[0].getElement() : null)
.map(e -> e instanceof PsiJavaModule ? (PsiJavaModule)e : null)
.filter(projectModules::contains)
.ifPresent(dependency -> relations.putValue(module, dependency));
}
}
if (!relations.isEmpty()) {
Graph<PsiJavaModule> graph = new SourceSemiGraph(relations);
DFSTBuilder<PsiJavaModule> builder = new DFSTBuilder<>(graph);
Collection<Collection<PsiJavaModule>> components = builder.getComponents();
if (!components.isEmpty()) {
return components.stream().map(ContainerUtil::newLinkedHashSet).collect(Collectors.toList());
}
}
}
return Collections.emptyList();
}
// Starting from source modules, collects all module dependencies in the project.
// The resulting graph is used for tracing readability.
private static RequiresGraph buildRequiresGraph(Project project) {
MultiMap<PsiJavaModule, PsiJavaModule> relations = MultiMap.create();
Set<String> publicEdges = ContainerUtil.newTroveSet();
for (Module module : ModuleManager.getInstance(project).getModules()) {
Collection<VirtualFile> files = FilenameIndex.getVirtualFilesByName(project, MODULE_INFO_FILE, module.getModuleScope(false));
Optional.ofNullable(ContainerUtil.getFirstItem(files))
.map(PsiManager.getInstance(project)::findFile)
.map(f -> f instanceof PsiJavaFile ? ((PsiJavaFile)f).getModuleDeclaration() : null)
.ifPresent(m -> visit(m, relations, publicEdges));
}
GraphGenerator<PsiJavaModule> graph = GraphGenerator.create(new RequiresSemiGraph(relations));
return new RequiresGraph(graph, publicEdges);
}
private static void visit(PsiJavaModule module, MultiMap<PsiJavaModule, PsiJavaModule> relations, Set<String> publicEdges) {
if (!relations.containsKey(module)) {
relations.putValues(module, Collections.emptyList());
for (PsiRequiresStatement statement : psiTraverser().children(module).filter(PsiRequiresStatement.class)) {
Optional.ofNullable(statement.getReferenceElement())
.map(PsiJavaModuleReferenceElement::getReference)
.map(PsiReference::resolve)
.map(e -> e instanceof PsiJavaModule ? (PsiJavaModule)e : null)
.ifPresent(dependency -> {
relations.putValue(module, dependency);
if (isPublic(statement)) publicEdges.add(RequiresGraph.key(dependency, module));
visit(dependency, relations, publicEdges);
});
}
}
}
private static boolean isPublic(PsiRequiresStatement statement) {
for (PsiElement child = statement.getFirstChild(); child != null; child = child.getNextSibling()) {
if (PsiUtil.isJavaToken(child, JavaTokenType.PUBLIC_KEYWORD)) return true;
if (child instanceof PsiJavaModuleReferenceElement) break;
}
return false;
}
private static class RequiresGraph {
private final Graph<PsiJavaModule> myGraph;
private final Set<String> myPublicEdges;
public RequiresGraph(Graph<PsiJavaModule> graph, Set<String> publicEdges) {
myGraph = graph;
myPublicEdges = publicEdges;
}
public boolean reads(PsiJavaModule source, PsiJavaModule destination) {
Iterator<PsiJavaModule> directReaders = myGraph.getOut(destination);
while (directReaders.hasNext()) {
PsiJavaModule next = directReaders.next();
if (source.equals(next) || myPublicEdges.contains(key(destination, next)) && reads(source, next)) {
return true;
}
}
return false;
}
public static String key(PsiJavaModule module, PsiJavaModule exporter) {
return module.getModuleName() + '/' + exporter.getModuleName();
}
}
//<editor-fold desc="Helpers.">
private static class SourceSemiGraph implements Graph<PsiJavaModule> {
private final MultiMap<PsiJavaModule, PsiJavaModule> myMap;
public SourceSemiGraph(MultiMap<PsiJavaModule, PsiJavaModule> map) {
myMap = map;
}
@Override
public Collection<PsiJavaModule> getNodes() {
return myMap.keySet();
}
@Override
public Iterator<PsiJavaModule> getIn(PsiJavaModule n) {
throw new UnsupportedOperationException();
}
@Override
public Iterator<PsiJavaModule> getOut(PsiJavaModule n) {
return myMap.get(n).iterator();
}
}
private static class RequiresSemiGraph implements GraphGenerator.SemiGraph<PsiJavaModule> {
private final MultiMap<PsiJavaModule, PsiJavaModule> myMap;
public RequiresSemiGraph(MultiMap<PsiJavaModule, PsiJavaModule> map) {
myMap = map;
}
@Override
public Collection<PsiJavaModule> getNodes() {
return myMap.keySet();
}
@Override
public Iterator<PsiJavaModule> getIn(PsiJavaModule n) {
return myMap.get(n).iterator();
}
}
//</editor-fold>
}
@@ -113,11 +113,8 @@ public class LambdaHighlightingUtil {
final PsiClass aClass = resolveResult.getElement();
if (aClass != null) {
if (aClass instanceof PsiTypeParameter) return null; //should be logged as cyclic inference
final List<HierarchicalMethodSignature> signatures = LambdaUtil.findFunctionCandidates(aClass);
if (signatures != null && signatures.size() == 1) {
final MethodSignature functionalMethod = signatures.get(0);
if (functionalMethod.getTypeParameters().length > 0) return "Target method is generic";
}
MethodSignature functionalMethod = LambdaUtil.getFunction(aClass);
if (functionalMethod != null && functionalMethod.getTypeParameters().length > 0) return "Target method is generic";
if (checkReturnTypeApplicable(resolveResult, aClass)) {
return "No instance of type " + functionalInterfaceType.getPresentableText() + " exists so that lambda expression can be type-checked";
}
@@ -37,7 +37,6 @@ import com.intellij.psi.util.PsiUtil;
import com.intellij.util.ObjectUtils;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.JBIterable;
import com.intellij.util.graph.Graph;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.PropertyKey;
@@ -55,6 +54,34 @@ import static com.intellij.psi.PsiJavaModule.MODULE_INFO_FILE;
import static com.intellij.psi.SyntaxTraverser.psiTraverser;
public class ModuleHighlightUtil {
@Nullable
static PsiJavaModule getModuleDescriptor(@NotNull PsiElement element) {
VirtualFile file = Optional.of(element)
.map(e -> e instanceof PsiFileSystemItem ? (PsiFileSystemItem)e : e.getContainingFile())
.map(PsiFileSystemItem::getVirtualFile)
.orElse(null);
if (file == null) return null;
Project project = element.getProject();
ProjectFileIndex index = ProjectFileIndex.SERVICE.getInstance(project);
if (element instanceof PsiCompiledElement) {
return Optional.ofNullable(index.getClassRootForFile(file))
.map(r -> r.findChild(PsiJavaModule.MODULE_INFO_CLS_FILE))
.map(PsiManager.getInstance(project)::findFile)
.map(f -> f instanceof PsiJavaFile ? ((PsiJavaFile)f).getModuleDeclaration() : null)
.orElse(null);
}
else {
Module module = index.getModuleForFile(file);
return Optional.ofNullable(module)
.map(m -> FilenameIndex.getVirtualFilesByName(project, MODULE_INFO_FILE, m.getModuleScope(false)))
.map(c -> c.size () == 1 ? c.iterator().next() : null)
.map(PsiManager.getInstance(project)::findFile)
.map(f -> f instanceof PsiJavaFile ? ((PsiJavaFile)f).getModuleDeclaration() : null)
.orElse(null);
}
}
@Nullable
static HighlightInfo checkFileName(@NotNull PsiJavaModule element, @NotNull PsiFile file) {
if (!MODULE_INFO_FILE.equals(file.getName())) {
@@ -198,15 +225,12 @@ public class ModuleHighlightUtil {
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(refElement).description(message).create();
}
else {
Graph<PsiJavaModule> graph = JavaModuleGraphBuilder.getOrBuild(target.getProject());
if (graph != null) {
Collection<PsiJavaModule> cycle = JavaModuleGraphBuilder.findCycle(graph, (PsiJavaModule)target);
if (cycle != null && cycle.contains(container)) {
Stream<String> stream = cycle.stream().map(PsiJavaModule::getModuleName);
if (ApplicationManager.getApplication().isUnitTestMode()) stream = stream.sorted();
String message = JavaErrorMessages.message("module.cyclic.dependence", stream.collect(Collectors.joining(", ")));
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(refElement).description(message).create();
}
Collection<PsiJavaModule> cycle = JavaModuleGraphUtil.findCycle((PsiJavaModule)target);
if (cycle != null && cycle.contains(container)) {
Stream<String> stream = cycle.stream().map(PsiJavaModule::getModuleName);
if (ApplicationManager.getApplication().isUnitTestMode()) stream = stream.sorted();
String message = JavaErrorMessages.message("module.cyclic.dependence", stream.collect(Collectors.joining(", ")));
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(refElement).description(message).create();
}
}
}
@@ -311,6 +335,70 @@ public class ModuleHighlightUtil {
return null;
}
@Nullable
static HighlightInfo checkPackageAccessibility(@NotNull PsiJavaCodeReferenceElement ref,
@NotNull PsiElement target,
@NotNull PsiJavaModule refModule) {
Module module = ModuleUtilCore.findModuleForPsiElement(ref);
if (module != null) {
if (target instanceof PsiClass && !(target instanceof PsiCompiledElement) && module != ModuleUtilCore.findModuleForPsiElement(target)) {
PsiElement targetFile = target.getParent();
if (targetFile instanceof PsiClassOwner) {
PsiJavaModule targetModule = getModuleDescriptor(target);
String packageName = ((PsiClassOwner)targetFile).getPackageName();
return checkPackageAccessibility(ref, refModule, targetModule, packageName);
}
}
else if (target instanceof PsiPackage) {
PsiElement refImport = ref.getParent();
if (refImport instanceof PsiImportStatementBase && ((PsiImportStatementBase)refImport).isOnDemand()) {
PsiDirectory[] dirs = ((PsiPackage)target).getDirectories(module.getModuleWithDependenciesAndLibrariesScope(false));
if (dirs.length == 1 && ModuleUtilCore.findModuleForPsiElement(dirs[0]) != module) {
PsiJavaModule targetModule = getModuleDescriptor(dirs[0]);
String packageName = ((PsiPackage)target).getQualifiedName();
return checkPackageAccessibility(ref, refModule, targetModule, packageName);
}
}
}
}
return null;
}
private static HighlightInfo checkPackageAccessibility(PsiJavaCodeReferenceElement ref,
PsiJavaModule refModule,
PsiJavaModule targetModule,
String packageName) {
if (targetModule == null) {
String message = JavaErrorMessages.message("module.package.on.classpath");
return HighlightInfo.newHighlightInfo(HighlightInfoType.WRONG_REF).range(ref).description(message).create();
}
String refModuleName = refModule.getModuleName();
String requiredName = targetModule.getModuleName();
if (!(targetModule instanceof PsiCompiledElement) && !isExported(targetModule, packageName)) {
String message = JavaErrorMessages.message("module.package.not.exported", requiredName, packageName, refModuleName);
return HighlightInfo.newHighlightInfo(HighlightInfoType.WRONG_REF).range(ref).description(message).create();
}
if (!(PsiJavaModule.JAVA_BASE.equals(requiredName) || JavaModuleGraphUtil.reads(refModule, targetModule))) {
String message = JavaErrorMessages.message("module.not.in.requirements", refModuleName, requiredName);
return HighlightInfo.newHighlightInfo(HighlightInfoType.WRONG_REF).range(ref).description(message).create();
}
return null;
}
private static boolean isExported(PsiJavaModule module, String packageName) {
for (PsiExportsStatement statement : psiTraverser().children(module).filter(PsiExportsStatement.class)) {
String exportedName = Optional.ofNullable(statement.getPackageReference()).map(ModuleHighlightUtil::refText).orElse("");
if (packageName.equals(exportedName)) {
return true;
}
}
return false;
}
private static HighlightInfo moduleResolveError(PsiJavaModuleReferenceElement refElement, PsiPolyVariantReference ref) {
boolean missing = ref.multiResolve(true).length == 0;
String message = JavaErrorMessages.message(missing ? "module.not.found" : "module.not.on.path", refElement.getReferenceText());
@@ -359,16 +359,10 @@ public class AnonymousCanBeLambdaInspection extends BaseJavaBatchLocalInspection
}
private static class ReplaceWithLambdaFix implements LocalQuickFix, HighPriorityAction {
@NotNull
@Override
public String getName() {
return "Replace with lambda";
}
@NotNull
@Override
public String getFamilyName() {
return getName();
return "Replace with lambda";
}
@Override
@@ -114,17 +114,11 @@ public class AnonymousCanBeMethodReferenceInspection extends BaseJavaBatchLocalI
private static class ReplaceWithMethodRefFix implements LocalQuickFix {
@NotNull
@Override
public String getName() {
public String getFamilyName() {
return "Replace with method reference";
}
@NotNull
@Override
public String getFamilyName() {
return getName();
}
@Override
@Override
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
final PsiElement element = descriptor.getPsiElement();
if (element instanceof PsiNewExpression) {
@@ -91,12 +91,6 @@ public class BlockMarkerCommentsInspection extends BaseJavaBatchLocalInspectionT
}
if (MARKER_PATTERN.accepts(element)) {
holder.registerProblem(element, "Redundant block marker", new LocalQuickFix() {
@NotNull
@Override
public String getName() {
return getFamilyName();
}
@NotNull
@Override
public String getFamilyName() {
@@ -82,16 +82,10 @@ public class ExplicitTypeCanBeDiamondInspection extends BaseJavaBatchLocalInspec
}
private static class ReplaceWithDiamondFix implements LocalQuickFix, HighPriorityAction {
@NotNull
@Override
public String getName() {
return "Replace with <>";
}
@NotNull
@Override
public String getFamilyName() {
return getName();
return "Replace with <>";
}
@Override
@@ -587,16 +587,10 @@ public class LambdaCanBeMethodReferenceInspection extends BaseJavaBatchLocalInsp
}
private static class ReplaceWithMethodRefFix implements LocalQuickFix {
@NotNull
@Override
public String getName() {
return "Replace lambda with method reference";
}
@NotNull
@Override
public String getFamilyName() {
return getName();
return "Replace lambda with method reference";
}
@Override
@@ -92,16 +92,10 @@ public class PossibleHeapPollutionVarargsInspection extends BaseJavaBatchLocalIn
}
private static class AnnotateAsSafeVarargsQuickFix implements LocalQuickFix {
@NotNull
@Override
public String getName() {
return "Annotate as @SafeVarargs";
}
@NotNull
@Override
public String getFamilyName() {
return getName();
return "Annotate as @SafeVarargs";
}
@Override
@@ -117,16 +111,10 @@ public class PossibleHeapPollutionVarargsInspection extends BaseJavaBatchLocalIn
}
private static class MakeFinalAndAnnotateQuickFix implements LocalQuickFix {
@NotNull
@Override
public String getName() {
return "Make final and annotate as @SafeVarargs";
}
@NotNull
@Override
public String getFamilyName() {
return getName();
return "Make final and annotate as @SafeVarargs";
}
@Override
@@ -124,16 +124,10 @@ public class RedundantLambdaCodeBlockInspection extends BaseJavaBatchLocalInspec
}
private static class ReplaceWithExprFix implements LocalQuickFix, HighPriorityAction {
@NotNull
@Override
public String getName() {
return "Replace with expression lambda";
}
@NotNull
@Override
public String getFamilyName() {
return getName();
return "Replace with expression lambda";
}
@Override
@@ -40,7 +40,7 @@ public class RemoveAnnotationQuickFix implements LocalQuickFix {
@Override
@NotNull
public String getName() {
public String getFamilyName() {
return CodeInsightBundle.message("remove.annotation");
}
@@ -58,10 +58,4 @@ public class RemoveAnnotationQuickFix implements LocalQuickFix {
ExternalAnnotationsManager.getInstance(project).deannotate(myListOwner, myAnnotation.getQualifiedName());
}
}
@Override
@NotNull
public String getFamilyName() {
return getName();
}
}
@@ -53,13 +53,6 @@ public class TrivialMethodReferenceInspection extends BaseJavaBatchLocalInspecti
}
private static class ReplaceMethodRefWithQualifierFix implements LocalQuickFix {
@Nls
@NotNull
@Override
public String getName() {
return getFamilyName();
}
@Nls
@NotNull
@Override
@@ -269,16 +269,10 @@ public class CanBeFinalInspection extends GlobalJavaBatchInspectionTool {
myManager = manager;
}
@Override
@NotNull
public String getName() {
return QUICK_FIX_NAME;
}
@Override
@NotNull
public String getFamilyName() {
return getName();
return QUICK_FIX_NAME;
}
@Override
@@ -29,12 +29,6 @@ public class RemoveElementQuickFix implements LocalQuickFix {
myName = name;
}
@NotNull
@Override
public String getName() {
return myName;
}
@NotNull
@Override
public String getFamilyName() {
@@ -833,7 +833,7 @@ public class DataFlowInspectionBase extends BaseJavaBatchLocalInspectionTool {
private static class RedundantInstanceofFix implements LocalQuickFix {
@Override
@NotNull
public String getName() {
public String getFamilyName() {
return InspectionsBundle.message("inspection.data.flow.redundant.instanceof.quickfix");
}
@@ -852,12 +852,6 @@ public class DataFlowInspectionBase extends BaseJavaBatchLocalInspectionTool {
}
}
}
@Override
@NotNull
public String getFamilyName() {
return getName();
}
}
@@ -901,7 +895,7 @@ public class DataFlowInspectionBase extends BaseJavaBatchLocalInspectionTool {
}
Collection<PsiCall> getAlwaysFailingCalls() {
return StreamEx.of(myFailingCalls.keySet()).filter(this::isAlwaysFailing).map(MethodCallInstruction::getCallExpression).toList();
return StreamEx.ofKeys(myFailingCalls, v -> v).map(MethodCallInstruction::getCallExpression).toList();
}
@Override
@@ -911,7 +905,7 @@ public class DataFlowInspectionBase extends BaseJavaBatchLocalInspectionTool {
DfaInstructionState[] states = super.visitMethodCall(instruction, runner, memState);
if (hasNonTrivialFailingContracts(instruction)) {
boolean allFail = Arrays.stream(states).allMatch(s -> s.getMemoryState().peek() == runner.getFactory().getConstFactory().getContractFail());
myFailingCalls.put(instruction, allFail && isAlwaysFailing(instruction));
myFailingCalls.merge(instruction, allFail, Boolean::logicalAnd);
}
return states;
}
@@ -926,10 +920,6 @@ public class DataFlowInspectionBase extends BaseJavaBatchLocalInspectionTool {
Arrays.stream(contract.arguments).anyMatch(v -> v != MethodContract.ValueConstraint.ANY_VALUE);
}
private boolean isAlwaysFailing(MethodCallInstruction instruction) {
return !Boolean.FALSE.equals(myFailingCalls.get(instruction));
}
@Override
protected boolean checkNotNullable(DfaMemoryState state, DfaValue value, NullabilityProblem problem, PsiElement anchor) {
boolean ok = super.checkNotNullable(state, value, problem, anchor);
@@ -100,13 +100,6 @@ public class DfaOptionalSupport {
myClearArguments = clearArguments;
}
@Nls
@NotNull
@Override
public String getName() {
return getFamilyName();
}
@NotNull
@Override
public String getFamilyName() {
@@ -484,13 +484,6 @@ public class Java8CollectionsApiInspection extends BaseJavaBatchLocalInspectionT
}
private static class ReplaceWithListSortFix implements LocalQuickFix {
@Nls
@NotNull
@Override
public String getName() {
return getFamilyName();
}
@Nls
@NotNull
@Override
@@ -584,13 +577,6 @@ public class Java8CollectionsApiInspection extends BaseJavaBatchLocalInspectionT
}
private static class ReplaceWithRemoveIfQuickFix implements LocalQuickFix {
@Nls
@NotNull
@Override
public String getName() {
return getFamilyName();
}
@Nls
@NotNull
@Override
@@ -325,7 +325,7 @@ public class LocalCanBeFinal extends BaseJavaBatchLocalInspectionTool {
private static class AcceptSuggested implements LocalQuickFix {
@Override
@NotNull
public String getName() {
public String getFamilyName() {
return InspectionsBundle.message("inspection.can.be.final.accept.quickfix");
}
@@ -344,12 +344,6 @@ public class LocalCanBeFinal extends BaseJavaBatchLocalInspectionTool {
LOG.error(e);
}
}
@Override
@NotNull
public String getFamilyName() {
return getName();
}
}
@Override
@@ -167,7 +167,7 @@ public class RedundantTypeArgsInspection extends GenericsInspectionToolBase {
private static class MyQuickFixAction implements LocalQuickFix {
@Override
@NotNull
public String getName() {
public String getFamilyName() {
return InspectionsBundle.message("inspection.redundant.type.remove.quickfix");
}
@@ -186,12 +186,6 @@ public class RedundantTypeArgsInspection extends GenericsInspectionToolBase {
LOG.error(e);
}
}
@Override
@NotNull
public String getFamilyName() {
return getName();
}
}
//separate quickfix is needed to invalidate initial method reference
@@ -199,7 +193,7 @@ public class RedundantTypeArgsInspection extends GenericsInspectionToolBase {
private static class MyMethodReferenceFixAction implements LocalQuickFix {
@Override
@NotNull
public String getName() {
public String getFamilyName() {
return InspectionsBundle.message("inspection.redundant.type.remove.quickfix");
}
@@ -212,11 +206,5 @@ public class RedundantTypeArgsInspection extends GenericsInspectionToolBase {
expression.replace(createMethodReference(expression, typeElement));
}
}
@Override
@NotNull
public String getFamilyName() {
return getName();
}
}
}
@@ -43,16 +43,10 @@ class ChangeNullableDefaultsFix implements LocalQuickFix {
myNullableName = nullable;
}
@NotNull
@Override
public String getName() {
return "Make \"" + (myNotNullName != null ? myNotNullName : myNullableName) + "\" default annotation";
}
@NotNull
@Override
public String getFamilyName() {
return getName();
return "Make \"" + (myNotNullName != null ? myNotNullName : myNullableName) + "\" default annotation";
}
@Override
@@ -133,7 +133,7 @@ public class RedundantCastInspection extends GenericsInspectionToolBase {
private static class AcceptSuggested implements LocalQuickFix {
@Override
@NotNull
public String getName() {
public String getFamilyName() {
return InspectionsBundle.message("inspection.redundant.cast.remove.quickfix");
}
@@ -146,12 +146,6 @@ public class RedundantCastInspection extends GenericsInspectionToolBase {
RedundantCastUtil.removeCast(cast);
}
}
@Override
@NotNull
public String getFamilyName() {
return getName();
}
}
@Override
@@ -19,6 +19,7 @@ import com.intellij.codeInsight.FileModificationService;
import com.intellij.codeInspection.LambdaCanBeMethodReferenceInspection;
import com.intellij.codeInspection.LocalQuickFix;
import com.intellij.codeInspection.ProblemDescriptor;
import com.intellij.codeInspection.streamMigration.StreamApiMigrationInspection.InitializerUsageStatus;
import com.intellij.codeInspection.streamMigration.StreamApiMigrationInspection.Operation;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
@@ -72,14 +73,13 @@ abstract class MigrateToStreamFix implements LocalQuickFix {
PsiType expressionType) {
PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(project);
restoreComments(foreachStatement, foreachStatement.getBody());
if (StreamApiMigrationInspection.isDeclarationJustBefore(var, foreachStatement)) {
InitializerUsageStatus status = StreamApiMigrationInspection.getInitializerUsageStatus(var, foreachStatement);
if (status != InitializerUsageStatus.UNKNOWN) {
PsiExpression initializer = var.getInitializer();
if (ExpressionUtils.isZero(initializer)) {
PsiType type = var.getType();
String replacement = (type.equals(expressionType) ? "" : "(" + type.getCanonicalText() + ") ") + builder;
initializer.replace(elementFactory.createExpressionFromText(replacement, foreachStatement));
removeLoop(foreachStatement);
simplifyAndFormat(project, var);
replaceInitializer(foreachStatement, var, initializer, replacement, status);
return;
}
}
@@ -88,6 +88,27 @@ abstract class MigrateToStreamFix implements LocalQuickFix {
simplifyAndFormat(project, result);
}
static void replaceInitializer(PsiForeachStatement foreachStatement,
PsiVariable var,
PsiExpression initializer,
String replacement,
InitializerUsageStatus status) {
Project project = foreachStatement.getProject();
PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(project);
if(status == InitializerUsageStatus.DECLARED_JUST_BEFORE) {
initializer.replace(elementFactory.createExpressionFromText(replacement, foreachStatement));
removeLoop(foreachStatement);
simplifyAndFormat(project, var);
} else {
if(status == InitializerUsageStatus.AT_WANTED_PLACE_ONLY) {
initializer.delete();
}
PsiElement result =
foreachStatement.replace(elementFactory.createStatementFromText(var.getName() + " = " + replacement + ";", foreachStatement));
simplifyAndFormat(project, result);
}
}
static void simplifyAndFormat(@NotNull Project project, PsiElement result) {
if (result == null) return;
LambdaCanBeMethodReferenceInspection.replaceAllLambdasWithMethodReferences(result);
@@ -16,7 +16,9 @@
package com.intellij.codeInspection.streamMigration;
import com.intellij.codeInspection.ProblemDescriptor;
import com.intellij.codeInspection.streamMigration.StreamApiMigrationInspection.InitializerUsageStatus;
import com.intellij.codeInspection.streamMigration.StreamApiMigrationInspection.Operation;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
@@ -32,6 +34,8 @@ import java.util.List;
* @author Tagir Valeev
*/
class ReplaceWithCollectFix extends MigrateToStreamFix {
private static final Logger LOG = Logger.getInstance(ReplaceWithCollectFix.class);
final String myMethodName;
protected ReplaceWithCollectFix(String methodName) {
@@ -87,16 +91,18 @@ class ReplaceWithCollectFix extends MigrateToStreamFix {
final StringBuilder builder = generateStream(iteratedValue, operations);
final PsiExpression qualifierExpression = methodCallExpression.getMethodExpression().getQualifierExpression();
final PsiExpression initializer = StreamApiMigrationInspection
.extractReplaceableCollectionInitializer(qualifierExpression, foreachStatement);
if (initializer != null) {
String callText = builder.append(".collect(java.util.stream.Collectors.")
.append(createInitializerReplacementText(qualifierExpression.getType(), initializer))
.append(")").toString();
PsiElement result = initializer.replace(elementFactory.createExpressionFromText(callText, null));
simplifyAndFormat(project, result);
removeLoop(foreachStatement);
return;
final PsiLocalVariable variable = StreamApiMigrationInspection.extractCollectionVariable(qualifierExpression);
if (variable != null) {
InitializerUsageStatus status = StreamApiMigrationInspection.getInitializerUsageStatus(variable, foreachStatement);
if(status != InitializerUsageStatus.UNKNOWN) {
PsiExpression initializer = variable.getInitializer();
LOG.assertTrue(initializer != null);
String callText = builder.append(".collect(java.util.stream.Collectors.")
.append(createInitializerReplacementText(qualifierExpression.getType(), initializer))
.append(")").toString();
replaceInitializer(foreachStatement, variable, initializer, callText, status);
return;
}
}
final String qualifierText = qualifierExpression != null ? qualifierExpression.getText() + "." : "";
@@ -16,6 +16,7 @@
package com.intellij.codeInspection.streamMigration;
import com.intellij.codeInspection.ProblemDescriptor;
import com.intellij.codeInspection.streamMigration.StreamApiMigrationInspection.InitializerUsageStatus;
import com.intellij.codeInspection.streamMigration.StreamApiMigrationInspection.Operation;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
@@ -76,13 +77,12 @@ class ReplaceWithFindFirstFix extends MigrateToStreamFix {
PsiExpression value = assignment.getRExpression();
if (value == null) return;
restoreComments(foreachStatement, body);
if (StreamApiMigrationInspection.isDeclarationJustBefore(var, foreachStatement)) {
InitializerUsageStatus status = StreamApiMigrationInspection.getInitializerUsageStatus(var, foreachStatement);
if (status != InitializerUsageStatus.UNKNOWN) {
PsiExpression initializer = var.getInitializer();
if (initializer != null) {
PsiElement result =
initializer.replace(elementFactory.createExpressionFromText(generateOptionalUnwrap(stream, tb, value, initializer), initializer));
removeLoop(foreachStatement);
simplifyAndFormat(project, result);
String replacementText = generateOptionalUnwrap(stream, tb, value, initializer);
replaceInitializer(foreachStatement, var, initializer, replacementText, status);
return;
}
}
@@ -16,6 +16,7 @@
package com.intellij.codeInspection.streamMigration;
import com.intellij.codeInspection.ProblemDescriptor;
import com.intellij.codeInspection.streamMigration.StreamApiMigrationInspection.InitializerUsageStatus;
import com.intellij.codeInspection.streamMigration.StreamApiMigrationInspection.Operation;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
@@ -93,7 +94,8 @@ class ReplaceWithMatchFix extends MigrateToStreamFix {
// for(....) if(...) {flag = true; break;}
PsiVariable var = (PsiVariable)maybeVar;
PsiExpression initializer = var.getInitializer();
if(initializer != null && StreamApiMigrationInspection.isDeclarationJustBefore(var, foreachStatement)) {
InitializerUsageStatus status = StreamApiMigrationInspection.getInitializerUsageStatus(var, foreachStatement);
if(initializer != null && status != InitializerUsageStatus.UNKNOWN) {
String replacement;
if(ExpressionUtils.isLiteral(initializer, Boolean.FALSE) &&
ExpressionUtils.isLiteral(rValue, Boolean.TRUE)) {
@@ -104,9 +106,7 @@ class ReplaceWithMatchFix extends MigrateToStreamFix {
} else {
replacement = streamText + "?" + rValue.getText() + ":" + initializer.getText();
}
PsiElement result = initializer.replace(elementFactory.createExpressionFromText(replacement, initializer));
removeLoop(foreachStatement);
simplifyAndFormat(project, result);
replaceInitializer(foreachStatement, var, initializer, replacement, status);
return;
}
}
@@ -49,6 +49,8 @@ import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.util.*;
import static com.intellij.codeInspection.streamMigration.StreamApiMigrationInspection.InitializerUsageStatus.*;
/**
* User: anna
*/
@@ -377,19 +379,17 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo
return consumerClass != null ? psiFacade.getElementFactory().createType(consumerClass, variable.getType()) : null;
}
@Contract("null, _ -> null")
static PsiExpression extractReplaceableCollectionInitializer(PsiExpression qualifierExpression, PsiStatement foreachStatement) {
@Contract("null -> null")
static PsiLocalVariable extractCollectionVariable(PsiExpression qualifierExpression) {
if (qualifierExpression instanceof PsiReferenceExpression) {
final PsiElement resolve = ((PsiReferenceExpression)qualifierExpression).resolve();
if (resolve instanceof PsiLocalVariable) {
PsiLocalVariable var = (PsiLocalVariable)resolve;
if (isDeclarationJustBefore(var, foreachStatement)) {
final PsiExpression initializer = var.getInitializer();
if (initializer instanceof PsiNewExpression) {
final PsiExpressionList argumentList = ((PsiNewExpression)initializer).getArgumentList();
if (argumentList != null && argumentList.getExpressions().length == 0) {
return initializer;
}
final PsiExpression initializer = var.getInitializer();
if (initializer instanceof PsiNewExpression) {
final PsiExpressionList argumentList = ((PsiNewExpression)initializer).getArgumentList();
if (argumentList != null && argumentList.getExpressions().length == 0) {
return var;
}
}
}
@@ -397,6 +397,40 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo
return null;
}
enum InitializerUsageStatus {
// Variable is declared just before the wanted place
DECLARED_JUST_BEFORE,
// All initial value usages go through wanted place and at wanted place the variable value is guaranteed to be the initial value
AT_WANTED_PLACE_ONLY,
// At wanted place the variable value is guaranteed to be the initial value, but this initial value might be used somewhere else
AT_WANTED_PLACE,
// It's not guaranteed that the variable value at wanted place is initial value
UNKNOWN
}
static InitializerUsageStatus getInitializerUsageStatus(PsiVariable var, PsiStatement nextStatement) {
if(var.getInitializer() == null) return UNKNOWN;
if(isDeclarationJustBefore(var, nextStatement)) return DECLARED_JUST_BEFORE;
PsiElement declaration = var.getParent();
// Check if variable is not referenced in the same declaration like "int a = 0, b = a;"
if(!PsiTreeUtil.processElements(declaration, e -> !(e instanceof PsiReferenceExpression) ||
((PsiReferenceExpression)e).resolve() != var)) return UNKNOWN;
PsiElement block = PsiUtil.getVariableCodeBlock(var, null);
if(block == null) return UNKNOWN;
final ControlFlow controlFlow;
try {
controlFlow = ControlFlowFactory.getInstance(nextStatement.getProject())
.getControlFlow(block, LocalsOrMyInstanceFieldsControlFlowPolicy.getInstance());
}
catch (AnalysisCanceledException ignored) {
return UNKNOWN;
}
int start = controlFlow.getEndOffset(declaration);
int stop = controlFlow.getStartOffset(nextStatement);
if(ControlFlowUtil.isVariableReferencedBetween(controlFlow, start, stop, var)) return UNKNOWN;
return ControlFlowUtil.isValueUsedWithoutVisitingStop(controlFlow, start, stop, var) ? AT_WANTED_PLACE : AT_WANTED_PLACE_ONLY;
}
static boolean isDeclarationJustBefore(PsiVariable var, PsiStatement nextStatement) {
PsiElement declaration = var.getParent();
PsiElement nextStatementParent = nextStatement.getParent();
@@ -482,8 +516,7 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo
methodName = "addAll";
} else {
PsiMethodCallExpression methodCallExpression = tb.getSingleMethodCall();
if(methodCallExpression != null && extractReplaceableCollectionInitializer(
methodCallExpression.getMethodExpression().getQualifierExpression(), statement) != null) {
if(canCollect(statement, methodCallExpression)) {
methodName = "collect";
} else {
if (!SUGGEST_FOREACH) return;
@@ -535,13 +568,19 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo
}
}
void handleSingleReturn(PsiForeachStatement statement,
TerminalBlock tb,
List<Operation> operations) {
boolean canCollect(PsiForeachStatement statement, PsiMethodCallExpression methodCallExpression) {
if(methodCallExpression == null) return false;
PsiLocalVariable variable = extractCollectionVariable(methodCallExpression.getMethodExpression().getQualifierExpression());
if(variable == null) return false;
return getInitializerUsageStatus(variable, statement) != UNKNOWN;
}
void handleSingleReturn(PsiForeachStatement statement, TerminalBlock tb, List<Operation> operations) {
PsiReturnStatement returnStatement = (PsiReturnStatement)tb.getSingleStatement();
PsiExpression value = returnStatement.getReturnValue();
PsiReturnStatement nextReturnStatement = getNextReturnStatement(statement);
if(nextReturnStatement != null && (ExpressionUtils.isLiteral(value, Boolean.TRUE) || ExpressionUtils.isLiteral(value, Boolean.FALSE))) {
if (nextReturnStatement != null &&
(ExpressionUtils.isLiteral(value, Boolean.TRUE) || ExpressionUtils.isLiteral(value, Boolean.FALSE))) {
boolean foundResult = (boolean)((PsiLiteralExpression)value).getValue();
if(ExpressionUtils.isLiteral(nextReturnStatement.getReturnValue(), !foundResult)) {
String methodName;
@@ -156,16 +156,10 @@ public class UnnecessaryModuleDependencyInspection extends GlobalInspectionTool
myDependency = dependency;
}
@Override
@NotNull
public String getName() {
return "Remove dependency";
}
@Override
@NotNull
public String getFamilyName() {
return getName();
return "Remove dependency";
}
@Override
@@ -201,7 +201,7 @@ public class RedundantThrows extends GlobalJavaBatchInspectionTool {
@Override
@NotNull
public String getName() {
public String getFamilyName() {
return InspectionsBundle.message("inspection.redundant.throws.remove.quickfix");
}
@@ -225,12 +225,6 @@ public class RedundantThrows extends GlobalJavaBatchInspectionTool {
}
}
@Override
@NotNull
public String getFamilyName() {
return getName();
}
private void removeExcessiveThrows(@Nullable RefMethod refMethod, @Nullable final PsiModifierListOwner element, final CommonProblemDescriptor[] problems) {
try {
@Nullable final PsiMethod psiMethod;
@@ -131,16 +131,10 @@ public class UnusedLibrariesInspection extends GlobalInspectionTool {
myFiles = files;
}
@Override
@NotNull
public String getName() {
return myFiles == null ? InspectionsBundle.message("detach.library.quickfix.name") : InspectionsBundle.message("detach.library.roots.quickfix.name");
}
@Override
@NotNull
public String getFamilyName() {
return getName();
return myFiles == null ? InspectionsBundle.message("detach.library.quickfix.name") : InspectionsBundle.message("detach.library.roots.quickfix.name");
}
@Override
@@ -47,7 +47,7 @@ import java.util.List;
import java.util.regex.Pattern;
class ExcludeTable extends ListTableWithButtons<ExcludeTable.Item> {
private static final Pattern ourPackagePattern = Pattern.compile("(\\w+\\.)*\\w+");
private static final Pattern ourPackagePattern = Pattern.compile("([\\w*]+\\.)*[\\w*]+");
private static final ColumnInfo<Item, String> NAME_COLUMN = new ColumnInfo<Item, String>("Class/package/member qualified name mask") {
@Nullable
@Override
@@ -25,6 +25,7 @@ import com.intellij.codeInsight.template.impl.TemplateImpl;
import com.intellij.codeInsight.template.impl.TemplateManagerImpl;
import com.intellij.codeInsight.template.impl.TemplateState;
import com.intellij.featureStatistics.FeatureUsageTracker;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.event.DocumentAdapter;
@@ -288,7 +289,8 @@ public class JavaMethodCallElement extends LookupItem<PsiMethod> implements Type
TextRange endRange = templateState.getVariableRange(template.getVariableNameAt(template.getVariableCount() - 1));
if (startRange == null || endRange == null) return;
editor.getDocument().deleteString(startRange.getStartOffset(), endRange.getEndOffset());
WriteCommandAction.runWriteCommandAction(editor.getProject(), () ->
editor.getDocument().deleteString(startRange.getStartOffset(), endRange.getEndOffset()));
}
});
}
@@ -33,12 +33,6 @@ public class CreateStaticMethodQuickFix implements LocalQuickFix {
}
@NotNull
@Override
public String getName() {
return QuickFixBundle.message("create.method.from.usage.family");
}
@NotNull
@Override
public String getFamilyName() {
@@ -70,12 +70,6 @@ public class CreateFieldOrPropertyFix implements IntentionAction, LocalQuickFix
return QuickFixBundle.message(myMemberType == PropertyMemberType.FIELD ? "create.field.text":"create.property.text", myName);
}
@Override
@NotNull
public String getName() {
return getText();
}
@Override
@NotNull
public String getFamilyName() {
@@ -239,13 +239,6 @@ public class CollectionAddAllCanBeReplacedWithConstructorInspection extends Base
myAssignmentExpression = smartPointerManager.createSmartPsiElementPointer(assignmentExpression);
}
@Nls
@NotNull
@Override
public String getName() {
return getFamilyName();
}
@NotNull
@Override
public String getFamilyName() {
@@ -42,18 +42,11 @@ public class DefaultAnnotationParamInspection extends BaseJavaBatchLocalInspecti
if (defaultValue == null) return;
if (areEqual(value, defaultValue)) {
holder.registerProblem(value, "Redundant default parameter value assignment", ProblemHighlightType.LIKE_UNUSED_SYMBOL, new LocalQuickFix() {
@Nls
@NotNull
@Override
public String getName() {
return "Remove redundant parameter";
}
@Nls
@NotNull
@Override
public String getFamilyName() {
return getName();
return "Remove redundant parameter";
}
@Override
@@ -36,7 +36,7 @@ public class RemoveInitializerFix implements LocalQuickFix {
@Override
@NotNull
public String getName() {
public String getFamilyName() {
return InspectionsBundle.message("inspection.unused.assignment.remove.quickfix");
}
@@ -91,10 +91,4 @@ public class RemoveInitializerFix implements LocalQuickFix {
LOG.error(e);
}
}
@Override
@NotNull
public String getFamilyName() {
return getName();
}
}
@@ -210,16 +210,10 @@ class UnusedParametersInspection extends GlobalJavaBatchInspectionTool {
return myHint;
}
@Override
@NotNull
public String getName() {
return InspectionsBundle.message("inspection.unused.parameter.delete.quickfix");
}
@Override
@NotNull
public String getFamilyName() {
return getName();
return InspectionsBundle.message("inspection.unused.parameter.delete.quickfix");
}
@Override
@@ -84,16 +84,10 @@ public class DefaultFileTemplateUsageInspection extends BaseJavaLocalInspectionT
myReplaceTemplateFix = replaceTemplateFix;
}
@Override
@NotNull
public String getName() {
return InspectionsBundle.message("default.file.template.edit.template");
}
@Override
@NotNull
public String getFamilyName() {
return getName();
return InspectionsBundle.message("default.file.template.edit.template");
}
@Override
@@ -25,13 +25,7 @@ import org.jetbrains.annotations.NotNull;
public abstract class ReplaceWithFileTemplateFix implements LocalQuickFix {
@Override
@NotNull
public String getName() {
public String getFamilyName() {
return InspectionsBundle.message("default.file.template.replace.with.actual.file.template");
}
@Override
@NotNull
public String getFamilyName() {
return getName();
}
}
@@ -307,12 +307,6 @@ public class EmptyMethodInspection extends GlobalJavaBatchInspectionTool {
myHint = hint;
}
@Override
@NotNull
public String getName() {
return QUICK_FIX_NAME;
}
@Override
@NotNull
public String getFamilyName() {
@@ -352,7 +346,7 @@ public class EmptyMethodInspection extends GlobalJavaBatchInspectionTool {
@Override
@NotNull
public String getName() {
public String getFamilyName() {
return QUICK_FIX_NAME;
}
@@ -361,12 +355,6 @@ public class EmptyMethodInspection extends GlobalJavaBatchInspectionTool {
applyFix(project, new ProblemDescriptor[]{descriptor}, new ArrayList<>(), null);
}
@Override
@NotNull
public String getFamilyName() {
return getName();
}
private void deleteHierarchy(RefMethod refMethod, List<PsiElement> result) {
Collection<RefMethod> derivedMethods = refMethod.getDerivedMethods();
RefMethod[] refMethods = derivedMethods.toArray(new RefMethod[derivedMethods.size()]);
@@ -47,18 +47,12 @@ public class EqualsAndHashcode extends EqualsAndHashcodeBase {
@NotNull
@Override
public String getName() {
public String getFamilyName() {
return myHasEquals
? InspectionsBundle.message("inspection.equals.hashcode.generate.hashcode.quickfix")
: InspectionsBundle.message("inspection.equals.hashcode.generate.equals.quickfix");
}
@NotNull
@Override
public String getFamilyName() {
return getName();
}
@Override
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
final Editor editor = FileEditorManager.getInstance(project).getSelectedTextEditor();
@@ -114,13 +114,6 @@ public class StaticPseudoFunctionalStyleMethodInspection extends BaseJavaBatchLo
myHandler = handler;
}
@Nls
@NotNull
@Override
public String getName() {
return getFamilyName();
}
@NotNull
@Override
public String getFamilyName() {
@@ -69,16 +69,10 @@ public class JavaDocReferenceInspection extends JavaDocReferenceInspectionBase {
myUnboundParams = unboundParams;
}
@Override
@NotNull
public String getName() {
return "Change to ...";
}
@Override
@NotNull
public String getFamilyName() {
return getName();
return "Change to ...";
}
@Override
@@ -114,12 +108,6 @@ public class JavaDocReferenceInspection extends JavaDocReferenceInspectionBase {
this.originalClasses = originalClasses;
}
@Override
@NotNull
public String getName() {
return QuickFixBundle.message("add.qualifier");
}
@Override
@NotNull
public String getFamilyName() {
@@ -129,13 +129,6 @@ public class RedundantLambdaParameterTypeInspection extends BaseJavaBatchLocalIn
}
private static class LambdaParametersFix implements LocalQuickFix {
@Nls
@NotNull
@Override
public String getName() {
return getFamilyName();
}
@Nls
@NotNull
@Override
@@ -203,16 +203,10 @@ public class MagicConstantInspection extends BaseJavaLocalInspectionTool {
String text = "No IDEA annotations attached to the JDK " + finalJdk.getName() + (path == null ? "" : " (" + FileUtil.toSystemDependentName(path) + ")")
+", some issues will not be found";
holder.registerProblem(file, text, ProblemHighlightType.GENERIC_ERROR_OR_WARNING, new LocalQuickFix() {
@NotNull
@Override
public String getName() {
return "Attach annotations";
}
@NotNull
@Override
public String getFamilyName() {
return getName();
return "Attach annotations";
}
@Override
@@ -54,12 +54,6 @@ public class RedundantArrayForVarargsCallInspection extends GenericsInspectionTo
@Override
@NotNull
public String getFamilyName() {
return getName();
}
@Override
@NotNull
public String getName() {
return InspectionsBundle.message("inspection.redundant.array.creation.quickfix");
}
}
@@ -45,18 +45,11 @@ public class SillyAssignmentInspection extends SillyAssignmentInspectionBase {
private static class RemoveSillyAssignmentFix implements LocalQuickFix {
@Nls
@NotNull
@Override
public String getName() {
return InspectionsBundle.message("assignment.to.itself.quickfix.name");
}
@Nls
@NotNull
@Override
public String getFamilyName() {
return getName();
return InspectionsBundle.message("assignment.to.itself.quickfix.name");
}
@Override
@@ -154,7 +154,7 @@ public class UnusedReturnValue extends GlobalJavaBatchInspectionTool{
@Override
@NotNull
public String getName() {
public String getFamilyName() {
return InspectionsBundle.message("inspection.unused.return.value.make.void.quickfix");
}
@@ -179,12 +179,6 @@ public class UnusedReturnValue extends GlobalJavaBatchInspectionTool{
return false;
}
@Override
@NotNull
public String getFamilyName() {
return getName();
}
private static void makeMethodHierarchyVoid(Project project, @NotNull PsiMethod psiMethod) {
replaceReturnStatements(psiMethod);
for (final PsiMethod oMethod : OverridingMethodsSearch.search(psiMethod)) {
@@ -50,7 +50,7 @@ public abstract class BaseConvertToLocalQuickFix<V extends PsiVariable> implemen
@Override
@NotNull
public final String getName() {
public final String getFamilyName() {
return InspectionsBundle.message("inspection.convert.to.local.quickfix");
}
@@ -225,12 +225,6 @@ public abstract class BaseConvertToLocalQuickFix<V extends PsiVariable> implemen
}
}
@Override
@NotNull
public String getFamilyName() {
return getName();
}
@Nullable
private static PsiElement getAnchorElement(PsiCodeBlock anchorBlock, @NotNull PsiElement firstElement) {
PsiElement element = firstElement;
@@ -549,13 +549,24 @@ public class JavaDocumentationProvider extends DocumentationProviderEx implement
@Nullable
public static String generateExternalJavadoc(@NotNull final PsiElement element) {
List<String> docURLs = getExternalJavaDocUrl(element);
return generateExternalJavadoc(element, docURLs);
}
@Nullable
public static String generateExternalJavadoc(@NotNull final PsiElement element, @Nullable List<String> docURLs) {
final JavaDocInfoGenerator javaDocInfoGenerator = JavaDocInfoGeneratorFactory.create(element.getProject(), element);
return generateExternalJavadoc(element, javaDocInfoGenerator);
return generateExternalJavadoc(javaDocInfoGenerator, docURLs);
}
@Nullable
public static String generateExternalJavadoc(@NotNull final PsiElement element, @NotNull JavaDocInfoGenerator generator) {
final List<String> docURLs = getExternalJavaDocUrl(element);
return generateExternalJavadoc(generator, docURLs);
}
@Nullable
private static String generateExternalJavadoc(@NotNull JavaDocInfoGenerator generator, @Nullable List<String> docURLs) {
return JavaDocExternalFilter.filterInternalDocInfo(generator.generateDocInfo(docURLs));
}
@@ -101,12 +101,17 @@ public class ReferenceChainLink {
PsiPackage pkg = JavaPsiFacade.getInstance(project).findPackage(referenceName);
if (pkg != null && pkg.getDirectories(scope).length > 0) return null;
if (!cache.processFieldsWithName(referenceName, processor, scope, null) ||
!cache.processClassesWithName(referenceName, processor, scope, null)) {
if (!cache.processFieldsWithName(referenceName, processor, scope, null)) {
markExpensive(project);
return null;
}
}
if (!cache.processClassesWithName(referenceName, processor, scope, null)) {
markExpensive(project);
return null;
}
return candidates;
}
@@ -24,10 +24,7 @@ import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.impl.source.resolve.graphInference.PsiPolyExpressionUtil;
import com.intellij.psi.infos.MethodCandidateInfo;
import com.intellij.psi.util.*;
import com.intellij.util.Consumer;
import com.intellij.util.Function;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.Producer;
import com.intellij.util.*;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.HashMap;
import org.jetbrains.annotations.Contract;
@@ -122,8 +119,7 @@ public class LambdaUtil {
public static boolean isFunctionalClass(PsiClass aClass) {
if (aClass != null) {
if (aClass instanceof PsiTypeParameter) return false;
final List<HierarchicalMethodSignature> signatures = findFunctionCandidates(aClass);
return signatures != null && signatures.size() == 1;
return getFunction(aClass) != null;
}
return false;
}
@@ -152,15 +148,69 @@ public class LambdaUtil {
@Contract("null -> null")
@Nullable
public static MethodSignature getFunction(PsiClass psiClass) {
if (psiClass == null) return null;
final List<HierarchicalMethodSignature> functions = findFunctionCandidates(psiClass);
if (functions != null && functions.size() == 1) {
return functions.get(0);
public static MethodSignature getFunction(final PsiClass psiClass) {
if (isPlainInterface(psiClass)) {
return CachedValuesManager.getCachedValue(psiClass, new CachedValueProvider<MethodSignature>() {
@Nullable
@Override
public Result<MethodSignature> compute() {
return Result.create(calcFunction(psiClass), PsiModificationTracker.JAVA_STRUCTURE_MODIFICATION_COUNT);
}
});
}
return null;
}
private static boolean isPlainInterface(PsiClass psiClass) {
return psiClass != null && psiClass.isInterface() && !psiClass.isAnnotationType();
}
@Nullable
private static MethodSignature calcFunction(@NotNull PsiClass psiClass) {
if (hasManyOwnAbstractMethods(psiClass) || hasManyInheritedAbstractMethods(psiClass)) return null;
final List<HierarchicalMethodSignature> functions = findFunctionCandidates(psiClass);
return functions != null && functions.size() == 1 ? functions.get(0) : null;
}
private static boolean hasManyOwnAbstractMethods(@NotNull PsiClass psiClass) {
int abstractCount = 0;
for (PsiMethod method : psiClass.getMethods()) {
if (isDefinitelyAbstractInterfaceMethod(method) && ++abstractCount > 1) {
return true;
}
}
return false;
}
private static boolean isDefinitelyAbstractInterfaceMethod(PsiMethod method) {
return method.hasModifierProperty(PsiModifier.ABSTRACT) && !isPublicObjectMethod(method.getName());
}
private static boolean isPublicObjectMethod(String methodName) {
return "equals".equals(methodName) || "hashCode".equals(methodName) || "toString".equals(methodName);
}
private static boolean hasManyInheritedAbstractMethods(@NotNull PsiClass psiClass) {
final Set<String> abstractNames = ContainerUtil.newHashSet();
final Set<String> defaultNames = ContainerUtil.newHashSet();
InheritanceUtil.processSupers(psiClass, true, new Processor<PsiClass>() {
@Override
public boolean process(PsiClass psiClass) {
for (PsiMethod method : psiClass.getMethods()) {
if (isDefinitelyAbstractInterfaceMethod(method)) {
abstractNames.add(method.getName());
}
else if (method.hasModifierProperty(PsiModifier.DEFAULT)) {
defaultNames.add(method.getName());
}
}
return true;
}
});
abstractNames.removeAll(defaultNames);
return abstractNames.size() > 1;
}
private static boolean overridesPublicObjectMethod(HierarchicalMethodSignature psiMethod) {
final List<HierarchicalMethodSignature> signatures = psiMethod.getSuperSignatures();
@@ -219,20 +269,9 @@ public class LambdaUtil {
@Contract("null -> null")
@Nullable
public static List<HierarchicalMethodSignature> findFunctionCandidates(final PsiClass psiClass) {
if (psiClass != null && psiClass.isInterface() && !psiClass.isAnnotationType()) {
return CachedValuesManager.getCachedValue(psiClass, new CachedValueProvider<List<HierarchicalMethodSignature>>() {
@Nullable
@Override
public Result<List<HierarchicalMethodSignature>> compute() {
return Result.create(calcFunctionCandidates(psiClass), PsiModificationTracker.JAVA_STRUCTURE_MODIFICATION_COUNT);
}
});
}
return null;
}
public static List<HierarchicalMethodSignature> findFunctionCandidates(@Nullable final PsiClass psiClass) {
if (!isPlainInterface(psiClass)) return null;
private static List<HierarchicalMethodSignature> calcFunctionCandidates(PsiClass psiClass) {
final List<HierarchicalMethodSignature> methods = new ArrayList<HierarchicalMethodSignature>();
final Map<MethodSignature, Set<PsiMethod>> overrideEquivalents = PsiSuperMethodUtil.collectOverrideEquivalents(psiClass);
final Collection<HierarchicalMethodSignature> visibleSignatures = psiClass.getVisibleSignatures();
@@ -26,6 +26,7 @@ public interface PsiJavaModule extends NavigatablePsiElement, PsiNamedElement, P
String MODULE_INFO_CLASS = "module-info";
String MODULE_INFO_FILE = MODULE_INFO_CLASS + ".java";
String MODULE_INFO_CLS_FILE = MODULE_INFO_CLASS + ".class";
String JAVA_BASE = "java.base";
@NotNull PsiJavaModuleReferenceElement getNameElement();
@NotNull String getModuleName();
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -29,6 +29,7 @@ import com.intellij.util.containers.IntArrayList;
import com.intellij.util.containers.IntStack;
import gnu.trove.THashMap;
import gnu.trove.THashSet;
import gnu.trove.TIntArrayList;
import gnu.trove.TIntHashSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -1284,6 +1285,112 @@ public class ControlFlowUtil {
return visitor.getResult().booleanValue();
}
/**
* Returns true if the value the variable has at start is later referenced without going through stop instruction
*
* @param flow ControlFlow to analyze
* @param start the point at which variable value is created
* @param stop the stop-point
* @param variable the variable to examine
* @return true if the value the variable has at start is later referenced without going through stop instruction
*/
public static boolean isValueUsedWithoutVisitingStop(final ControlFlow flow, final int start, final int stop, final PsiVariable variable) {
if(start == stop) return false;
class MyVisitor extends InstructionClientVisitor<Boolean> {
// true if value the variable has at given offset maybe referenced without going through stop instruction
final boolean[] maybeReferenced = new boolean[flow.getSize() + 1];
@Override
public void visitInstruction(Instruction instruction, int offset, int nextOffset) {
if (offset == stop) {
maybeReferenced[offset] = false;
return;
}
if(instruction instanceof WriteVariableInstruction && ((WriteVariableInstruction)instruction).variable == variable) {
maybeReferenced[offset] = false;
return;
}
if (maybeReferenced[offset]) return;
if (nextOffset > flow.getSize()) nextOffset = flow.getSize();
boolean nextState = maybeReferenced[nextOffset];
maybeReferenced[offset] =
nextState || (instruction instanceof ReadVariableInstruction && ((ReadVariableInstruction)instruction).variable == variable);
}
@Override
public Boolean getResult() {
return maybeReferenced[start];
}
}
MyVisitor visitor = new MyVisitor();
depthFirstSearch(flow, visitor, start, flow.getSize());
return visitor.getResult().booleanValue();
}
/**
* Checks whether variable can be referenced between start and stop points. Back-edges are also considered, so the actual place
* where it referenced might be outside of (start, stop) interval.
*
* @param flow ControlFlow to analyze
* @param start start point
* @param stop stop point
* @param variable variable to analyze
* @return true if variable can be referenced between start and stop points
*/
public static boolean isVariableReferencedBetween(final ControlFlow flow,
final int start,
final int stop,
final PsiVariable variable) {
if(start == stop) return false;
// DFS visits instructions mainly in backward direction while here visiting in forward direction
// greatly reduces number of iterations. So first we just collect edges, then reverse their order.
// contains (from, to) pairs representing control flow arcs
final TIntArrayList list = new TIntArrayList();
depthFirstSearch(flow, new InstructionClientVisitor<Void>() {
@Override
public void visitInstruction(Instruction instruction, int offset, int nextOffset) {
list.add(offset);
list.add(nextOffset);
}
@Override
public Void getResult() {
return null;
}
}, start, flow.getSize());
BitSet violated = new BitSet();
List<Instruction> instructions = flow.getInstructions();
boolean changed = true;
while(changed) {
changed = false;
for(int i=list.size()-2; i>=0; i-=2) {
int from = list.get(i);
int to = list.get(i+1);
if(from == stop) continue;
if(violated.get(from)) {
if(!violated.get(to)) {
if(to == stop) return true;
violated.set(to);
changed = true;
}
continue;
}
Instruction instruction = instructions.get(from);
if((instruction instanceof ReadVariableInstruction && ((ReadVariableInstruction)instruction).variable == variable) ||
(instruction instanceof WriteVariableInstruction && ((WriteVariableInstruction)instruction).variable == variable)) {
violated.set(from);
violated.set(to);
if(to == stop) return true;
changed = true;
}
}
}
return false;
}
/**
* @return min offset after sourceOffset which is definitely reachable from all references
*/
@@ -411,6 +411,9 @@ module.service.abstract=The service implementation is an abstract class: {0}
module.service.no.ctor=The service implementation does not have a default constructor: {0}
module.service.hidden.ctor=The default constructor of the service implementation is not public: {0}
module.service.unused=Service interface provided but not exported or used
module.package.not.exported=The module ''{0}'' does not export the package ''{1}'' to the module ''{2}''
module.package.on.classpath=A named module cannot access packages of an unnamed one
module.not.in.requirements=The module ''{0}'' does not have the module ''{1}'' in requirements
feature.generics=Generics
feature.annotations=Annotations
@@ -0,0 +1,17 @@
class A {
void foo() {
new IterHelper<>().loopMap((k, val) -> {
//do something
});
}
}
final class IterHelper<K, V> {
public void loopMap(final MapIterCallback<K, V> callback) {
//do something
}
public static interface MapIterCallback<K, V> {
abstract void eval(K k, V v);
}
}
@@ -4,6 +4,6 @@ import java.util.List;
public class Main {
public void testAssignment(List<String> data) {
String found = data.stream().map(String::trim).anyMatch(trimmed -> !trimmed.isEmpty()) ? "yes" : "no";
String found = data.stream().map(String::trim).anyMatch(trimmed -> !trimmed.isEmpty()) ? "yes" : "no";
}
}
@@ -4,6 +4,6 @@ import java.util.List;
public class Main {
public void testAssignment(List<String> data) {
boolean found = data.stream().map(String::trim).anyMatch(trimmed -> !trimmed.isEmpty());
boolean found = data.stream().map(String::trim).anyMatch(trimmed -> !trimmed.isEmpty());
}
}
@@ -4,6 +4,6 @@ import java.util.List;
public class Main {
public void testAssignment(List<String> data) {
boolean found = !data.stream().map(String::trim).anyMatch(trimmed -> !trimmed.isEmpty());
boolean found = !data.stream().map(String::trim).anyMatch(trimmed -> !trimmed.isEmpty());
}
}
@@ -0,0 +1,14 @@
// "Replace with anyMatch()" "true"
import java.util.List;
public class Main {
public void testAssignment(List<String> data) {
boolean found;
if (data.size() > 10) {
System.out.println("Big data");
}
found = data.stream().map(String::trim).anyMatch(trimmed -> !trimmed.isEmpty());
System.out.println(found);
}
}
@@ -4,13 +4,12 @@ import java.util.List;
public class Main {
public void testAssignment(List<String> data) {
boolean found = false;
boolean found;
if(Math.random() > 0.5) {
found = true;
} else {
if (data.stream().map(String::trim).anyMatch(trimmed -> !trimmed.isEmpty())) {
found = true;
}
found = data.stream().map(String::trim).anyMatch(trimmed -> !trimmed.isEmpty());
}
System.out.println(found);
}
}
@@ -0,0 +1,15 @@
// "Replace with anyMatch()" "true"
import java.util.List;
public class Main {
public void testAssignment(List<String> data) {
boolean found = false;
if(Math.random() > 0.5) {
System.out.println("oops");
} else {
found = data.stream().map(String::trim).anyMatch(trimmed -> !trimmed.isEmpty());
}
System.out.println(found);
}
}
@@ -0,0 +1,16 @@
// "Replace with anyMatch()" "true"
import java.util.List;
public class Main {
public void testAssignment(List<String> data) {
boolean found;
try {
found = data.stream().map(String::trim).anyMatch(trimmed -> !trimmed.isEmpty());
System.out.println(found);
}
catch(Exception ex) {
ex.printStackTrace();
}
}
}
@@ -0,0 +1,16 @@
// "Replace with anyMatch()" "true"
import java.util.List;
public class Main {
public void testAssignment(List<String> data) {
boolean found = false;
try {
found = data.stream().map(String::trim).anyMatch(trimmed -> !trimmed.isEmpty());
}
catch(Exception ex) {
ex.printStackTrace();
}
System.out.println(found);
}
}
@@ -6,6 +6,6 @@ import java.util.stream.Collectors;
public class Main {
public void test(Integer[] arr) {
List<Integer> result = Arrays.stream(arr).filter(x -> x > 5).collect(Collectors.toList());
List<Integer> result = Arrays.stream(arr).filter(x -> x > 5).collect(Collectors.toList());
}
}
@@ -4,7 +4,7 @@ import java.util.stream.Collectors;
class A {
public static void main(List<String> args) {
ArrayList<String> uniqNames = args.stream().map(name -> name.substring(1)).collect(Collectors.toCollection(ArrayList::new));
ArrayList<String> uniqNames = args.stream().map(name -> name.substring(1)).collect(Collectors.toCollection(ArrayList::new));
uniqNames.forEach(System.out::println);
}
@@ -5,7 +5,7 @@ import java.util.stream.Collectors;
class Test {
public static <T> List<TokenFilter<T>> fromString(final T src, Function<T, List<String>> extractor) {
final List<TokenFilter<T>> result = extractor.apply(src).stream().map((Function<String, TokenFilter<T>>) TokenFilter::new).collect(Collectors.toList());
final List<TokenFilter<T>> result = extractor.apply(src).stream().map((Function<String, TokenFilter<T>>) TokenFilter::new).collect(Collectors.toList());
return result;
}
@@ -10,6 +10,6 @@ public class Collect {
}
void collectNames(List<Person> persons){
List<String> names = persons.stream().map(Person::getName).collect(Collectors.toList());
List<String> names = persons.stream().map(Person::getName).collect(Collectors.toList());
}
}
@@ -10,6 +10,6 @@ public class Collect {
}
void collectNames(List<Person> persons){
List<String> names = persons.stream().filter(Objects::nonNull).map(Person::getName).collect(Collectors.toList());
List<String> names = persons.stream().filter(Objects::nonNull).map(Person::getName).collect(Collectors.toList());
}
}
@@ -10,6 +10,6 @@ public class Collect {
}
void collectNames(List<Person> persons){
Collection<String> names = persons.stream().map(Person::getName).collect(Collectors.toList());
Collection<String> names = persons.stream().map(Person::getName).collect(Collectors.toList());
}
}
@@ -10,7 +10,7 @@ public class Collect {
}
void collectNames(List<Person> persons){
List<String> names = persons.stream().map(Person::getName).collect(Collectors.toList());
List<String> names = persons.stream().map(Person::getName).collect(Collectors.toList());
//some comment
}
}
@@ -10,6 +10,6 @@ public class Collect {
}
void collectNames(List<Person> persons){
List<String> names = persons.stream().map(person -> "name: " + person.getName()).collect(Collectors.toList());
List<String> names = persons.stream().map(person -> "name: " + person.getName()).collect(Collectors.toList());
}
}
@@ -6,6 +6,6 @@ import java.util.stream.Collectors;
public class Main {
public void testPrimitiveMap(List<String> data) {
List<Integer> list = data.stream().filter(str -> str.startsWith("xyz")).mapToInt(String::length).filter(len -> len > 10).boxed().collect(Collectors.toList());
List<Integer> list = data.stream().filter(str -> str.startsWith("xyz")).mapToInt(String::length).filter(len -> len > 10).boxed().collect(Collectors.toList());
}
}
@@ -10,6 +10,6 @@ public class Collect {
}
void collectNames(List<Person> persons){
Set<String> names = persons.stream().map(Person::getName).collect(Collectors.toSet());
Set<String> names = persons.stream().map(Person::getName).collect(Collectors.toSet());
}
}
@@ -0,0 +1,17 @@
// "Replace with collect" "true"
import java.util.*;
import java.util.stream.Collectors;
public class Collect {
class Person {
String getName() {
return "";
}
}
void collectNames(List<Person> persons){
Set<String> names, other = new HashSet<>();
names = persons.stream().map(Person::getName).collect(Collectors.toSet());
System.out.println(names);
}
}
@@ -0,0 +1,18 @@
// "Replace with forEach" "true"
import java.util.*;
public class Collect {
class Person {
String getName() {
return "";
}
}
void collectNames(List<Person> persons){
Set<String> names = new HashSet<>();
for(int i=0; i<10; i++) {
persons.stream().map(Person::getName).forEach(names::add);
}
System.out.println(names);
}
}
@@ -0,0 +1,17 @@
// "Replace with forEach" "true"
import java.util.*;
public class Collect {
class Person {
String getName() {
return "";
}
}
void collectNames(List<Person> persons){
Set<String> names = new HashSet<>();
names.add("Test");
persons.stream().map(Person::getName).forEach(names::add);
System.out.println(names);
}
}
@@ -0,0 +1,19 @@
// "Replace with collect" "true"
import java.util.*;
import java.util.stream.Collectors;
public class Collect {
class Person {
String getName() {
return "";
}
}
void collectNames(List<Person> persons){
Set<String> names = new HashSet<>(), other = new HashSet<>();
if(persons != null) {
names = persons.stream().map(Person::getName).collect(Collectors.toSet());
}
System.out.println(names);
}
}
@@ -10,6 +10,6 @@ public class Collect {
}
void collectNames(List<Person> persons){
Set<String> names = persons.stream().map(Person::getName).collect(Collectors.toCollection(LinkedHashSet::new));
Set<String> names = persons.stream().map(Person::getName).collect(Collectors.toCollection(LinkedHashSet::new));
}
}
@@ -6,6 +6,6 @@ import java.util.stream.Collectors;
public class Main {
public void testPrimitiveMap(List<String> data) {
List<String> list = data.stream().filter(str -> str.startsWith("xyz")).mapToInt(String::length).filter(len -> len > 10).mapToObj(String::valueOf).collect(Collectors.toList());
List<String> list = data.stream().filter(str -> str.startsWith("xyz")).mapToInt(String::length).filter(len -> len > 10).mapToObj(String::valueOf).collect(Collectors.toList());
}
}
@@ -6,7 +6,7 @@ import java.util.stream.Collectors;
public class Main {
public List<String> test(Map<String, List<String>> map) {
List<String> result = map.entrySet().stream().filter(entry -> !entry.getKey().isEmpty()).map(Map.Entry::getValue).filter(Objects::nonNull).flatMap(Collection::stream).map(String::trim).filter(trimmed -> !trimmed.isEmpty()).collect(Collectors.toList());
List<String> result = map.entrySet().stream().filter(entry -> !entry.getKey().isEmpty()).map(Map.Entry::getValue).filter(Objects::nonNull).flatMap(Collection::stream).map(String::trim).filter(trimmed -> !trimmed.isEmpty()).collect(Collectors.toList());
return result;
}
}
@@ -6,7 +6,7 @@ import java.util.stream.Collectors;
public class Main {
public List<String> test(Map<String, List<String>> map) {
List<String> result = map.entrySet().stream().filter(entry -> !entry.getKey().isEmpty()).map(Map.Entry::getValue).filter(Objects::nonNull).flatMap(Collection::stream).map(String::trim).filter(trimmed -> !trimmed.isEmpty()).collect(Collectors.toList());
List<String> result = map.entrySet().stream().filter(entry -> !entry.getKey().isEmpty()).map(Map.Entry::getValue).filter(Objects::nonNull).flatMap(Collection::stream).map(String::trim).filter(trimmed -> !trimmed.isEmpty()).collect(Collectors.toList());
return result;
}
}
@@ -4,6 +4,6 @@ import java.util.stream.Collectors;
public class Main {
public void test(List<Set<String>> nested) {
List<String> result = nested.stream().filter(Objects::nonNull).flatMap(Collection::stream).filter(str -> str.startsWith("xyz")).map(String::trim).collect(Collectors.toList());
List<String> result = nested.stream().filter(Objects::nonNull).flatMap(Collection::stream).filter(str -> str.startsWith("xyz")).map(String::trim).collect(Collectors.toList());
}
}
@@ -6,6 +6,6 @@ import java.util.stream.Collectors;
public class Main {
public void test(List<String[]> list) {
List<String> result = list.stream().filter(arr -> arr.length > 2).flatMap(Arrays::stream).map(String::trim).filter(trimmed -> !trimmed.isEmpty()).collect(Collectors.toList());
List<String> result = list.stream().filter(arr -> arr.length > 2).flatMap(Arrays::stream).map(String::trim).filter(trimmed -> !trimmed.isEmpty()).collect(Collectors.toList());
}
}
@@ -7,6 +7,6 @@ import java.util.stream.Collectors;
public class Main {
public void test(Map<String, String[]> map) {
List<String> result = map.entrySet().stream().filter(entry -> entry.getKey().startsWith("x")).map(Map.Entry::getValue).flatMap(Arrays::stream).map(String::trim).collect(Collectors.toList());
List<String> result = map.entrySet().stream().filter(entry -> entry.getKey().startsWith("x")).map(Map.Entry::getValue).flatMap(Arrays::stream).map(String::trim).collect(Collectors.toList());
}
}
@@ -6,7 +6,7 @@ import java.util.Objects;
public class Main {
public void testMap(Map<String, List<String>> map) throws Exception {
int firstSize = map.values().stream().filter(Objects::nonNull).findFirst().map(List::size).orElse(0);
int firstSize = map.values().stream().filter(Objects::nonNull).findFirst().map(List::size).orElse(0);
// comment
System.out.println(firstSize);
}
@@ -4,7 +4,7 @@ import java.util.List;
public class Main {
public void testCast(List<Object> data) {
String found = (String) data.stream().filter(obj -> obj instanceof String).findFirst().orElse(null);
String found = (String) data.stream().filter(obj -> obj instanceof String).findFirst().orElse(null);
System.out.println(found);
}
}
@@ -0,0 +1,18 @@
// "Replace with findFirst()" "true"
import java.util.List;
import java.util.Map;
import java.util.Objects;
public class Main {
public void testMap(Map<String, List<String>> map) throws Exception {
int firstSize;
int other = map.size();
if(other > 10) {
System.out.println("Big");
}
// comment
firstSize = map.values().stream().filter(Objects::nonNull).findFirst().map(List::size).orElse(0);
System.out.println(firstSize);
}
}
@@ -0,0 +1,14 @@
// "Replace with findFirst()" "true"
import java.util.List;
import java.util.Map;
import java.util.Objects;
public class Main {
public void testMap(Map<String, List<String>> map) throws Exception {
int firstSize = 0, other = firstSize;
// comment
firstSize = map.values().stream().filter(Objects::nonNull).findFirst().map(List::size).orElse(firstSize);
System.out.println(firstSize);
}
}
@@ -7,7 +7,7 @@ import java.util.Objects;
public class Main {
public void testMap(Map<String, List<String>> map) throws Exception {
String firstStr = map.values().stream().filter(Objects::nonNull).flatMap(Collection::stream).filter(str -> !str.isEmpty()).findFirst().orElse("");
String firstStr = map.values().stream().filter(Objects::nonNull).flatMap(Collection::stream).filter(str -> !str.isEmpty()).findFirst().orElse("");
System.out.println(firstStr);
}
}
@@ -7,7 +7,7 @@ import java.util.stream.Collectors;
public class Main {
public List<String> test(String[][] arr) {
List<String> result = Arrays.stream(arr).filter(Objects::nonNull).flatMap(Arrays::stream).collect(Collectors.toList());
List<String> result = Arrays.stream(arr).filter(Objects::nonNull).flatMap(Arrays::stream).collect(Collectors.toList());
return result;
}
}
@@ -7,7 +7,7 @@ import java.util.stream.Collectors;
public class Main {
public List<Integer> test(int[][] arr) {
List<Integer> result = Arrays.stream(arr).filter(Objects::nonNull).flatMapToInt(Arrays::stream).boxed().collect(Collectors.toList());
List<Integer> result = Arrays.stream(arr).filter(Objects::nonNull).flatMapToInt(Arrays::stream).boxed().collect(Collectors.toList());
return result;
}
}
@@ -0,0 +1,15 @@
// "Replace with sum()" "true"
import java.util.List;
public class Main {
public void testPrimitiveMap(List<String> data) {
int sum;
if(Math.random() > 0.5) {
sum = 10;
} else {
sum = data.stream().filter(str -> str.startsWith("xyz")).mapToInt(String::length).filter(len -> len > 10).map(len -> len * 2).sum();
}
System.out.println(sum);
}
}
@@ -0,0 +1,15 @@
// "Replace with sum()" "true"
import java.util.List;
public class Main {
public void testPrimitiveMap(List<String> data) {
int sum = 0;
if(Math.random() > 0.5) {
System.out.println("oops");
} else {
sum = data.stream().filter(str -> str.startsWith("xyz")).mapToInt(String::length).filter(len -> len > 10).map(len -> len * 2).sum();
}
System.out.println(sum);
}
}

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