From 9eb540d2a7e40d8629f031f357b09c0bcdeaa617 Mon Sep 17 00:00:00 2001 From: Maxim Medvedev Date: Mon, 28 Jun 2010 14:43:46 +0400 Subject: [PATCH] IDEA-48678: No error message when there is no default constructor in superclass --- .../plugins/groovy/GroovyBundle.properties | 1 + .../groovy/annotator/GroovyAnnotator.java | 66 ++++++++++++++++++- .../groovy/lang/GroovyHighlightingTest.java | 2 + .../highlighting/NoDefaultConstructor.groovy | 11 ++++ 4 files changed, 77 insertions(+), 3 deletions(-) create mode 100644 plugins/groovy/testdata/highlighting/NoDefaultConstructor.groovy diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/GroovyBundle.properties b/plugins/groovy/src/org/jetbrains/plugins/groovy/GroovyBundle.properties index 3e5f565cab0a..90490f8e15c4 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/GroovyBundle.properties +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/GroovyBundle.properties @@ -340,3 +340,4 @@ method.doesnot.override.super=Method does not override method from its super cla method.duplicate=Method with signature {0} is already defined in the class ''{1}'' ambiguous.code.block=Ambiguous code block cyclic.inheritance.involving.0=Cyclic inheritance involving ''{0}'' +there.is.no.default.constructor.available.in.class.0=There is no default constructor available in class ''{0}'' diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/GroovyAnnotator.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/GroovyAnnotator.java index e828a0c01620..5db12cf6ed1c 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/GroovyAnnotator.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/GroovyAnnotator.java @@ -281,6 +281,41 @@ public class GroovyAnnotator extends GroovyElementVisitor implements Annotator { final GrTypeDefinitionBody body = typeDefinition.getBody(); if (body != null) checkDuplicateMethod(body.getGroovyMethods(), myHolder); checkImplementedMethodsOfClass(myHolder, typeDefinition); + checkConstructors(myHolder, typeDefinition); + } + + private static void checkConstructors(AnnotationHolder holder, GrTypeDefinition typeDefinition) { + if (typeDefinition.isEnum() || typeDefinition.isInterface() || typeDefinition.isAnonymous()) return; + final PsiClass superClass = typeDefinition.getSuperClass(); + if (superClass == null) return; + + PsiMethod defConstructor = getDefaultConstructor(superClass); + boolean hasImplicitDefConstructor = superClass.getConstructors().length ==0; + + final PsiMethod[] constructors = typeDefinition.getConstructors(); + final String qName = superClass.getQualifiedName(); + if (constructors.length == 0) { + if (!hasImplicitDefConstructor && (defConstructor == null || !PsiUtil.isAccessible(typeDefinition, defConstructor))) { + final TextRange range = getHeaderTextRange(typeDefinition); + holder.createErrorAnnotation(range, GroovyBundle.message("there.is.no.default.constructor.available.in.class.0", qName)); + } + return; + } + for (PsiMethod method : constructors) { + if (method instanceof GrMethod) { + final GrOpenBlock block = ((GrMethod)method).getBlock(); + if (block == null) continue; + final GrStatement[] statements = block.getStatements(); + if (statements.length > 0) { + if (statements[0] instanceof GrConstructorInvocation) continue; + } + + if (!hasImplicitDefConstructor && (defConstructor == null || !PsiUtil.isAccessible(typeDefinition, defConstructor))) { + final TextRange range = getMethodHeaderTextRange((GrMethod)method); + holder.createErrorAnnotation(range, GroovyBundle.message("there.is.no.default.constructor.available.in.class.0", qName)); + } + } + } } @Override @@ -289,6 +324,18 @@ public class GroovyAnnotator extends GroovyElementVisitor implements Annotator { checkInnerMethod(myHolder, method); } + @Nullable + private static PsiMethod getDefaultConstructor(PsiClass clazz) { + final String className = clazz.getName(); + if (className == null) return null; + final PsiMethod[] byName = clazz.findMethodsByName(className, true); + if (byName.length == 0) return null; + for (PsiMethod method : byName) { + if (method.getParameterList().getParametersCount() == 0) return method; + } + return null; + } + @Override public void visitVariableDeclaration(GrVariableDeclaration variableDeclaration) { @@ -753,13 +800,26 @@ public class GroovyAnnotator extends GroovyElementVisitor implements Annotator { assert element instanceof PsiNamedElement; String notImplementedMethodName = ((PsiNamedElement)element).getName(); - final int startOffset = typeDefinition.getTextOffset(); - int endOffset = typeDefinition.getNameIdentifierGroovy().getTextRange().getEndOffset(); - final Annotation annotation = holder.createErrorAnnotation(new TextRange(startOffset, endOffset), + final TextRange range = getHeaderTextRange(typeDefinition); + final Annotation annotation = holder.createErrorAnnotation(range, GroovyBundle.message("method.is.not.implemented", notImplementedMethodName)); registerImplementsMethodsFix(typeDefinition, annotation); } + private static TextRange getHeaderTextRange(GrNamedElement namedElement) { + final int startOffset = namedElement.getTextOffset(); + int endOffset = namedElement.getNameIdentifierGroovy().getTextRange().getEndOffset(); + return new TextRange(startOffset, endOffset); + } + + private static TextRange getMethodHeaderTextRange(GrMethod method) { + final int startOffset = method.getTextOffset(); + final ASTNode node = method.getNode().findChildByType(GroovyTokenTypes.mRPAREN); + assert node != null; + int endOffset = node.getTextRange().getEndOffset(); + return new TextRange(startOffset, endOffset); + } + private static void registerImplementsMethodsFix(GrTypeDefinition typeDefinition, Annotation annotation) { annotation.registerFix(new ImplementMethodsQuickFix(typeDefinition)); } diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GroovyHighlightingTest.java b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GroovyHighlightingTest.java index 4118072b37ac..0db5c309f86c 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GroovyHighlightingTest.java +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GroovyHighlightingTest.java @@ -251,4 +251,6 @@ public class GroovyHighlightingTest extends LightCodeInsightFixtureTestCase { public void testDuplicateParameterInClosableBlock() throws Exception {doTest();} public void testCyclicInheritance() throws Exception {doTest();} + + public void testNoDefaultConstructor() throws Exception {doTest();} } \ No newline at end of file diff --git a/plugins/groovy/testdata/highlighting/NoDefaultConstructor.groovy b/plugins/groovy/testdata/highlighting/NoDefaultConstructor.groovy new file mode 100644 index 000000000000..495de4eb53f1 --- /dev/null +++ b/plugins/groovy/testdata/highlighting/NoDefaultConstructor.groovy @@ -0,0 +1,11 @@ +class X { + X(int a) {} +} + +class Y extends X { + + Y() { + + } +} +