mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-01-06 03:21:12 +07:00
Provide fixes for missing branches in switch expression (create 'default', create missing branches)
Fixes IDEA-203071 Switch expressions: provide fix to generate missed branches for enum switches Fixes IDEA-203449 Switch statement without default branch: provide a quick-fix
This commit is contained in:
@@ -19,6 +19,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author cdr
|
||||
@@ -460,4 +461,8 @@ public abstract class QuickFixFactory {
|
||||
public IntentionAction createSameErasureButDifferentMethodsFix(@NotNull PsiMethod method, @NotNull PsiMethod superMethod) {
|
||||
throw new AbstractMethodError();
|
||||
}
|
||||
|
||||
public abstract IntentionAction createAddMissingEnumBranchesFix(@NotNull PsiSwitchBlock switchBlock, @NotNull Set<String> missingCases);
|
||||
|
||||
public abstract IntentionAction createAddSwitchDefaultFix(@NotNull PsiSwitchBlock switchBlock);
|
||||
}
|
||||
@@ -1989,29 +1989,30 @@ public class HighlightUtil extends HighlightUtilBase {
|
||||
}
|
||||
|
||||
if (results.isEmpty() && switchBlock instanceof PsiSwitchExpression) {
|
||||
if (values.isEmpty()) {
|
||||
String message = JavaErrorMessages.message("switch.expr.empty");
|
||||
results.add(HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(switchBlock).descriptionAndTooltip(message).create());
|
||||
}
|
||||
else if (!hasDefaultCase) {
|
||||
Set<String> missingConstants = new HashSet<>();
|
||||
boolean exhaustive = hasDefaultCase;
|
||||
if (!exhaustive) {
|
||||
if (selectorType instanceof PsiClassType) {
|
||||
PsiClass type = ((PsiClassType)selectorType).resolve();
|
||||
if (type != null && type.isEnum()) {
|
||||
Set<Object> constants = new HashSet<>();
|
||||
for (PsiField field : type.getFields()) {
|
||||
if (field instanceof PsiEnumConstant) {
|
||||
constants.add(field.getName());
|
||||
if (field instanceof PsiEnumConstant && !values.containsKey(field.getName())) {
|
||||
missingConstants.add(field.getName());
|
||||
}
|
||||
}
|
||||
constants.removeAll(values.keySet());
|
||||
hasDefaultCase = constants.isEmpty();
|
||||
exhaustive = missingConstants.isEmpty();
|
||||
}
|
||||
}
|
||||
if (!hasDefaultCase) {
|
||||
PsiElement range = ObjectUtils.notNull(selectorExpression, switchBlock);
|
||||
String message = JavaErrorMessages.message("switch.expr.incomplete");
|
||||
results.add(HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(range).descriptionAndTooltip(message).create());
|
||||
}
|
||||
if (!exhaustive) {
|
||||
PsiElement range = ObjectUtils.notNull(selectorExpression, switchBlock);
|
||||
String message = JavaErrorMessages.message(values.isEmpty() ? "switch.expr.empty" : "switch.expr.incomplete");
|
||||
HighlightInfo info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(range).descriptionAndTooltip(message).create();
|
||||
if (!missingConstants.isEmpty()) {
|
||||
QuickFixAction.registerQuickFixAction(info, QUICK_FIX_FACTORY.createAddMissingEnumBranchesFix(switchBlock, missingConstants));
|
||||
}
|
||||
QuickFixAction.registerQuickFixAction(info, QUICK_FIX_FACTORY.createAddSwitchDefaultFix(switchBlock));
|
||||
results.add(info);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -47,6 +47,8 @@ import com.intellij.psi.util.PropertyMemberType;
|
||||
import com.intellij.refactoring.memberPushDown.JavaPushDownHandler;
|
||||
import com.intellij.util.DocumentUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import com.siyeh.ig.fixes.CreateDefaultBranchFix;
|
||||
import com.siyeh.ig.fixes.CreateMissingSwitchBranchesFix;
|
||||
import org.jetbrains.annotations.Nls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -933,4 +935,14 @@ public class QuickFixFactoryImpl extends QuickFixFactory {
|
||||
public IntentionAction createSameErasureButDifferentMethodsFix(@NotNull PsiMethod method, @NotNull PsiMethod superMethod) {
|
||||
return new SameErasureButDifferentMethodsFix(method, superMethod);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IntentionAction createAddMissingEnumBranchesFix(@NotNull PsiSwitchBlock switchBlock, @NotNull Set<String> missingCases) {
|
||||
return new CreateMissingSwitchBranchesFix(switchBlock, missingCases);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IntentionAction createAddSwitchDefaultFix(@NotNull PsiSwitchBlock switchBlock) {
|
||||
return new CreateDefaultBranchFix(switchBlock);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,101 +0,0 @@
|
||||
/*
|
||||
* Copyright 2003-2017 Dave Griffith, Bas Leijdekkers
|
||||
*
|
||||
* 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.siyeh.ig.controlflow;
|
||||
|
||||
import com.intellij.codeInspection.ui.SingleCheckboxOptionsPanel;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.siyeh.InspectionGadgetsBundle;
|
||||
import com.siyeh.ig.BaseInspection;
|
||||
import com.siyeh.ig.BaseInspectionVisitor;
|
||||
import com.siyeh.ig.psiutils.SwitchUtils;
|
||||
import one.util.streamex.StreamEx;
|
||||
import org.intellij.lang.annotations.Pattern;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class SwitchStatementsWithoutDefaultInspection extends BaseInspection {
|
||||
|
||||
@SuppressWarnings("PublicField")
|
||||
public boolean m_ignoreFullyCoveredEnums = true;
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String getDisplayName() {
|
||||
return InspectionGadgetsBundle.message("switch.statements.without.default.display.name");
|
||||
}
|
||||
|
||||
@Pattern(VALID_ID_PATTERN)
|
||||
@Override
|
||||
@NotNull
|
||||
public String getID() {
|
||||
return "SwitchStatementWithoutDefaultBranch";
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
protected String buildErrorString(Object... infos) {
|
||||
return InspectionGadgetsBundle.message("switch.statements.without.default.problem.descriptor");
|
||||
}
|
||||
|
||||
@Override
|
||||
public JComponent createOptionsPanel() {
|
||||
return new SingleCheckboxOptionsPanel(InspectionGadgetsBundle.message("switch.statement.without.default.ignore.option"),
|
||||
this, "m_ignoreFullyCoveredEnums");
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseInspectionVisitor buildVisitor() {
|
||||
return new SwitchStatementsWithoutDefaultVisitor();
|
||||
}
|
||||
|
||||
private class SwitchStatementsWithoutDefaultVisitor extends BaseInspectionVisitor {
|
||||
|
||||
@Override
|
||||
public void visitSwitchStatement(@NotNull PsiSwitchStatement statement) {
|
||||
super.visitSwitchStatement(statement);
|
||||
final int count = SwitchUtils.calculateBranchCount(statement);
|
||||
if (count <= 0) {
|
||||
return;
|
||||
}
|
||||
if (m_ignoreFullyCoveredEnums && switchStatementIsFullyCoveredEnum(statement)) {
|
||||
return;
|
||||
}
|
||||
registerStatementError(statement);
|
||||
}
|
||||
|
||||
private boolean switchStatementIsFullyCoveredEnum(PsiSwitchStatement statement) {
|
||||
final PsiExpression expression = statement.getExpression();
|
||||
if (expression == null) {
|
||||
return true; // don't warn on incomplete code
|
||||
}
|
||||
final PsiClass aClass = PsiUtil.resolveClassInClassTypeOnly(expression.getType());
|
||||
if (aClass == null || !aClass.isEnum()) return false;
|
||||
List<PsiSwitchLabelStatementBase> labels = PsiTreeUtil.getChildrenOfTypeAsList(statement.getBody(), PsiSwitchLabelStatementBase.class);
|
||||
Set<PsiEnumConstant> constants = StreamEx.of(labels).flatCollection(SwitchUtils::findEnumConstants).toSet();
|
||||
for (PsiField field : aClass.getFields()) {
|
||||
if (field instanceof PsiEnumConstant && !constants.remove(field)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -754,8 +754,8 @@
|
||||
implementationClass="com.siyeh.ig.controlflow.SwitchStatementWithTooManyBranchesInspection"/>
|
||||
<localInspection groupPath="Java" language="JAVA" suppressId="SwitchStatementWithoutDefaultBranch" shortName="SwitchStatementsWithoutDefault"
|
||||
bundle="com.siyeh.InspectionGadgetsBundle" key="switch.statements.without.default.display.name"
|
||||
groupBundle="messages.InspectionsBundle" groupKey="group.names.control.flow.issues" enabledByDefault="false"
|
||||
level="WARNING" implementationClass="com.siyeh.ig.controlflow.SwitchStatementsWithoutDefaultInspection"/>
|
||||
groupBundle="messages.InspectionsBundle" groupKey="group.names.control.flow.issues" enabledByDefault="true"
|
||||
level="INFORMATION" implementationClass="com.siyeh.ig.controlflow.SwitchStatementsWithoutDefaultInspection"/>
|
||||
<localInspection groupPath="Java" language="JAVA" suppressId="RedundantIfStatement" shortName="TrivialIf" bundle="com.siyeh.InspectionGadgetsBundle"
|
||||
key="trivial.if.display.name" groupBundle="messages.InspectionsBundle" groupKey="group.names.control.flow.issues"
|
||||
enabledByDefault="true" level="WARNING" implementationClass="com.siyeh.ig.controlflow.TrivialIfInspection" cleanupTool="true"/>
|
||||
|
||||
@@ -15,39 +15,27 @@
|
||||
*/
|
||||
package com.siyeh.ig.controlflow;
|
||||
|
||||
import com.intellij.codeInsight.intention.IntentionAction;
|
||||
import com.intellij.codeInsight.template.TemplateBuilder;
|
||||
import com.intellij.codeInsight.template.TemplateBuilderFactory;
|
||||
import com.intellij.codeInsight.template.impl.ConstantNode;
|
||||
import com.intellij.codeInspection.*;
|
||||
import com.intellij.codeInspection.AbstractBaseJavaLocalInspectionTool;
|
||||
import com.intellij.codeInspection.ProblemHighlightType;
|
||||
import com.intellij.codeInspection.ProblemsHolder;
|
||||
import com.intellij.codeInspection.dataFlow.CommonDataflow;
|
||||
import com.intellij.codeInspection.ui.SingleCheckboxOptionsPanel;
|
||||
import com.intellij.lang.injection.InjectedLanguageManager;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.fileEditor.FileEditorManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Couple;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.profile.codeInspection.InspectionProjectProfileManager;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.siyeh.InspectionGadgetsBundle;
|
||||
import com.siyeh.ig.psiutils.CommentTracker;
|
||||
import com.siyeh.ig.fixes.CreateMissingSwitchBranchesFix;
|
||||
import com.siyeh.ig.psiutils.SwitchUtils;
|
||||
import com.siyeh.ig.psiutils.TypeUtils;
|
||||
import one.util.streamex.Joining;
|
||||
import one.util.streamex.StreamEx;
|
||||
import org.jetbrains.annotations.Nls;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.util.*;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class EnumSwitchStatementWhichMissesCasesInspection extends AbstractBaseJavaLocalInspectionTool {
|
||||
|
||||
@@ -66,15 +54,10 @@ public class EnumSwitchStatementWhichMissesCasesInspection extends AbstractBaseJ
|
||||
return InspectionGadgetsBundle
|
||||
.message("enum.switch.statement.which.misses.cases.problem.descriptor.single", enumName, names.iterator().next());
|
||||
}
|
||||
String namesString = formatMissingBranches(names);
|
||||
String namesString = CreateMissingSwitchBranchesFix.formatMissingBranches(names);
|
||||
return InspectionGadgetsBundle.message("enum.switch.statement.which.misses.cases.problem.descriptor", enumName, namesString);
|
||||
}
|
||||
|
||||
static String formatMissingBranches(Set<String> names) {
|
||||
return StreamEx.of(names).map(name -> "'" + name + "'").mapLast("and "::concat)
|
||||
.collect(Joining.with(", ").maxChars(50).cutAfterDelimiter());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public JComponent createOptionsPanel() {
|
||||
@@ -116,7 +99,7 @@ public class EnumSwitchStatementWhichMissesCasesInspection extends AbstractBaseJ
|
||||
}
|
||||
continue;
|
||||
}
|
||||
List<PsiEnumConstant> enumConstants = findEnumConstants(child);
|
||||
List<PsiEnumConstant> enumConstants = SwitchUtils.findEnumConstants(child);
|
||||
if (enumConstants.isEmpty()) {
|
||||
// Syntax error or unresolved constant: do not report anything on incomplete code
|
||||
return;
|
||||
@@ -162,233 +145,4 @@ public class EnumSwitchStatementWhichMissesCasesInspection extends AbstractBaseJ
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static class CreateMissingSwitchBranchesFix implements LocalQuickFix, IntentionAction {
|
||||
private final Set<String> myNames;
|
||||
private final SmartPsiElementPointer<PsiSwitchBlock> myBlock;
|
||||
|
||||
private CreateMissingSwitchBranchesFix(@NotNull PsiSwitchBlock block, Set<String> names) {
|
||||
myBlock = SmartPointerManager.createPointer(block);
|
||||
myNames = names;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getText() {
|
||||
return getName();
|
||||
}
|
||||
|
||||
@Nls(capitalization = Nls.Capitalization.Sentence)
|
||||
@NotNull
|
||||
@Override
|
||||
public String getName() {
|
||||
if (myNames.size() == 1) {
|
||||
return "Create missing switch branch '" + myNames.iterator().next() + "'";
|
||||
}
|
||||
return "Create missing branches: " + formatMissingBranches(myNames);
|
||||
}
|
||||
|
||||
@Nls(capitalization = Nls.Capitalization.Sentence)
|
||||
@NotNull
|
||||
@Override
|
||||
public String getFamilyName() {
|
||||
return "Create enum switch branches";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
|
||||
invoke();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException {
|
||||
invoke();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean startInWriteAction() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void invoke() {
|
||||
PsiSwitchBlock switchBlock = myBlock.getElement();
|
||||
if (switchBlock == null) return;
|
||||
final PsiCodeBlock body = switchBlock.getBody();
|
||||
final PsiExpression switchExpression = switchBlock.getExpression();
|
||||
if (switchExpression == null) return;
|
||||
final PsiClassType switchType = (PsiClassType)switchExpression.getType();
|
||||
if (switchType == null) return;
|
||||
final PsiClass enumClass = switchType.resolve();
|
||||
if (enumClass == null) return;
|
||||
boolean isRuleBasedFormat = SwitchUtils.isRuleFormatSwitch(switchBlock);
|
||||
if (body == null) {
|
||||
// replace entire switch statement if no code block is present
|
||||
@NonNls final StringBuilder newStatementText = new StringBuilder();
|
||||
CommentTracker commentTracker = new CommentTracker();
|
||||
newStatementText.append("switch(").append(commentTracker.text(switchExpression)).append("){");
|
||||
for (String missingName : myNames) {
|
||||
newStatementText.append(String.join("", generateStatements(missingName, switchBlock, isRuleBasedFormat)));
|
||||
}
|
||||
newStatementText.append('}');
|
||||
commentTracker.replaceAndRestoreComments(switchBlock, newStatementText.toString());
|
||||
createTemplate(switchBlock);
|
||||
return;
|
||||
}
|
||||
List<PsiEnumConstant> allEnumConstants = StreamEx.of(enumClass.getAllFields()).select(PsiEnumConstant.class).toList();
|
||||
Map<PsiEnumConstant, PsiEnumConstant> nextEnumConstants =
|
||||
StreamEx.of(allEnumConstants).pairMap(Couple::of).toMap(c -> c.getFirst(), c -> c.getSecond());
|
||||
List<PsiEnumConstant> missingEnumElements = StreamEx.of(allEnumConstants).filter(c -> myNames.contains(c.getName())).toList();
|
||||
PsiEnumConstant nextEnumConstant = getNextEnumConstant(nextEnumConstants, missingEnumElements);
|
||||
PsiElement bodyElement = body.getFirstBodyElement();
|
||||
while (bodyElement != null) {
|
||||
List<PsiEnumConstant> constants = findEnumConstants(bodyElement);
|
||||
while (nextEnumConstant != null && constants.contains(nextEnumConstant)) {
|
||||
addSwitchLabelStatementBefore(missingEnumElements.get(0), bodyElement, switchBlock, isRuleBasedFormat);
|
||||
missingEnumElements.remove(0);
|
||||
if (missingEnumElements.isEmpty()) {
|
||||
break;
|
||||
}
|
||||
nextEnumConstant = getNextEnumConstant(nextEnumConstants, missingEnumElements);
|
||||
}
|
||||
if (isDefaultSwitchLabelStatement(bodyElement)) {
|
||||
for (PsiEnumConstant missingEnumElement : missingEnumElements) {
|
||||
addSwitchLabelStatementBefore(missingEnumElement, bodyElement, switchBlock, isRuleBasedFormat);
|
||||
}
|
||||
missingEnumElements.clear();
|
||||
break;
|
||||
}
|
||||
bodyElement = bodyElement.getNextSibling();
|
||||
}
|
||||
if (!missingEnumElements.isEmpty()) {
|
||||
final PsiElement lastChild = body.getLastChild();
|
||||
for (PsiEnumConstant missingEnumElement : missingEnumElements) {
|
||||
addSwitchLabelStatementBefore(missingEnumElement, lastChild, switchBlock, isRuleBasedFormat);
|
||||
}
|
||||
}
|
||||
createTemplate(switchBlock);
|
||||
}
|
||||
|
||||
private void createTemplate(@NotNull PsiSwitchBlock block) {
|
||||
if (!(block instanceof PsiSwitchExpression)) return;
|
||||
PsiFile file = block.getContainingFile();
|
||||
Project project = block.getProject();
|
||||
Editor editor = FileEditorManager.getInstance(project).getSelectedTextEditor();
|
||||
if (editor == null) return;
|
||||
Document document = editor.getDocument();
|
||||
PsiFile topLevelFile = InjectedLanguageManager.getInstance(project).getTopLevelFile(file);
|
||||
if (topLevelFile == null || document != topLevelFile.getViewProvider().getDocument()) return;
|
||||
PsiDocumentManager.getInstance(project).doPostponedOperationsAndUnblockDocument(document);
|
||||
TemplateBuilder builder = TemplateBuilderFactory.getInstance().createTemplateBuilder(block);
|
||||
List<PsiSwitchLabelStatementBase> labels = PsiTreeUtil.getChildrenOfTypeAsList(block.getBody(), PsiSwitchLabelStatementBase.class);
|
||||
List<PsiExpression> elementsToReplace = getElementsToReplace(labels);
|
||||
for (PsiExpression expression : elementsToReplace) {
|
||||
builder.replaceElement(expression, new ConstantNode(expression.getText()));
|
||||
}
|
||||
builder.run(editor, true);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private List<PsiExpression> getElementsToReplace(@NotNull List<PsiSwitchLabelStatementBase> labels) {
|
||||
List<PsiExpression> elementsToReplace = new ArrayList<>();
|
||||
for (PsiSwitchLabelStatementBase label : labels) {
|
||||
List<PsiEnumConstant> constants = findEnumConstants(label);
|
||||
if (constants.size() == 1 && myNames.contains(constants.get(0).getName())) {
|
||||
if (label instanceof PsiSwitchLabeledRuleStatement) {
|
||||
PsiStatement body = ((PsiSwitchLabeledRuleStatement)label).getBody();
|
||||
if (body instanceof PsiExpressionStatement) {
|
||||
ContainerUtil.addIfNotNull(elementsToReplace, ((PsiExpressionStatement)body).getExpression());
|
||||
}
|
||||
} else {
|
||||
PsiElement next = PsiTreeUtil.skipWhitespacesAndCommentsForward(label);
|
||||
if(next instanceof PsiBreakStatement) {
|
||||
ContainerUtil.addIfNotNull(elementsToReplace, ((PsiBreakStatement)next).getValueExpression());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return elementsToReplace;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
|
||||
PsiSwitchBlock startSwitch = myBlock.getElement();
|
||||
if (startSwitch == null) return false;
|
||||
int offset = Math.min(editor.getCaretModel().getOffset(), startSwitch.getTextRange().getEndOffset() - 1);
|
||||
PsiSwitchBlock currentSwitch = PsiTreeUtil.getNonStrictParentOfType(file.findElementAt(offset), PsiSwitchBlock.class);
|
||||
return currentSwitch == startSwitch;
|
||||
}
|
||||
|
||||
private static List<String> generateStatements(String name, PsiSwitchBlock switchBlock, boolean isRuleBasedFormat) {
|
||||
if (switchBlock instanceof PsiSwitchExpression) {
|
||||
String value = TypeUtils.getDefaultValue(((PsiSwitchExpression)switchBlock).getType());
|
||||
if (isRuleBasedFormat) {
|
||||
return Collections.singletonList("case "+name+" -> " + value + ";");
|
||||
} else {
|
||||
return Arrays.asList("case "+name+":", "break " + value + ";");
|
||||
}
|
||||
} else {
|
||||
if (isRuleBasedFormat) {
|
||||
return Collections.singletonList("case "+name+" -> {}");
|
||||
} else {
|
||||
return Arrays.asList("case "+name+":", "break;");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void addSwitchLabelStatementBefore(PsiEnumConstant missingEnumElement,
|
||||
PsiElement anchor,
|
||||
PsiSwitchBlock switchBlock,
|
||||
boolean isRuleBasedFormat) {
|
||||
if (anchor instanceof PsiSwitchLabelStatement) {
|
||||
PsiElement sibling = PsiTreeUtil.skipWhitespacesBackward(anchor);
|
||||
while (sibling instanceof PsiSwitchLabelStatement) {
|
||||
anchor = sibling;
|
||||
sibling = PsiTreeUtil.skipWhitespacesBackward(anchor);
|
||||
}
|
||||
}
|
||||
PsiElement correctedAnchor = anchor;
|
||||
final PsiElement parent = anchor.getParent();
|
||||
final PsiElementFactory factory = JavaPsiFacade.getElementFactory(anchor.getProject());
|
||||
generateStatements(missingEnumElement.getName(), switchBlock, isRuleBasedFormat).stream()
|
||||
.map(text -> factory.createStatementFromText(text, parent))
|
||||
.forEach(statement -> parent.addBefore(statement, correctedAnchor));
|
||||
}
|
||||
|
||||
private static PsiEnumConstant getNextEnumConstant(Map<PsiEnumConstant, PsiEnumConstant> nextEnumConstants,
|
||||
List<PsiEnumConstant> missingEnumElements) {
|
||||
PsiEnumConstant nextEnumConstant = nextEnumConstants.get(missingEnumElements.get(0));
|
||||
while (missingEnumElements.contains(nextEnumConstant)) {
|
||||
nextEnumConstant = nextEnumConstants.get(nextEnumConstant);
|
||||
}
|
||||
return nextEnumConstant;
|
||||
}
|
||||
|
||||
private static boolean isDefaultSwitchLabelStatement(PsiElement element) {
|
||||
return element instanceof PsiSwitchLabelStatementBase && ((PsiSwitchLabelStatementBase)element).isDefaultCase();
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static List<PsiEnumConstant> findEnumConstants(PsiElement element) {
|
||||
if (!(element instanceof PsiSwitchLabelStatementBase)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
final PsiSwitchLabelStatementBase switchLabelStatement = (PsiSwitchLabelStatementBase)element;
|
||||
final PsiExpressionList list = switchLabelStatement.getCaseValues();
|
||||
if (list == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<PsiEnumConstant> constants = new ArrayList<>();
|
||||
for (PsiExpression value : list.getExpressions()) {
|
||||
if (value instanceof PsiReferenceExpression) {
|
||||
final PsiElement target = ((PsiReferenceExpression)value).resolve();
|
||||
if (target instanceof PsiEnumConstant) {
|
||||
constants.add((PsiEnumConstant)target);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return constants;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright 2003-2017 Dave Griffith, Bas Leijdekkers
|
||||
*
|
||||
* 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.siyeh.ig.controlflow;
|
||||
|
||||
import com.intellij.codeInspection.AbstractBaseJavaLocalInspectionTool;
|
||||
import com.intellij.codeInspection.ProblemHighlightType;
|
||||
import com.intellij.codeInspection.ProblemsHolder;
|
||||
import com.intellij.codeInspection.ui.SingleCheckboxOptionsPanel;
|
||||
import com.intellij.profile.codeInspection.InspectionProjectProfileManager;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.siyeh.InspectionGadgetsBundle;
|
||||
import com.siyeh.ig.fixes.CreateDefaultBranchFix;
|
||||
import com.siyeh.ig.psiutils.SwitchUtils;
|
||||
import one.util.streamex.StreamEx;
|
||||
import org.intellij.lang.annotations.Pattern;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class SwitchStatementsWithoutDefaultInspection extends AbstractBaseJavaLocalInspectionTool {
|
||||
|
||||
@SuppressWarnings("PublicField")
|
||||
public boolean m_ignoreFullyCoveredEnums = true;
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String getDisplayName() {
|
||||
return InspectionGadgetsBundle.message("switch.statements.without.default.display.name");
|
||||
}
|
||||
|
||||
@Pattern(VALID_ID_PATTERN)
|
||||
@Override
|
||||
@NotNull
|
||||
public String getID() {
|
||||
return "SwitchStatementWithoutDefaultBranch";
|
||||
}
|
||||
|
||||
@Override
|
||||
public JComponent createOptionsPanel() {
|
||||
return new SingleCheckboxOptionsPanel(InspectionGadgetsBundle.message("switch.statement.without.default.ignore.option"),
|
||||
this, "m_ignoreFullyCoveredEnums");
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
|
||||
return new JavaElementVisitor() {
|
||||
@Override
|
||||
public void visitSwitchStatement(@NotNull PsiSwitchStatement statement) {
|
||||
super.visitSwitchStatement(statement);
|
||||
final int count = SwitchUtils.calculateBranchCount(statement);
|
||||
if (count < 0 || statement.getBody() == null) {
|
||||
return;
|
||||
}
|
||||
boolean infoMode = false;
|
||||
if (count == 0 || m_ignoreFullyCoveredEnums && switchStatementIsFullyCoveredEnum(statement)) {
|
||||
if (!isOnTheFly) return;
|
||||
infoMode = true;
|
||||
}
|
||||
String message = InspectionGadgetsBundle.message("switch.statements.without.default.problem.descriptor");
|
||||
if (infoMode || (isOnTheFly && InspectionProjectProfileManager.isInformationLevel(getShortName(), statement))) {
|
||||
holder.registerProblem(statement, message, ProblemHighlightType.INFORMATION, new CreateDefaultBranchFix(statement));
|
||||
} else {
|
||||
holder.registerProblem(statement.getFirstChild(), message, new CreateDefaultBranchFix(statement));
|
||||
}
|
||||
}
|
||||
|
||||
private boolean switchStatementIsFullyCoveredEnum(PsiSwitchStatement statement) {
|
||||
final PsiExpression expression = statement.getExpression();
|
||||
if (expression == null) {
|
||||
return true; // don't warn on incomplete code
|
||||
}
|
||||
final PsiClass aClass = PsiUtil.resolveClassInClassTypeOnly(expression.getType());
|
||||
if (aClass == null || !aClass.isEnum()) return false;
|
||||
List<PsiSwitchLabelStatementBase> labels = PsiTreeUtil.getChildrenOfTypeAsList(statement.getBody(), PsiSwitchLabelStatementBase.class);
|
||||
Set<PsiEnumConstant> constants = StreamEx.of(labels).flatCollection(SwitchUtils::findEnumConstants).toSet();
|
||||
for (PsiField field : aClass.getFields()) {
|
||||
if (field instanceof PsiEnumConstant && !constants.remove(field)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
// 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.fixes;
|
||||
|
||||
import com.intellij.codeInsight.intention.IntentionAction;
|
||||
import com.intellij.codeInspection.LocalQuickFix;
|
||||
import com.intellij.codeInspection.ProblemDescriptor;
|
||||
import com.intellij.lang.injection.InjectedLanguageManager;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.fileEditor.FileEditorManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public abstract class BaseSwitchFix implements LocalQuickFix, IntentionAction {
|
||||
protected final SmartPsiElementPointer<PsiSwitchBlock> myBlock;
|
||||
|
||||
public BaseSwitchFix(@NotNull PsiSwitchBlock block) {
|
||||
myBlock = SmartPointerManager.createPointer(block);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
|
||||
invoke();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException {
|
||||
invoke();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean startInWriteAction() {
|
||||
return true;
|
||||
}
|
||||
|
||||
abstract protected void invoke();
|
||||
|
||||
@Override
|
||||
public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
|
||||
PsiSwitchBlock startSwitch = myBlock.getElement();
|
||||
if (startSwitch == null) return false;
|
||||
int offset = Math.min(editor.getCaretModel().getOffset(), startSwitch.getTextRange().getEndOffset() - 1);
|
||||
PsiSwitchBlock currentSwitch = PsiTreeUtil.getNonStrictParentOfType(file.findElementAt(offset), PsiSwitchBlock.class);
|
||||
return currentSwitch == startSwitch;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
static Editor prepareForTemplateAndObtainEditor(@NotNull PsiElement element) {
|
||||
PsiFile file = element.getContainingFile();
|
||||
Project project = element.getProject();
|
||||
Editor editor = FileEditorManager.getInstance(project).getSelectedTextEditor();
|
||||
if (editor == null) return null;
|
||||
Document document = editor.getDocument();
|
||||
PsiFile topLevelFile = InjectedLanguageManager.getInstance(project).getTopLevelFile(file);
|
||||
if (topLevelFile == null || document != topLevelFile.getViewProvider().getDocument()) return null;
|
||||
PsiDocumentManager.getInstance(project).doPostponedOperationsAndUnblockDocument(document);
|
||||
return editor;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
// 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.fixes;
|
||||
|
||||
import com.intellij.codeInsight.template.TemplateBuilder;
|
||||
import com.intellij.codeInsight.template.TemplateBuilderFactory;
|
||||
import com.intellij.codeInsight.template.impl.ConstantNode;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.siyeh.ig.psiutils.SwitchUtils;
|
||||
import com.siyeh.ig.psiutils.TypeUtils;
|
||||
import org.jetbrains.annotations.Nls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class CreateDefaultBranchFix extends BaseSwitchFix {
|
||||
|
||||
public CreateDefaultBranchFix(@NotNull PsiSwitchBlock block) {
|
||||
super(block);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getText() {
|
||||
return getName();
|
||||
}
|
||||
|
||||
@Nls(capitalization = Nls.Capitalization.Sentence)
|
||||
@NotNull
|
||||
@Override
|
||||
public String getFamilyName() {
|
||||
return "Insert 'default' branch";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void invoke() {
|
||||
PsiSwitchBlock switchBlock = myBlock.getElement();
|
||||
if (switchBlock == null) return;
|
||||
PsiCodeBlock body = switchBlock.getBody();
|
||||
if (body == null) return;
|
||||
if (SwitchUtils.calculateBranchCount(switchBlock) < 0) {
|
||||
// Default already present for some reason
|
||||
return;
|
||||
}
|
||||
PsiExpression switchExpression = switchBlock.getExpression();
|
||||
if (switchExpression == null) return;
|
||||
boolean isRuleBasedFormat = SwitchUtils.isRuleFormatSwitch(switchBlock);
|
||||
PsiElement anchor = body.getLastChild();
|
||||
if (anchor == null) return;
|
||||
PsiElement parent = anchor.getParent();
|
||||
PsiElementFactory factory = JavaPsiFacade.getElementFactory(anchor.getProject());
|
||||
generateStatements(switchBlock, isRuleBasedFormat).stream()
|
||||
.map(text -> factory.createStatementFromText(text, parent))
|
||||
.forEach(statement -> parent.addBefore(statement, anchor));
|
||||
adjustEditor(switchBlock);
|
||||
}
|
||||
|
||||
private static void adjustEditor(@NotNull PsiSwitchBlock block) {
|
||||
PsiCodeBlock body = block.getBody();
|
||||
if (body == null) return;
|
||||
Editor editor = prepareForTemplateAndObtainEditor(block);
|
||||
if (editor == null) return;
|
||||
PsiStatement lastStatement = ArrayUtil.getLastElement(body.getStatements());
|
||||
PsiExpression expression = null;
|
||||
int offset = -1;
|
||||
if (lastStatement instanceof PsiBreakStatement) {
|
||||
expression = ((PsiBreakStatement)lastStatement).getExpression();
|
||||
} else if (lastStatement instanceof PsiSwitchLabeledRuleStatement) {
|
||||
PsiStatement ruleBody = ((PsiSwitchLabeledRuleStatement)lastStatement).getBody();
|
||||
if (ruleBody instanceof PsiExpressionStatement) {
|
||||
expression = ((PsiExpressionStatement)ruleBody).getExpression();
|
||||
} else if (ruleBody instanceof PsiBlockStatement) {
|
||||
offset = ruleBody.getTextRange().getStartOffset()+1;
|
||||
}
|
||||
} else if (lastStatement instanceof PsiSwitchLabelStatement) {
|
||||
offset = lastStatement.getTextRange().getEndOffset();
|
||||
}
|
||||
if (expression == null) {
|
||||
if (offset != -1) {
|
||||
editor.getCaretModel().moveToOffset(offset);
|
||||
}
|
||||
} else {
|
||||
TemplateBuilder builder = TemplateBuilderFactory.getInstance().createTemplateBuilder(block);
|
||||
builder.replaceElement(expression, new ConstantNode(expression.getText()));
|
||||
builder.run(editor, true);
|
||||
}
|
||||
}
|
||||
|
||||
private static List<String> generateStatements(PsiSwitchBlock switchBlock, boolean isRuleBasedFormat) {
|
||||
if (switchBlock instanceof PsiSwitchExpression) {
|
||||
String value = TypeUtils.getDefaultValue(((PsiSwitchExpression)switchBlock).getType());
|
||||
if (isRuleBasedFormat) {
|
||||
return Collections.singletonList("default -> " + value + ";");
|
||||
} else {
|
||||
return Arrays.asList("default:", "break " + value + ";");
|
||||
}
|
||||
} else {
|
||||
if (isRuleBasedFormat) {
|
||||
return Collections.singletonList("default -> {}");
|
||||
} else {
|
||||
return Collections.singletonList("default:");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,201 @@
|
||||
// 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.fixes;
|
||||
|
||||
import com.intellij.codeInsight.template.TemplateBuilder;
|
||||
import com.intellij.codeInsight.template.TemplateBuilderFactory;
|
||||
import com.intellij.codeInsight.template.impl.ConstantNode;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.util.Couple;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.siyeh.ig.psiutils.CommentTracker;
|
||||
import com.siyeh.ig.psiutils.SwitchUtils;
|
||||
import com.siyeh.ig.psiutils.TypeUtils;
|
||||
import one.util.streamex.Joining;
|
||||
import one.util.streamex.StreamEx;
|
||||
import org.jetbrains.annotations.Nls;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class CreateMissingSwitchBranchesFix extends BaseSwitchFix {
|
||||
private final Set<String> myNames;
|
||||
|
||||
public CreateMissingSwitchBranchesFix(@NotNull PsiSwitchBlock block, Set<String> names) {
|
||||
super(block);
|
||||
myNames = names;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getText() {
|
||||
return getName();
|
||||
}
|
||||
|
||||
@Nls(capitalization = Nls.Capitalization.Sentence)
|
||||
@NotNull
|
||||
@Override
|
||||
public String getName() {
|
||||
if (myNames.size() == 1) {
|
||||
return "Create missing switch branch '" + myNames.iterator().next() + "'";
|
||||
}
|
||||
return "Create missing branches: " + formatMissingBranches(myNames);
|
||||
}
|
||||
|
||||
@Nls(capitalization = Nls.Capitalization.Sentence)
|
||||
@NotNull
|
||||
@Override
|
||||
public String getFamilyName() {
|
||||
return "Create enum switch branches";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void invoke() {
|
||||
PsiSwitchBlock switchBlock = myBlock.getElement();
|
||||
if (switchBlock == null) return;
|
||||
final PsiCodeBlock body = switchBlock.getBody();
|
||||
final PsiExpression switchExpression = switchBlock.getExpression();
|
||||
if (switchExpression == null) return;
|
||||
final PsiClassType switchType = (PsiClassType)switchExpression.getType();
|
||||
if (switchType == null) return;
|
||||
final PsiClass enumClass = switchType.resolve();
|
||||
if (enumClass == null) return;
|
||||
boolean isRuleBasedFormat = SwitchUtils.isRuleFormatSwitch(switchBlock);
|
||||
if (body == null) {
|
||||
// replace entire switch statement if no code block is present
|
||||
@NonNls final StringBuilder newStatementText = new StringBuilder();
|
||||
CommentTracker commentTracker = new CommentTracker();
|
||||
newStatementText.append("switch(").append(commentTracker.text(switchExpression)).append("){");
|
||||
for (String missingName : myNames) {
|
||||
newStatementText.append(String.join("", generateStatements(missingName, switchBlock, isRuleBasedFormat)));
|
||||
}
|
||||
newStatementText.append('}');
|
||||
commentTracker.replaceAndRestoreComments(switchBlock, newStatementText.toString());
|
||||
createTemplate(switchBlock);
|
||||
return;
|
||||
}
|
||||
List<PsiEnumConstant> allEnumConstants = StreamEx.of(enumClass.getAllFields()).select(PsiEnumConstant.class).toList();
|
||||
Map<PsiEnumConstant, PsiEnumConstant> nextEnumConstants =
|
||||
StreamEx.of(allEnumConstants).pairMap(Couple::of).toMap(c -> c.getFirst(), c -> c.getSecond());
|
||||
List<PsiEnumConstant> missingEnumElements = StreamEx.of(allEnumConstants).filter(c -> myNames.contains(c.getName())).toList();
|
||||
PsiEnumConstant nextEnumConstant = getNextEnumConstant(nextEnumConstants, missingEnumElements);
|
||||
PsiElement bodyElement = body.getFirstBodyElement();
|
||||
while (bodyElement != null) {
|
||||
List<PsiEnumConstant> constants = SwitchUtils.findEnumConstants(bodyElement);
|
||||
while (nextEnumConstant != null && constants.contains(nextEnumConstant)) {
|
||||
addSwitchLabelStatementBefore(missingEnumElements.get(0), bodyElement, switchBlock, isRuleBasedFormat);
|
||||
missingEnumElements.remove(0);
|
||||
if (missingEnumElements.isEmpty()) {
|
||||
break;
|
||||
}
|
||||
nextEnumConstant = getNextEnumConstant(nextEnumConstants, missingEnumElements);
|
||||
}
|
||||
if (isDefaultSwitchLabelStatement(bodyElement)) {
|
||||
for (PsiEnumConstant missingEnumElement : missingEnumElements) {
|
||||
addSwitchLabelStatementBefore(missingEnumElement, bodyElement, switchBlock, isRuleBasedFormat);
|
||||
}
|
||||
missingEnumElements.clear();
|
||||
break;
|
||||
}
|
||||
bodyElement = bodyElement.getNextSibling();
|
||||
}
|
||||
if (!missingEnumElements.isEmpty()) {
|
||||
final PsiElement lastChild = body.getLastChild();
|
||||
for (PsiEnumConstant missingEnumElement : missingEnumElements) {
|
||||
addSwitchLabelStatementBefore(missingEnumElement, lastChild, switchBlock, isRuleBasedFormat);
|
||||
}
|
||||
}
|
||||
createTemplate(switchBlock);
|
||||
}
|
||||
|
||||
private void createTemplate(@NotNull PsiSwitchBlock block) {
|
||||
if (!(block instanceof PsiSwitchExpression)) return;
|
||||
Editor editor = BaseSwitchFix.prepareForTemplateAndObtainEditor(block);
|
||||
if (editor == null) return;
|
||||
TemplateBuilder builder = TemplateBuilderFactory.getInstance().createTemplateBuilder(block);
|
||||
List<PsiSwitchLabelStatementBase> labels = PsiTreeUtil.getChildrenOfTypeAsList(block.getBody(), PsiSwitchLabelStatementBase.class);
|
||||
List<PsiExpression> elementsToReplace = getElementsToReplace(labels);
|
||||
for (PsiExpression expression : elementsToReplace) {
|
||||
builder.replaceElement(expression, new ConstantNode(expression.getText()));
|
||||
}
|
||||
builder.run(editor, true);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private List<PsiExpression> getElementsToReplace(@NotNull List<PsiSwitchLabelStatementBase> labels) {
|
||||
List<PsiExpression> elementsToReplace = new ArrayList<>();
|
||||
for (PsiSwitchLabelStatementBase label : labels) {
|
||||
List<PsiEnumConstant> constants = SwitchUtils.findEnumConstants(label);
|
||||
if (constants.size() == 1 && myNames.contains(constants.get(0).getName())) {
|
||||
if (label instanceof PsiSwitchLabeledRuleStatement) {
|
||||
PsiStatement body = ((PsiSwitchLabeledRuleStatement)label).getBody();
|
||||
if (body instanceof PsiExpressionStatement) {
|
||||
ContainerUtil.addIfNotNull(elementsToReplace, ((PsiExpressionStatement)body).getExpression());
|
||||
}
|
||||
} else {
|
||||
PsiElement next = PsiTreeUtil.skipWhitespacesAndCommentsForward(label);
|
||||
if(next instanceof PsiBreakStatement) {
|
||||
ContainerUtil.addIfNotNull(elementsToReplace, ((PsiBreakStatement)next).getValueExpression());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return elementsToReplace;
|
||||
}
|
||||
|
||||
private static List<String> generateStatements(String name, PsiSwitchBlock switchBlock, boolean isRuleBasedFormat) {
|
||||
if (switchBlock instanceof PsiSwitchExpression) {
|
||||
String value = TypeUtils.getDefaultValue(((PsiSwitchExpression)switchBlock).getType());
|
||||
if (isRuleBasedFormat) {
|
||||
return Collections.singletonList("case " + name + " -> " + value + ";");
|
||||
} else {
|
||||
return Arrays.asList("case "+name+":", "break " + value + ";");
|
||||
}
|
||||
} else {
|
||||
if (isRuleBasedFormat) {
|
||||
return Collections.singletonList("case "+name+" -> {}");
|
||||
} else {
|
||||
return Arrays.asList("case "+name+":", "break;");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void addSwitchLabelStatementBefore(PsiEnumConstant missingEnumElement,
|
||||
PsiElement anchor,
|
||||
PsiSwitchBlock switchBlock,
|
||||
boolean isRuleBasedFormat) {
|
||||
if (anchor instanceof PsiSwitchLabelStatement) {
|
||||
PsiElement sibling = PsiTreeUtil.skipWhitespacesBackward(anchor);
|
||||
while (sibling instanceof PsiSwitchLabelStatement) {
|
||||
anchor = sibling;
|
||||
sibling = PsiTreeUtil.skipWhitespacesBackward(anchor);
|
||||
}
|
||||
}
|
||||
PsiElement correctedAnchor = anchor;
|
||||
final PsiElement parent = anchor.getParent();
|
||||
final PsiElementFactory factory = JavaPsiFacade.getElementFactory(anchor.getProject());
|
||||
generateStatements(missingEnumElement.getName(), switchBlock, isRuleBasedFormat).stream()
|
||||
.map(text -> factory.createStatementFromText(text, parent))
|
||||
.forEach(statement -> parent.addBefore(statement, correctedAnchor));
|
||||
}
|
||||
|
||||
private static PsiEnumConstant getNextEnumConstant(Map<PsiEnumConstant, PsiEnumConstant> nextEnumConstants,
|
||||
List<PsiEnumConstant> missingEnumElements) {
|
||||
PsiEnumConstant nextEnumConstant = nextEnumConstants.get(missingEnumElements.get(0));
|
||||
while (missingEnumElements.contains(nextEnumConstant)) {
|
||||
nextEnumConstant = nextEnumConstants.get(nextEnumConstant);
|
||||
}
|
||||
return nextEnumConstant;
|
||||
}
|
||||
|
||||
private static boolean isDefaultSwitchLabelStatement(PsiElement element) {
|
||||
return element instanceof PsiSwitchLabelStatementBase && ((PsiSwitchLabelStatementBase)element).isDefaultCase();
|
||||
}
|
||||
|
||||
public static String formatMissingBranches(Set<String> names) {
|
||||
return StreamEx.of(names).map(name -> "'" + name + "'").mapLast("and "::concat)
|
||||
.collect(Joining.with(", ").maxChars(50).cutAfterDelimiter());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// "Insert 'default' branch" "true"
|
||||
class X {
|
||||
void test(int i) {
|
||||
switch(i) {
|
||||
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// "Insert 'default' branch" "true"
|
||||
class X {
|
||||
void test(int i) {
|
||||
switch(i) {
|
||||
|
||||
default -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// "Insert 'default' branch" "true"
|
||||
class X {
|
||||
int test(int i) {
|
||||
return switch(i) {
|
||||
case 1 -> 2;
|
||||
default -> 0;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// "Insert 'default' branch" "true"
|
||||
class X {
|
||||
void test(int i, int j) {
|
||||
switch(i) {
|
||||
case 0:
|
||||
|
||||
switch (j) {
|
||||
default: break;
|
||||
}
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// "Insert 'default' branch" "true"
|
||||
class X {
|
||||
void test(int i) {
|
||||
switch(i) {
|
||||
case 0 -> System.out.println("oops");
|
||||
default -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// "Insert 'default' branch" "true"
|
||||
class X {
|
||||
void test(int i) {
|
||||
switch(i) {
|
||||
case 0:break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
// "Insert 'default' branch" "true"
|
||||
class X {
|
||||
void test(int i) {
|
||||
switch(i) {
|
||||
<caret>
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
// "Insert 'default' branch" "true"
|
||||
class X {
|
||||
void test(int i) {
|
||||
switch(i) {
|
||||
<caret>
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
// "Insert 'default' branch" "true"
|
||||
class X {
|
||||
int test(int i) {
|
||||
return switch(<caret>i) {
|
||||
case 1 -> 2;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
// "Insert 'default' branch" "false"
|
||||
class X {
|
||||
void test(int i, int j) {
|
||||
switch(i) {
|
||||
case 0:
|
||||
switch (j) {
|
||||
<caret>
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
// "Insert 'default' branch" "true"
|
||||
class X {
|
||||
void test(int i, int j) {
|
||||
switch(i) {
|
||||
case 0:
|
||||
<caret>
|
||||
switch (j) {
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
// "Insert 'default' branch" "true"
|
||||
class X {
|
||||
void test(int i) {
|
||||
switch(i) {
|
||||
case 0 -> System.out.println("oops");<caret>
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
// "Insert 'default' branch" "true"
|
||||
class X {
|
||||
void test(int i) {
|
||||
switch(i) {
|
||||
case 0:<caret>break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
// 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.fixes.controlflow;
|
||||
|
||||
import com.intellij.codeInsight.daemon.quickFix.LightQuickFixParameterizedTestCase;
|
||||
import com.intellij.codeInspection.LocalInspectionTool;
|
||||
import com.intellij.openapi.application.ex.PathManagerEx;
|
||||
import com.siyeh.ig.LightInspectionTestCase;
|
||||
import com.siyeh.ig.controlflow.SwitchStatementsWithoutDefaultInspection;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class CreateDefaultBranchFixTest extends LightQuickFixParameterizedTestCase {
|
||||
@NotNull
|
||||
@Override
|
||||
protected LocalInspectionTool[] configureLocalInspectionTools() {
|
||||
return new SwitchStatementsWithoutDefaultInspection[]{new SwitchStatementsWithoutDefaultInspection()};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getBasePath() {
|
||||
return "/com/siyeh/igfixes/controlflow/create_default";
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected String getTestDataPath() {
|
||||
return PathManagerEx.getCommunityHomePath() + LightInspectionTestCase.INSPECTION_GADGETS_TEST_DATA_PATH;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user