diff --git a/plugins/groovy/resources/inspectionDescriptions/NewInstanceOfSingleton.html b/plugins/groovy/resources/inspectionDescriptions/NewInstanceOfSingleton.html new file mode 100644 index 000000000000..56e4080a0b21 --- /dev/null +++ b/plugins/groovy/resources/inspectionDescriptions/NewInstanceOfSingleton.html @@ -0,0 +1,7 @@ + + + + This inspection reports new instance creation of classes annotated with @groovy.lang.Singleton. Such constructions can lead to runtime exception "Can't instantiate singleton". + +Powered by JetGroovy + \ No newline at end of file diff --git a/plugins/groovy/src/META-INF/plugin.xml b/plugins/groovy/src/META-INF/plugin.xml index bcab8d91ee94..80817474d0ed 100644 --- a/plugins/groovy/src/META-INF/plugin.xml +++ b/plugins/groovy/src/META-INF/plugin.xml @@ -642,6 +642,9 @@ + diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/GroovyInspectionBundle.properties b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/GroovyInspectionBundle.properties index e89d06c35cff..d674c0d4cae4 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/GroovyInspectionBundle.properties +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/GroovyInspectionBundle.properties @@ -72,4 +72,6 @@ implement.class=Implement {0} fix.class=Fix class {0} check.labeled.statement=Labeled statement inspection unnecessary.qualified.reference=Unnecessary qualified reference -rtype.cannot.contain.ltype=''{1}'' cannot contain ''{0}'' \ No newline at end of file +rtype.cannot.contain.ltype=''{1}'' cannot contain ''{0}'' +new.instance.of.singleton=New instance of class annotated with @groovy.lang.Singleton +replace.new.expression.with.0.instance=Replace with ''{0}.instance'' \ No newline at end of file diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/bugs/NewInstanceOfSingletonInspection.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/bugs/NewInstanceOfSingletonInspection.java new file mode 100644 index 000000000000..c3504bfc4f18 --- /dev/null +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/bugs/NewInstanceOfSingletonInspection.java @@ -0,0 +1,112 @@ +/* + * Copyright 2000-2012 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.bugs; + +import com.intellij.codeInspection.ProblemDescriptor; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiElement; +import com.intellij.util.IncorrectOperationException; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.plugins.groovy.codeInspection.BaseInspection; +import org.jetbrains.plugins.groovy.codeInspection.BaseInspectionVisitor; +import org.jetbrains.plugins.groovy.codeInspection.GroovyFix; +import org.jetbrains.plugins.groovy.codeInspection.GroovyInspectionBundle; +import org.jetbrains.plugins.groovy.dsl.psi.PsiClassCategory; +import org.jetbrains.plugins.groovy.lang.GrReferenceAdjuster; +import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrNewExpression; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition; +import org.jetbrains.plugins.groovy.lang.psi.api.types.GrCodeReferenceElement; +import org.jetbrains.plugins.groovy.lang.psi.util.GroovyCommonClassNames; + +/** + * @author Max Medvedev + */ +public class NewInstanceOfSingletonInspection extends BaseInspection { + private static final Logger LOG = Logger.getInstance(NewInstanceOfSingletonInspection.class); + + @Override + protected BaseInspectionVisitor buildVisitor() { + return new BaseInspectionVisitor() { + @Override + public void visitNewExpression(GrNewExpression newExpression) { + super.visitNewExpression(newExpression); + + final GrCodeReferenceElement refElement = newExpression.getReferenceElement(); + if (refElement == null) return; + if (newExpression.getArrayDeclaration() != null) return; + + final PsiElement resolved = refElement.resolve(); + if (resolved instanceof GrTypeDefinition && + PsiClassCategory.hasAnnotation((GrTypeDefinition)resolved, GroovyCommonClassNames.GROOVY_LANG_SINGLETON)) { + registerError(newExpression, GroovyInspectionBundle.message("new.instance.of.singleton")); + } + } + }; + } + + @Override + public boolean isEnabledByDefault() { + return true; + } + + @Override + protected GroovyFix buildFix(final PsiElement location) { + final GrCodeReferenceElement refElement = ((GrNewExpression)location).getReferenceElement(); + LOG.assertTrue(refElement != null); + final GrTypeDefinition singleton = (GrTypeDefinition)refElement.resolve(); + LOG.assertTrue(singleton != null); + + return new GroovyFix() { + @Override + protected void doFix(Project project, ProblemDescriptor descriptor) throws IncorrectOperationException { + final GrExpression instanceRef = + GroovyPsiElementFactory.getInstance(project).createExpressionFromText(singleton.getQualifiedName() + ".instance"); + + final GrExpression replaced = ((GrNewExpression)location).replaceWithExpression(instanceRef, true); + GrReferenceAdjuster.shortenReferences(replaced); + } + + @NotNull + @Override + public String getName() { + return GroovyInspectionBundle.message("replace.new.expression.with.0.instance", singleton.getName()); + } + }; + } + + @Nls + @NotNull + @Override + public String getGroupDisplayName() { + return CONFUSING_CODE_CONSTRUCTS; + } + + @Override + protected String buildErrorString(Object... args) { + return (String)args[0]; + } + + @Nls + @NotNull + @Override + public String getDisplayName() { + return "New instance of class annotated with @groovy.lang.Singleton"; + } +} diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/util/GroovyCommonClassNames.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/util/GroovyCommonClassNames.java index db931442d89c..bb39b5090a92 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/util/GroovyCommonClassNames.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/util/GroovyCommonClassNames.java @@ -49,6 +49,7 @@ public final class GroovyCommonClassNames { @NonNls public static final String GROOVY_LANG_USE = "groovy.lang.Use"; @NonNls public static final String GROOVY_LANG_MIXIN = "groovy.lang.Mixin"; @NonNls public static final String GROOVY_UTIL_TEST_CASE = "groovy.util.GroovyTestCase"; + public static final String GROOVY_LANG_SINGLETON = "groovy.lang.Singleton"; private GroovyCommonClassNames() {