mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[groovy] make semicolon necessary by default (IDEA-192360)
- highlight only known unnecessary semicolon cases; - move messages to bundle; - extract the fix to separate class.
This commit is contained in:
+3
-1
@@ -123,4 +123,6 @@ unnecessary.modifier.description=Modifier ''{0}'' is not necessary
|
||||
unnecessary.modifier.remove=Remove unnecessary ''{0}''
|
||||
unnecessary.def.explicitly.typed.only=Only report in explicitly typed declarations
|
||||
unnecessary.alias.fix=Remove unnecessary alias
|
||||
unnecessary.alias.description=Alias is unnecessary
|
||||
unnecessary.alias.description=Alias is unnecessary
|
||||
unnecessary.semicolon.description=Semicolon is unnecessary
|
||||
unnecessary.semicolon.fix=Remove semicolon
|
||||
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
// 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 org.jetbrains.plugins.groovy.codeInspection.fixes
|
||||
|
||||
import com.intellij.codeInspection.LocalQuickFix
|
||||
import com.intellij.codeInspection.ProblemDescriptor
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.codeStyle.CodeStyleManager
|
||||
|
||||
class RemoveElementWithoutFormatterFix(private val familyName: String) : LocalQuickFix {
|
||||
|
||||
override fun getFamilyName(): String = familyName
|
||||
|
||||
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
|
||||
val element = descriptor.psiElement ?: return
|
||||
CodeStyleManager.getInstance(project).performActionWithFormatterDisabled {
|
||||
element.delete()
|
||||
}
|
||||
}
|
||||
}
|
||||
-84
@@ -1,84 +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 org.jetbrains.plugins.groovy.codeInspection.style;
|
||||
|
||||
import com.intellij.codeInspection.*;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiElementVisitor;
|
||||
import com.intellij.psi.codeStyle.CodeStyleManager;
|
||||
import com.intellij.psi.tree.TokenSet;
|
||||
import org.jetbrains.annotations.Nls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.plugins.groovy.codeInspection.GroovySuppressableInspectionTool;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrStatement;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.clauses.GrTraditionalForClause;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil;
|
||||
|
||||
import static com.intellij.psi.tree.TokenSet.andNot;
|
||||
import static com.intellij.psi.tree.TokenSet.orSet;
|
||||
import static org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes.mNLS;
|
||||
import static org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes.mSEMI;
|
||||
import static org.jetbrains.plugins.groovy.lang.lexer.TokenSets.WHITE_SPACES_OR_COMMENTS;
|
||||
|
||||
public class GrUnnecessarySemicolonInspection extends GroovySuppressableInspectionTool implements CleanupLocalInspectionTool {
|
||||
|
||||
private static final TokenSet NLS_SET = TokenSet.create(mNLS);
|
||||
private static final TokenSet FORWARD_SET = andNot(orSet(WHITE_SPACES_OR_COMMENTS, TokenSet.create(mSEMI)), NLS_SET);
|
||||
private static final TokenSet BACKWARD_SET = andNot(WHITE_SPACES_OR_COMMENTS, NLS_SET);
|
||||
|
||||
private static final LocalQuickFix FIX = new LocalQuickFix() {
|
||||
|
||||
@Nls
|
||||
@NotNull
|
||||
@Override
|
||||
public String getFamilyName() {
|
||||
return "Remove semicolon";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
|
||||
PsiElement element = descriptor.getPsiElement();
|
||||
if (element != null && element.getNode().getElementType() == mSEMI) {
|
||||
CodeStyleManager.getInstance(project).performActionWithFormatterDisabled((Runnable)element::delete);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
|
||||
return new PsiElementVisitor() {
|
||||
@Override
|
||||
public void visitElement(PsiElement element) {
|
||||
if (element.getNode().getElementType() != mSEMI) return;
|
||||
if (isSemicolonNecessary(element)) return;
|
||||
holder.registerProblem(element, "Semicolon is unnecessary", ProblemHighlightType.LIKE_UNUSED_SYMBOL, FIX);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static boolean isSemicolonNecessary(@NotNull PsiElement semicolon) {
|
||||
if (semicolon.getParent() instanceof GrTraditionalForClause) return true;
|
||||
|
||||
PsiElement prevSibling = PsiUtil.skipSet(semicolon, false, BACKWARD_SET);
|
||||
PsiElement nextSibling = PsiUtil.skipSet(semicolon, true, FORWARD_SET);
|
||||
return prevSibling instanceof GrStatement && (
|
||||
nextSibling instanceof GrStatement || nextSibling != null && nextSibling.getNextSibling() instanceof GrClosableBlock
|
||||
);
|
||||
}
|
||||
}
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
// 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 org.jetbrains.plugins.groovy.codeInspection.style
|
||||
|
||||
import com.intellij.codeInspection.CleanupLocalInspectionTool
|
||||
import com.intellij.codeInspection.ProblemHighlightType
|
||||
import com.intellij.codeInspection.ProblemsHolder
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiElementVisitor
|
||||
import org.jetbrains.plugins.groovy.codeInspection.GroovyInspectionBundle.message
|
||||
import org.jetbrains.plugins.groovy.codeInspection.GroovySuppressableInspectionTool
|
||||
import org.jetbrains.plugins.groovy.codeInspection.fixes.RemoveElementWithoutFormatterFix
|
||||
import org.jetbrains.plugins.groovy.lang.lexer.TokenSets.WHITE_SPACES_OR_COMMENTS
|
||||
import org.jetbrains.plugins.groovy.lang.psi.GroovyElementTypes.*
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrStatement
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.clauses.GrTraditionalForClause
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.params.GrParameterList
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrEnumConstantList
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.toplevel.imports.GrImportStatement
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.toplevel.packaging.GrPackageDefinition
|
||||
import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil.skipSet
|
||||
import org.jetbrains.plugins.groovy.util.TokenSet
|
||||
import org.jetbrains.plugins.groovy.util.minus
|
||||
import org.jetbrains.plugins.groovy.util.plus
|
||||
|
||||
class GrUnnecessarySemicolonInspection : GroovySuppressableInspectionTool(), CleanupLocalInspectionTool {
|
||||
|
||||
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor = object : PsiElementVisitor() {
|
||||
override fun visitElement(element: PsiElement) {
|
||||
if (element.node.elementType !== T_SEMI || isSemicolonNecessary(element)) return
|
||||
holder.registerProblem(
|
||||
element,
|
||||
message("unnecessary.semicolon.description"),
|
||||
ProblemHighlightType.LIKE_UNUSED_SYMBOL,
|
||||
fix
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private val fix = RemoveElementWithoutFormatterFix(message("unnecessary.semicolon.fix"))
|
||||
private val nlSet = TokenSet(NL)
|
||||
private val forwardSet = WHITE_SPACES_OR_COMMENTS + TokenSet(T_SEMI) - nlSet
|
||||
private val backwardSet = WHITE_SPACES_OR_COMMENTS - nlSet
|
||||
private val separators = TokenSet(NL, T_SEMI)
|
||||
private val previousSet = TokenSet(T_LBRACE, T_ARR)
|
||||
|
||||
private fun isSemicolonNecessary(semicolon: PsiElement): Boolean {
|
||||
if (semicolon.parent is GrTraditionalForClause) return true
|
||||
|
||||
val prevSibling = skipSet(semicolon, false, backwardSet) ?: return false
|
||||
val nextSibling = skipSet(semicolon, true, forwardSet) ?: return false
|
||||
|
||||
val prevType = prevSibling.node.elementType
|
||||
|
||||
return when {
|
||||
prevType in separators -> {
|
||||
prevSibling.prevSibling is GrEnumConstantList
|
||||
}
|
||||
prevType in previousSet -> {
|
||||
false
|
||||
}
|
||||
prevSibling is GrStatement -> {
|
||||
nextSibling is GrStatement || nextSibling.nextSibling is GrClosableBlock
|
||||
}
|
||||
prevSibling is GrPackageDefinition || prevSibling is GrImportStatement -> {
|
||||
nextSibling.node.elementType !in separators
|
||||
}
|
||||
prevSibling is GrParameterList && prevSibling.parent is GrClosableBlock -> {
|
||||
false // beginning of a closure
|
||||
}
|
||||
else -> true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
// 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 org.jetbrains.plugins.groovy.util
|
||||
|
||||
import com.intellij.psi.tree.IElementType
|
||||
import com.intellij.psi.tree.TokenSet
|
||||
import com.intellij.psi.tree.TokenSet.andNot
|
||||
import com.intellij.psi.tree.TokenSet.orSet
|
||||
|
||||
operator fun TokenSet.plus(another: TokenSet): TokenSet = orSet(this, another)
|
||||
|
||||
operator fun TokenSet.minus(another: TokenSet): TokenSet = andNot(this, another)
|
||||
|
||||
@Suppress("FunctionName")
|
||||
fun TokenSet(vararg tokens: IElementType): TokenSet = TokenSet.create(*tokens)
|
||||
+44
-17
@@ -1,18 +1,4 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
// 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 org.jetbrains.plugins.groovy.codeInspection.style
|
||||
|
||||
import com.intellij.testFramework.LightProjectDescriptor
|
||||
@@ -91,7 +77,7 @@ print(a); /*asd*/
|
||||
doTest 'foo {; ;1;;/*asd*/;54; ;<caret>;; }', 'foo { 1;/*asd*/54 }'
|
||||
}
|
||||
|
||||
void 'test within closure with arrow' () {
|
||||
void 'test within closure with arrow'() {
|
||||
doTest 'foo { -> ; ;1;;/*asd*/;54; ;<caret>;; }', 'foo { -> 1;/*asd*/54 }'
|
||||
}
|
||||
|
||||
@@ -111,6 +97,47 @@ class A {
|
||||
'''
|
||||
}
|
||||
|
||||
void 'test after package definition'() {
|
||||
doTest 'package com.foo<caret>; import java.lang.String'
|
||||
doTest '''\
|
||||
package com.foo<caret>;
|
||||
import java.lang.String
|
||||
''', '''\
|
||||
package com.foo
|
||||
import java.lang.String
|
||||
'''
|
||||
}
|
||||
|
||||
void 'test after import definition'() {
|
||||
doTest 'import java.lang.String<caret>; import java.lang.String'
|
||||
doTest '''\
|
||||
import java.lang.String<caret>;
|
||||
import java.lang.String;
|
||||
''', '''\
|
||||
import java.lang.String
|
||||
import java.lang.String
|
||||
'''
|
||||
}
|
||||
|
||||
void 'test after enum constants'() {
|
||||
doTest '''\
|
||||
enum E {
|
||||
foo, bar, <caret>;
|
||||
E() {}
|
||||
}
|
||||
'''
|
||||
doTest '''\
|
||||
enum E {
|
||||
foo, bar,
|
||||
|
||||
<caret>;
|
||||
|
||||
|
||||
E() {}
|
||||
}
|
||||
'''
|
||||
}
|
||||
|
||||
private doTest(String before, String after) {
|
||||
fixture.with {
|
||||
enableInspections GrUnnecessarySemicolonInspection
|
||||
@@ -124,7 +151,7 @@ class A {
|
||||
fixture.with {
|
||||
enableInspections GrUnnecessarySemicolonInspection
|
||||
configureByText '_.groovy', text
|
||||
checkHighlighting()
|
||||
assertNull(getAvailableIntention("Fix all 'Unnecessary semicolon'"))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user