IDEA-108052 Groovy: "@DelegatesTo inspection" could report unused @DelegatesTo.Target if a closure parameter delegates to a different target

This commit is contained in:
Max Medvedev
2013-05-31 16:07:00 +04:00
parent f545952126
commit c6be52d0c8
13 changed files with 213 additions and 35 deletions
@@ -47,6 +47,7 @@ public class GroovyCodeStyleSettings extends CustomCodeStyleSettings {
public boolean SPACE_BEFORE_CLOSURE_LBRACE = true;
public boolean SPACE_WITHIN_GSTRING_INJECTION_BRACES = false;
public boolean SPACE_WITHIN_TUPLE_EXPRESSION = false;
public boolean INDENT_LABEL_BLOCKS = false;
//imports
public boolean USE_FQ_CLASS_NAMES = false;
@@ -23,6 +23,7 @@ import com.intellij.psi.codeStyle.CodeStyleSettings;
import com.intellij.psi.codeStyle.CodeStyleSettingsCustomizable;
import com.intellij.psi.codeStyle.CommonCodeStyleSettings;
import com.intellij.psi.codeStyle.LanguageCodeStyleSettingsProvider;
import com.intellij.ui.components.JBLabel;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.groovy.GroovyFileType;
@@ -32,6 +33,11 @@ import javax.swing.*;
* @author Rustam Vishnyakov
*/
public class GroovyLanguageCodeStyleSettingsProvider extends LanguageCodeStyleSettingsProvider {
public static final String NONE = "None";
public static final String ABSOLUTE = "Absolute";
public static final String RELATIVE = "Relative";
@NotNull
@Override
public Language getLanguage() {
@@ -222,7 +228,8 @@ public class GroovyLanguageCodeStyleSettingsProvider extends LanguageCodeStyleSe
consumer.showCustomOption(GroovyCodeStyleSettings.class, "SPACE_WITHIN_LIST_OR_MAP", "List and maps literals", CodeStyleSettingsCustomizable.SPACES_WITHIN);
consumer.showCustomOption(GroovyCodeStyleSettings.class, "SPACE_BEFORE_CLOSURE_LBRACE", "Closure left brace in method calls", CodeStyleSettingsCustomizable.SPACES_BEFORE_LEFT_BRACE);
consumer.showCustomOption(GroovyCodeStyleSettings.class, "SPACE_WITHIN_GSTRING_INJECTION_BRACES", "GString injection braces", CodeStyleSettingsCustomizable.SPACES_WITHIN);
consumer.showCustomOption(GroovyCodeStyleSettings.class, "SPACE_WITHIN_TUPLE_EXPRESSION", "Tuple assignment expression", CodeStyleSettingsCustomizable.SPACES_WITHIN);
consumer.showCustomOption(GroovyCodeStyleSettings.class, "SPACE_WITHIN_TUPLE_EXPRESSION", "Tuple assignment expression",
CodeStyleSettingsCustomizable.SPACES_WITHIN);
return;
}
if (settingsType == SettingsType.BLANK_LINES_SETTINGS) {
@@ -258,6 +265,7 @@ public class GroovyLanguageCodeStyleSettingsProvider extends LanguageCodeStyleSe
defaultSettings.KEEP_SIMPLE_CLASSES_IN_ONE_LINE = true;
defaultSettings.KEEP_SIMPLE_METHODS_IN_ONE_LINE = true;
//defaultSettings.KEEP_SIMPLE_BLOCKS_IN_ONE_LINE = true;
defaultSettings.getIndentOptions().LABEL_INDENT_SIZE = 2;
return defaultSettings;
}
@@ -273,13 +281,16 @@ public class GroovyLanguageCodeStyleSettingsProvider extends LanguageCodeStyleSe
}
}
@Override
public IndentOptionsEditor getIndentOptionsEditor() {
return new SmartIndentOptionsEditor() {
private JTextField myLabelIndent;
private JLabel myLabelIndentLabel;
private JCheckBox myLabelIndentAbsolute;
private JComboBox myLabelIndentStyle;
private JBLabel myStyleLabel;
protected void addComponents() {
super.addComponents();
@@ -287,41 +298,65 @@ public class GroovyLanguageCodeStyleSettingsProvider extends LanguageCodeStyleSe
myLabelIndent = new JTextField(4);
add(myLabelIndentLabel = new JLabel(ApplicationBundle.message("editbox.indent.label.indent")), myLabelIndent);
myLabelIndentAbsolute = new JCheckBox(ApplicationBundle.message("checkbox.indent.absolute.label.indent"));
add(myLabelIndentAbsolute, true);
myStyleLabel = new JBLabel("Label indent style:");
myLabelIndentStyle = new JComboBox(new Object[] {NONE, ABSOLUTE, RELATIVE});
add(myStyleLabel, myLabelIndentStyle);
}
public boolean isModified(final CodeStyleSettings settings, final CommonCodeStyleSettings.IndentOptions options) {
boolean isModified = super.isModified(settings, options);
isModified |= isFieldModified(myLabelIndent, options.LABEL_INDENT_SIZE);
isModified |= isFieldModified(myLabelIndentAbsolute, options.LABEL_INDENT_ABSOLUTE);
isModified |= isLabelStyleModified(options.LABEL_INDENT_ABSOLUTE, settings.getCustomSettings(GroovyCodeStyleSettings.class).INDENT_LABEL_BLOCKS);
return isModified;
}
private boolean isLabelStyleModified(boolean absolute, boolean relative) {
if (absolute) {
return !ABSOLUTE.equals(myLabelIndentStyle.getSelectedItem());
}
else if (relative) {
return !RELATIVE.equals(myLabelIndentStyle.getSelectedItem());
}
else {
return !NONE.equals(myLabelIndentStyle.getSelectedItem());
}
}
public void apply(final CodeStyleSettings settings, final CommonCodeStyleSettings.IndentOptions options) {
super.apply(settings, options);
options.LABEL_INDENT_SIZE = getFieldValue(myLabelIndent, Integer.MIN_VALUE, options.LABEL_INDENT_SIZE);
options.LABEL_INDENT_ABSOLUTE = myLabelIndentAbsolute.isSelected();
options.LABEL_INDENT_ABSOLUTE = ABSOLUTE.equals(myLabelIndentStyle.getSelectedItem());
settings.getCustomSettings(GroovyCodeStyleSettings.class).INDENT_LABEL_BLOCKS = RELATIVE
.equals(myLabelIndentStyle.getSelectedItem());
}
public void reset(@NotNull final CodeStyleSettings settings, @NotNull final CommonCodeStyleSettings.IndentOptions options) {
super.reset(settings, options);
myLabelIndent.setText(Integer.toString(options.LABEL_INDENT_SIZE));
myLabelIndentAbsolute.setSelected(options.LABEL_INDENT_ABSOLUTE);
if (options.LABEL_INDENT_ABSOLUTE) {
myLabelIndentStyle.setSelectedItem(ABSOLUTE);
}
else if(settings.getCustomSettings(GroovyCodeStyleSettings.class).INDENT_LABEL_BLOCKS) {
myLabelIndentStyle.setSelectedItem(RELATIVE);
}
else {
myLabelIndentStyle.setSelectedItem(NONE);
}
}
public void setEnabled(final boolean enabled) {
super.setEnabled(enabled);
myLabelIndent.setEnabled(enabled);
myLabelIndentLabel.setEnabled(enabled);
myLabelIndentAbsolute.setEnabled(enabled);
myStyleLabel.setEnabled(enabled);
myLabelIndentStyle.setEnabled(enabled);
}
};
}
private final static String INDENT_OPTIONS_SAMPLE =
/*
@@ -336,6 +371,16 @@ public class GroovyLanguageCodeStyleSettingsProvider extends LanguageCodeStyleSe
" }\n" +
" return Math.max(arg,\n" +
" 0)\n" +
"}\n\n" +
"def \"length of Spock's and his friends' names\"() {\n" +
" expect:\n" +
" name.size() == length\n" +
"\n" +
" where: \"something\"\n" +
" name || length\n" +
" \"Kirk\" || 4\n" +
" \"Scotty\" || 6\n" +
"}\n \n" +
"}";
private final static String SPACING_SAMPLE =
@@ -52,7 +52,7 @@ public class ClosureBodyBlock extends GroovyBlock {
GroovyBlockGenerator generator = new GroovyBlockGenerator(this);
List<ASTNode> children = GroovyBlockGenerator.getClosureBodyVisibleChildren(myNode.getTreeParent());
mySubBlocks = generator.generateSubBlockForCodeBlocks(false, children);
mySubBlocks = generator.generateSubBlockForCodeBlocks(false, children, myContext.getGroovySettings().INDENT_LABEL_BLOCKS);
//at least -> exists
assert !mySubBlocks.isEmpty();
@@ -0,0 +1,61 @@
/*
* Copyright 2000-2013 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.formatter.blocks;
import com.intellij.formatting.Block;
import com.intellij.formatting.Indent;
import com.intellij.formatting.Wrap;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.util.TextRange;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.formatter.FormattingContext;
import java.util.List;
/**
* @author Max Medvedev
*/
public class GrLabelBlock extends GroovyBlock {
private List<Block> myBlocks;
private TextRange myRange;
public GrLabelBlock(@NotNull ASTNode node,
List<ASTNode> subStatements,
boolean classLevel,
@NotNull Indent indent,
@Nullable Wrap wrap,
@NotNull FormattingContext context) {
super(node, indent, wrap, context);
final GroovyBlockGenerator generator = new GroovyBlockGenerator(this);
myBlocks = generator.generateSubBlockForCodeBlocks(classLevel, subStatements, false);
myRange = new TextRange(subStatements.get(0).getTextRange().getStartOffset(),
subStatements.get(subStatements.size() - 1).getTextRange().getEndOffset());
}
@NotNull
@Override
public TextRange getTextRange() {
return myRange;
}
@NotNull
@Override
public List<Block> getSubBlocks() {
return myBlocks;
}
}
@@ -242,7 +242,7 @@ public class GroovyBlockGenerator implements GroovyElementTypes {
}
if (blockPsi instanceof GrCodeBlock || blockPsi instanceof GroovyFile || classLevel) {
return generateSubBlockForCodeBlocks(classLevel, visibleChildren(myNode));
return generateSubBlockForCodeBlocks(classLevel, visibleChildren(myNode), myContext.getGroovySettings().INDENT_LABEL_BLOCKS);
}
if (blockPsi instanceof GrMethod) {
@@ -324,13 +324,32 @@ public class GroovyBlockGenerator implements GroovyElementTypes {
return wrap;
}
public List<Block> generateSubBlockForCodeBlocks(boolean classLevel, final List<ASTNode> children) {
public List<Block> generateSubBlockForCodeBlocks(boolean classLevel, final List<ASTNode> children, final boolean indentLabelBlocks) {
calculateAlignments(children, classLevel);
final ArrayList<Block> subBlocks = new ArrayList<Block>();
for (ASTNode childNode : children) {
subBlocks.add(new GroovyBlock(childNode, getIndent(childNode), getChildWrap(childNode), myContext));
if (indentLabelBlocks) {
for (int i = 0; i < children.size(); i++) {
ASTNode childNode = children.get(i);
if (childNode.getElementType() == LABELED_STATEMENT) {
int j = i;
do {
i++;
}
while (i < children.size() && children.get(i).getElementType() != LABELED_STATEMENT && children.get(i).getElementType() != mRCURLY);
subBlocks.add(new GrLabelBlock(childNode, children.subList(j, i), classLevel, getIndent(childNode), getChildWrap(childNode), myContext));
i--;
}
else {
subBlocks.add(new GroovyBlock(childNode, getIndent(childNode), getChildWrap(childNode), myContext));
}
}
}
else {
for (ASTNode childNode : children) {
subBlocks.add(new GroovyBlock(childNode, getIndent(childNode), getChildWrap(childNode), myContext));
}
}
return subBlocks;
}
@@ -24,6 +24,7 @@ import com.intellij.psi.tree.IElementType;
import com.intellij.psi.tree.TokenSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.groovy.formatter.blocks.ClosureBodyBlock;
import org.jetbrains.plugins.groovy.formatter.blocks.GrLabelBlock;
import org.jetbrains.plugins.groovy.formatter.blocks.GroovyBlock;
import org.jetbrains.plugins.groovy.lang.groovydoc.psi.api.GrDocComment;
import org.jetbrains.plugins.groovy.lang.groovydoc.psi.api.GrDocMethodParams;
@@ -87,6 +88,12 @@ public class GroovyIndentProcessor extends GroovyElementVisitor {
return Indent.getNormalIndent();
}
}
if (parentBlock instanceof GrLabelBlock) {
return myChildType == LABELED_STATEMENT
? Indent.getNoneIndent()
: Indent.getLabelIndent();
}
if (GSTRING_TOKENS_INNER.contains(myChildType)) {
return Indent.getAbsoluteNoneIndent();
@@ -138,11 +145,12 @@ public class GroovyIndentProcessor extends GroovyElementVisitor {
if (indentOptions != null && indentOptions.LABEL_INDENT_ABSOLUTE) {
myResult = Indent.getAbsoluteLabelIndent();
}
else {
}
else {
if (myBlock.getContext().getGroovySettings().INDENT_LABEL_BLOCKS) {
myResult = Indent.getLabelIndent();
}
}
}
@Override
@@ -204,6 +204,18 @@ public class GroovySpacingProcessor extends GroovyElementVisitor {
createSpaceInCode(false);
}
@Override
public void visitLabeledStatement(GrLabeledStatement labeledStatement) {
if (myType1 == mCOLON) {
if (myGroovySettings.INDENT_LABEL_BLOCKS) {
createLF(true);
}
else {
createSpaceInCode(true);
}
}
}
@Override
public void visitAnnotation(GrAnnotation annotation) {
if (myType2 == ANNOTATION_ARGUMENTS) {
@@ -28,6 +28,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.groovy.codeStyle.GroovyCodeStyleSettings;
import org.jetbrains.plugins.groovy.formatter.FormattingContext;
import org.jetbrains.plugins.groovy.formatter.blocks.ClosureBodyBlock;
import org.jetbrains.plugins.groovy.formatter.blocks.GrLabelBlock;
import org.jetbrains.plugins.groovy.formatter.blocks.GroovyBlock;
import org.jetbrains.plugins.groovy.formatter.blocks.MethodCallWithoutQualifierBlock;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrStatement;
@@ -227,6 +228,7 @@ public abstract class GroovySpacingProcessorBasic {
private static boolean mirrorsAst(GroovyBlock block) {
return block.getNode().getTextRange().equals(block.getTextRange()) ||
block instanceof MethodCallWithoutQualifierBlock ||
block instanceof ClosureBodyBlock;
block instanceof ClosureBodyBlock ||
block instanceof GrLabelBlock;
}
}
@@ -711,6 +711,30 @@ print abc ?:
'''
}
void testLabelsInBasicMode() {
groovyCustomSettings.INDENT_LABEL_BLOCKS = false
checkFormatting('''\
abc:foo()
bar()
''', '''\
abc: foo()
bar()
''')
}
void testLabels() {
checkFormatting('''\
abc:foo()
bar()
''', '''\
abc:
foo()
bar()
''')
}
private void doGeeseTest() {
GroovyCodeStyleSettings customSettings = myTempSettings.getCustomSettings(GroovyCodeStyleSettings.class);
boolean oldvalue = customSettings.USE_FLYING_GEESE_BRACES;
@@ -28,6 +28,7 @@ import com.intellij.psi.codeStyle.CommonCodeStyleSettings
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase
import com.intellij.util.IncorrectOperationException
import org.jetbrains.plugins.groovy.GroovyFileType
import org.jetbrains.plugins.groovy.codeStyle.GroovyCodeStyleSettings
/**
* @author peter
*/
@@ -55,6 +56,10 @@ public abstract class GroovyFormatterTestCase extends LightCodeInsightFixtureTes
return myTempSettings.getCommonSettings(GroovyFileType.GROOVY_LANGUAGE);
}
protected GroovyCodeStyleSettings getGroovyCustomSettings() {
return myTempSettings.getCustomSettings(GroovyCodeStyleSettings)
}
protected void setSettings(Project project) {
assertNull(myTempSettings);
CodeStyleSettings settings = CodeStyleSettingsManager.getSettings(project);
+7 -7
View File
@@ -17,15 +17,15 @@ class HelloSpock extends spock.lang.Specification {
class HelloSpock extends spock.lang.Specification {
def "length of Spock's and his friends' names"() {
expect:
name.size() == length
name.size() == length
where:
name | length | foo
"Spock" | 5
"Kirk" | 4 | xxx | yyy
"Scotty" | 6 | dddddddddd | fff
name | length | foo
"Spock" | 5
"Kirk" | 4 | xxx | yyy
"Scotty" | 6 | dddddddddd | fff
//aaa
a | b | c
//aaa
a | b | c
}
}
@@ -13,11 +13,11 @@ class HelloSpock extends spock.lang.Specification {
class HelloSpock extends spock.lang.Specification {
def "length of Spock's and his friends' names"() {
expect:
name.size() == length
name.size() == length
where:
name | length
"Kirk" | 4 // a great guy
"Scotty" | 6
name | length
"Kirk" | 4 // a great guy
"Scotty" | 6
}
}
@@ -13,11 +13,12 @@ class HelloSpock extends spock.lang.Specification {
class HelloSpock extends spock.lang.Specification {
def "length of Spock's and his friends' names"() {
expect:
name.size() == length
name.size() == length
where: "something"
name || length
"Kirk" || 4
"Scotty" || 6
where:
"something"
name || length
"Kirk" || 4
"Scotty" || 6
}
}