// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.plugins.groovy.lang.highlighting;

import junit.framework.TestCase;
import org.jetbrains.plugins.groovy.codeInspection.assignment.GroovyAssignabilityCheckInspection;
import org.jetbrains.plugins.groovy.codeInspection.bugs.GroovyConstructorNamedArgumentsInspection;
import org.jetbrains.plugins.groovy.codeInspection.untypedUnresolvedAccess.GrUnresolvedAccessInspection;
import org.jetbrains.plugins.groovy.codeInspection.unusedDef.UnusedDefInspection;
import org.jetbrains.plugins.groovy.transformations.TransformationUtilKt;

public class GroovyHighlightingTest extends GrHighlightingTestBase {
  public void testDuplicateClosurePrivateVariable() { doTest(); }

  public void testClosureRedefiningVariable() { doTest(); }

  public void testCircularInheritance() {
    TransformationUtilKt.disableAssertOnRecursion(getTestRootDisposable());
    doTest();
  }

  public void testEmptyTupleType() { doTest(); }

  public void testMapDeclaration() { doTest(); }

  public void testShouldNotImplementGroovyObjectMethods() {
    addGroovyObject();
    myFixture.addFileToProject("Foo.groovy", "class Foo {}");
    myFixture.testHighlighting(false, false, false, getTestName(false) + ".java");
  }

  public void testJavaClassImplementingGroovyInterface() {
    addGroovyObject();
    myFixture.addFileToProject("Foo.groovy", "interface Foo {}");
    myFixture.testHighlighting(false, false, false, getTestName(false) + ".java");
  }

  public void testDuplicateFields() { doTest(); }

  public void testNoDuplicationThroughClosureBorder() {
    myFixture.addClass("package groovy.lang; public interface Closure {}");
    doTest();
  }

  public void testRecursiveMethodTypeInference() { doTest(); }

  public void testSuperClassNotExists() { doRefTest(); }

  public void testAnonymousClassConstructor() { doTest(); }

  public void testAnonymousClassAbstractMethod() { doTest(); }

  public void testAnonymousClassShouldImplementMethods() { doTest(); }

  public void testAnonymousClassShouldImplementSubstitutedMethod() { doTest(); }

  public void testUnresolvedLhsAssignment() { doRefTest(); }

  public void testUnresolvedAccess() { doRefTest(); }

  public void testBooleanProperties() { doRefTest(); }

  public void testDuplicateInnerClass() { doTest(); }

  public void testThisInStaticContext() { doTest(); }

  public void testLocalVariableInStaticContext() { doTest(); }

  public void testModifiersInPackageAndImportStatements() {
    myFixture.copyFileToProject(getTestName(false) + ".groovy", "x/" + getTestName(false) + ".groovy");
    myFixture.testHighlighting(true, false, false, "x/" + getTestName(false) + ".groovy");
  }

  public void testBreakOutside() { doTest(); }

  public void testUndefinedLabel() { doTest(); }

  public void testNestedMethods() { doTest(); }

  public void testRawOverriddenMethod() { doTest(); }

  public void testFQNJavaClassesUsages() { doTest(); }

  public void testGstringAssignableToString() { doTest(); }

  public void testGstringAssignableToStringInClosureParameter() { doTest(); }

  public void testEachOverRange() { doTest(); }

  public void testEllipsisParam() {
    myFixture.configureByText("a.groovy", """
      class A {
        def foo(int... x){}
        def foo(int<error descr="Ellipsis type is not allowed here">...</error> x, double y) {}
      }
      """);
    myFixture.checkHighlighting(true, false, false);
  }

  public void testStringAndGStringUpperBound() { doTest(); }

  public void testWithMethod() { doTest(); }

  public void testArrayLikeAccess() { doTest(); }

  public void testSetInitializing() { doTest(); }

  public void testEmptyTupleAssignability() { doTest(); }

  public void testGrDefFieldsArePrivateInJavaCode() {
    myFixture.configureByText("X.groovy", "public class X{def x=5}");
    myFixture.testHighlighting(true, false, false, getTestName(false) + ".java");
  }

  public void testSuperConstructorInvocation() { doTest(new GroovyAssignabilityCheckInspection()); }

  public void testDuplicateMapKeys() { doTest(); }

  public void testIndexPropertyAccess() { doTest(); }

  public void testPropertyAndFieldDeclaration() { doTest(); }

  public void testGenericsMethodUsage() { doTest(); }

  public void testWildcardInExtendsList() { doTest(); }

  public void testOverrideAnnotation() { doTest(); }

  public void testClosureCallWithTupleTypeArgument() { doTest(); }

  public void testMethodDuplicates() { doTest(); }

  public void testNotAmbiguousClosableBlock() { doTest(); }

  public void testDuplicateParameterInClosableBlock() { doTest(); }

  public void testCyclicInheritance() {
    TransformationUtilKt.disableAssertOnRecursion(getTestRootDisposable());
    doTest();
  }

  public void testNoDefaultConstructor() { doTest(); }

  public void testNumberDuplicatesInMaps() { doTest(); }

  public void testBuiltInTypeInstantiation() { doTest(); }

  public void testVoidArrayType() {
    doTestHighlighting("""
                         <error descr="Illegal type: 'void'">void</error>[] foo() {}
                         <error descr="Illegal type: 'void'">void</error>[] bar = foo()
                         """);
  }

  public void testSOEInFieldDeclarations() { doTest(); }

  public void testWrongAnnotation() { doTest(); }

  public void testAmbiguousMethods() {
    myFixture.copyFileToProject(getTestName(false) + ".java");
    doTest();
  }

  public void testGroovyEnumInJavaFile() {
    myFixture.copyFileToProject(getTestName(false) + ".groovy");
    myFixture.testHighlighting(true, false, false, getTestName(false) + ".java");
  }

  public void testSOFInDelegate() {
    TransformationUtilKt.disableAssertOnRecursion(getTestRootDisposable());
    doTest();
  }

  public void testMethodImplementedByDelegate() {
    doTest();
  }

  public void testGdslWildcardTypes() {
    myFixture.configureByText("a.groovy", "List<? extends String> la = []; la.get(1); " +
                                          "List<? super String> lb = []; lb.get(1); " +
                                          "List<?> lc = []; lc.get(1); ");
    myFixture.checkHighlighting(true, false, false);
  }

  public void testDuplicatedNamedArgs() { doTest(); }

  public void testConstructorWithAllParametersOptional() { doTest(); }

  public void testTupleConstructorAttributes() { doTest(new GroovyAssignabilityCheckInspection()); }

  public void testDelegatedMethodIsImplemented() { doTest(); }

  public void testEnumImplementsAllGroovyObjectMethods() { doTest(); }

  public void testRecursiveConstructors() { doTest(); }

  public void testImmutableConstructorFromJava() {
    myFixture.addFileToProject("a.groovy", """
      @groovy.transform.Immutable class Foo { int a; String b }""");
    myFixture.configureByText("a.java", """
      class Bar {{
        new Foo<error>()</error>;
        new Foo<error>(2)</error>;
        new Foo(2, "3");
      }}""");
    myFixture.checkHighlighting(false, false, false);
  }

  public void testTupleConstructorFromJava() {
    myFixture.addFileToProject("a.groovy", """
      @groovy.transform.TupleConstructor class Foo { int a; String b }""");
    myFixture.configureByText("a.java", """
      class Bar {{
        new Foo();
        new Foo(2);
        new Foo(2, "3");
        new Foo<error>(2, "3", 9)</error>;
      }}""");
    myFixture.checkHighlighting(false, false, false);
  }

  public void testInheritConstructorsFromJava() {
    myFixture.addFileToProject("a.groovy", """
      class Person {
        Person(String first, String last) { }
        Person(String first, String last, String address) { }
        Person(String first, String last, int zip) { }
      }

      @groovy.transform.InheritConstructors
      class PersonAge extends Person {
        PersonAge(String first, String last, int zip) { }
      }
      """);
    myFixture.configureByText("a.java", """
      class Bar {{
        new PersonAge("a", "b");
        new PersonAge("a", "b", "c");
        new PersonAge("a", "b", 239);
        new PersonAge<error>(2, "3", 9)</error>;
      }}""");
    myFixture.checkHighlighting(false, false, false);
  }

  public void testDefaultInitializersAreNotAllowedInAbstractMethods() { doTest(); }

  public void testConstructorTypeArgs() { doTest(); }

  public void testIncorrectEscaping() { doTest(); }

  public void testExtendingOwnInner() { doTest(); }

  public void testDuplicateMethods() {
    myFixture.configureByText("a.groovy", """
      class A {
        <error descr="Method with signature foo() is already defined in the class 'A'">def foo()</error>{}
        <error descr="Method with signature foo() is already defined in the class 'A'">def foo(def a=null)</error>{}
      }
      """);
    myFixture.checkHighlighting(true, false, false);
  }

  public void testPrivateTopLevelClassInJava() {
    myFixture.addFileToProject("pack/Foo.groovy", "package pack; private class Foo{}");
    myFixture.configureByText("Abc.java", """
      import pack.Foo;

      class Abc {
        void foo() {
          System.out.print(new Foo()); // top-level Groovy class can't be private
        }
      }
      """);

    myFixture.testHighlighting(false, false, false);
  }

  public void testDelegateToMethodWithItsOwnTypeParams() {
    myFixture.configureByText("a.groovy", """
      interface I<S> {
          def <T> void foo(List<T> a);
      }

      class Foo {
          @Delegate private I list
      }

      <error descr="Method 'foo' is not implemented">class Bar implements I</error> {
        def <T> void foo(List<T> a){}
      }

      class Baz implements I {
        def void foo(List a){}
      }
      """);

    myFixture.testHighlighting(false, false, false);
  }

  public void testMethodDelegate() {
    myFixture.addClass("""
                          package groovy.lang;
                          @Target({ElementType.FIELD, ElementType.METHOD})
                          public @interface Delegate {}
                          """);
    myFixture.configureByText("a.groovy", """
      class A {
        def foo(){}
      }

      class B {
        @Delegate A getA(){return new A()}
      }

      new B().foo()
      """);

    getFixture().checkHighlighting();
  }

  public void testMethodDelegateError() {
    myFixture.configureByText("a.groovy", """
      class A {
        def foo(){}
      }

      class B {
        <error>@Delegate</error> A getA(int i){return new A()}
      }

      new B().foo()
      """);

    getFixture().checkHighlighting();
  }

  public void testBuilderSimpleStrategyError() {
    myFixture.addClass("""
                          package groovy.transform.builder;
                          @Target({ ElementType.TYPE})

                          public @interface Builder {
                            Class<?> builderStrategy();
                            boolean includeSuperProperties() default false;
                          }
                          """);
    myFixture.addClass("""
                          package groovy.transform.builder;
                          public class SimpleStrategy {}
                          """);

    myFixture.configureByText("a.groovy", """
      import groovy.transform.builder.Builder
      import groovy.transform.builder.SimpleStrategy

      <error>@Builder(builderStrategy = SimpleStrategy, includeSuperProperties = true)</error>
      class Pojo {
          String name
          def dynamic
          int counter

          def method() {}
      }
      """);

    getFixture().checkHighlighting();
  }

  public void testBuilderSimpleStrategy() {
    myFixture.addClass("""
                          package groovy.transform.builder;
                          @Target({ ElementType.TYPE})

                          public @interface Builder {
                            Class<?> builderStrategy();
                            boolean includeSuperProperties() default false;
                          }
                          """);

    myFixture.addClass("""
                          package groovy.transform.builder;
                          public class SimpleStrategy {}
                          """);

    myFixture.configureByText("a.groovy", """
      import groovy.transform.builder.Builder
      import groovy.transform.builder.SimpleStrategy

      @Builder(builderStrategy = SimpleStrategy)
      class Pojo {
          String name
          def dynamic
          int counter

          def method() {}
      }
      new Pojo().setName("sd").setCounter(5)
      """);

    getFixture().checkHighlighting();
  }

  public void testPrimitiveTypeParams() {
    myFixture.configureByText("a.groovy", """
      List<<error descr="Primitive type parameters are not allowed in type parameter list">int</error>> list = new ArrayList<int><EOLError descr="'(' expected"></EOLError>
      List<? extends <error descr="Primitive bound types are not allowed">double</error>> l = new ArrayList<double>()
      List<?> list2
      """);
    myFixture.testHighlighting(true, false, false);
  }

  public void testAliasInParameterType() {
    myFixture.configureByText("a_.groovy", """
      import java.awt.event.ActionListener
      import java.awt.event.ActionEvent as AE

      public class CorrectImplementor implements ActionListener {
        public void actionPerformed (AE e) { //AE is alias to ActionEvent
        }
      }

      <error descr="Method 'actionPerformed' is not implemented">public class IncorrectImplementor implements ActionListener</error> {
        public void actionPerformed (Object e) {
        }
      }
      """);
    myFixture.testHighlighting(true, false, false);
  }

  public void testReassignedHighlighting() {
    myFixture.testHighlighting(true, true, true, getTestName(false) + ".groovy");
  }

  public void testInstanceOf() {
    myFixture.configureByText("_a.groovy", """
      class DslPointcut {}

      private def handleImplicitBind(arg) {
          if (arg instanceof Map && arg.size() == 1 && arg.keySet().iterator().next() instanceof String && arg.values().iterator().next() instanceof DslPointcut) {
              return DslPointcut.bind(arg)
          }
          return arg
      }""");
    myFixture.testHighlighting(true, false, false);
  }

  public void testIncorrectTypeArguments() {
    myFixture.configureByText("_.groovy", """
      class C <T extends String> {}
      C<<warning descr="Type parameter 'java.lang.Double' is not in its bound; should extend 'java.lang.String'">Double</warning>> c
      C<String> c2
      C<warning descr="Wrong number of type arguments: 2; required: 1"><String, Double></warning> c3
      """);
    myFixture.testHighlighting(true, false, true);
  }

  public void testTryCatch1() {
    doTestHighlighting("""
                         try {}
                         catch (Exception e){}
                         catch (<warning descr="Exception 'java.io.IOException' has already been caught">IOException</warning> e){}
                         """);
  }

  public void testTryCatch2() {
    doTestHighlighting("""
                         try {}
                         catch (e){}
                         catch (<warning descr="Exception 'java.lang.Exception' has already been caught">e</warning>){}
                         """);
  }

  public void testTryCatch3() {
    doTestHighlighting("""
                         try {}
                         catch (e){}
                         catch (<warning descr="Exception 'java.io.IOException' has already been caught">IOException</warning> e){}
                         """);
  }

  public void testTryCatch4() {
    doTestHighlighting("""
                         try {}
                         catch (Exception | <warning descr="Unnecessary exception 'java.io.IOException'. 'java.lang.Exception' is already declared">IOException</warning> e){}
                         """);
  }

  public void testTryCatch5() {
    doTestHighlighting("""
                         try {}
                         catch (RuntimeException | IOException e){}
                         catch (<warning descr="Exception 'java.lang.NullPointerException' has already been caught">NullPointerException</warning> e){}
                         """);
  }

  public void testTryCatch6() {
    doTestHighlighting("""
                         try {}
                         catch (NullPointerException | IOException e){}
                         catch (ClassNotFoundException | <warning descr="Exception 'java.lang.NullPointerException' has already been caught">NullPointerException</warning> e){}
                         """);
  }

  public void testTryWithoutCatchFinally() { doTest(); }

  public void testVariableDeclarationTypeParameters() { doTest(); }

  public void testAnnotationFieldWithoutType() { doTest(); }

  public void testVariableDeclarationDuplicateModifiers() { doTest(); }

  public void testCompileStatic() {
    myFixture.addClass("""
                          package groovy.transform;
                          public @interface CompileStatic {
                          }""");

    doTestHighlighting("""
                         import groovy.transform.CompileStatic

                         class A {

                         def foo() {
                         print <warning descr="Cannot resolve symbol 'abc'">abc</warning>
                         }

                         @CompileStatic
                         def bar() {
                         print <error descr="Cannot resolve symbol 'abc'">abc</error>
                           new Object() {
                             def baz() {
                               print(<error descr="Cannot resolve symbol 'unknown'">unknown</error>)
                             }
                           }
                         }
                         }
                         """, true, false, false, GrUnresolvedAccessInspection.class);
  }

  public void testUnresolvedVarInStaticMethod() {
    doTestHighlighting("""
                         static def foo() {
                           print <error descr="Cannot resolve symbol 'abc'">abc</error>

                           def cl = {
                              print <warning descr="Cannot resolve symbol 'cde'">cde</warning>
                           }
                         }
                         """, GrUnresolvedAccessInspection.class);
  }

  public void testStaticOkForClassMembersWithThisQualifier() {
    doTestHighlighting("""
                         class A {
                           def foo(){}
                           static bar() {
                             this.toString()
                             this.getFields()
                             this.<warning descr="Cannot reference non-static symbol 'foo' from static context">foo</warning>()
                           }
                         }
                         """, GrUnresolvedAccessInspection.class);
  }

  public void testScriptFieldsAreAllowedOnlyInScriptBody() {
    addGroovyTransformField();
    doTestHighlighting("""
                         import groovy.transform.Field

                         @Field
                         def foo

                         def foo() {
                           <error descr="Annotation @Field can only be used within a script body">@Field</error>
                           def bar
                         }

                         class X {
                           <error descr="Annotation @Field can only be used within a script">@Field</error>
                           def bar

                           def b() {
                             <error descr="Annotation @Field can only be used within a script">@Field</error>
                             def x
                           }
                         }
                         """);
  }

  public void testDuplicatedScriptField() {
    addGroovyTransformField();
    doTestHighlighting("""
                         import groovy.transform.Field

                         while(true) {
                           @Field def <error descr="Field 'foo' already defined">foo</error>
                         }

                         while(false) {
                           @Field def <error descr="Field 'foo' already defined">foo</error>
                         }

                         while(i) {
                           def foo
                         }

                         def foo
                         """);
  }

  public void testReturnTypeInStaticallyCompiledMethod() {
    addCompileStatic();
    doTestHighlighting("""
                         import groovy.transform.CompileStatic
                         @CompileStatic
                         int method(x, y, z) {
                             if (x) {
                                 <error descr="Cannot return 'String' from method returning 'int'">'String'</error>
                             } else if (y) {
                                 42
                             } else if (z) {
                               <error descr="Cannot return 'String' from method returning 'int'">return</error> 'abc'
                             } else {
                               return 43
                             }
                         }
                         """);
  }

  public void testOverrideForVars() {
    doTestHighlighting("""
                         class S {
                           @<error descr="'@Override' not applicable to field">Override</error> def foo;

                           def bar() {
                            @<error descr="'@Override' not applicable to local variable">Override</error> def x
                           }
                         }""");
  }

  public void testUnusedImportToList() {
    myFixture.addClass("""
                         package java.awt;
                         public class Component{}""");
    doTestHighlighting("""
                         import java.awt.Component
                         <warning descr="Unused import">import java.util.List</warning>

                         print Component
                         print List
                         """);
  }

  public void testUsedImportToList() {
    myFixture.addClass("""
                         package java.awt;
                         public class Component{}""");
    myFixture.addClass("""
                         package java.awt;
                         public class List{}""");
    myFixture.addClass("""
                         package java.util.concurrent;
                         public class ConcurrentHashMap{}""");
    doTestHighlighting("""
                         import java.awt.*
                         import java.util.List
                         <warning descr="Unused import">import java.util.concurrent.ConcurrentHashMap</warning>

                         print Component
                         print List
                         """);
  }

  public void testIncompatibleTypeOfImplicitGetter() {
    doTestHighlighting("""
                         abstract class Base {
                             abstract String getFoo()
                         }

                         class Inheritor extends Base {
                             final <error descr="The return type of java.lang.Object getFoo() in Inheritor is incompatible with java.lang.String getFoo() in Base">foo</error> = '3'
                         }""");
  }

  public void testIncompatibleTypeOfInheritedMethod() {
    doTestHighlighting("""
                         abstract class Base {
                           abstract String getFoo()
                         }

                         class Inheritor extends Base {
                             def <error descr="The return type of java.lang.Object getFoo() in Inheritor is incompatible with java.lang.String getFoo() in Base">getFoo</error>() {''}
                         }""");
  }

  public void testIncompatibleTypeOfInheritedMethod2() {
    doTestHighlighting("""
                         abstract class Base {
                           abstract String getFoo()
                         }

                         class Inheritor extends Base {
                             <error descr="The return type of java.lang.Object getFoo() in Inheritor is incompatible with java.lang.String getFoo() in Base">Object</error> getFoo() {''}
                         }""");
  }

  public void testIncompatibleTypeOfInheritedMethodInAnonymous() {
    doTestHighlighting("""
                         abstract class Base {
                           abstract String getFoo()
                         }

                         new Base() {
                             <error descr="The return type of java.lang.Object getFoo() in anonymous class derived from Base is incompatible with java.lang.String getFoo() in Base">Object</error> getFoo() {''}
                         }""");
  }

  public void testAnnotationArgs() {
    doTestHighlighting("""
                         @interface Int {
                           int value()
                           String s() default 'a'
                         }

                         @Int(<error descr="Cannot assign 'String' to 'int'">'a'</error>) def foo(){}

                         @Int(2) def bar(){}

                         @Int(value = 2) def c(){}

                         @Int(value = 3, s = <error descr="Cannot assign 'Integer' to 'String'">4</error>) def x(){}

                         @Int(value = 3, s = '4') def y(){}
                         """);
  }

  public void testDefaultAttributeValue() {
    doTestHighlighting("""
                         @interface Int {
                           int value1() default 2
                           String value2() default <error descr="Cannot assign 'Integer' to 'String'">2</error>
                           String value3()
                         }
                         """);
  }

  public void testAnnotationMethodThrowsList() {
    doTestHighlighting("""
                         @interface A {
                           int aaa() <error descr="'throws' clause is not allowed in @interface members">throws IOException</error>;
                           int aab() throws<error descr="<type> expected, got ';'"> </error>;
                         }
                         """);
  }

  public void testAnnotationAttributeTypes() {
    doTestHighlighting("""
                         @interface Int {
                           int a()
                           String b()
                           <error descr="Unexpected attribute type: 'PsiType:Date'">Date</error> c()
                           Int d()
                           int[] e()
                           String[] f()
                           boolean[][] g()
                           <error descr="Unexpected attribute type: 'PsiType:Boolean'">Boolean</error>[] h()
                           Int[][][][] i()
                         }
                         """);
  }

  public void testDefaultAnnotationValue() {
    doTestHighlighting("""
                         @interface A {
                           int a() default 2
                           String b() default <error descr="Cannot assign 'List<String>' to 'String'">['a']</error>
                           String[][] c() default <error descr="Cannot assign 'String' to 'String[][]'">'f'</error>
                           String[][] d() default [['f']]
                           String[][] e() default [[<error descr="Cannot assign 'List<String>' to 'String'">['f']</error>]]
                         }
                         """);
  }

  public void testAbstractMethodWithBody() {
    doTestHighlighting("""
                         interface A {
                           def foo()<error descr="Interface abstract methods must not have body">{}</error>
                         
                           <error descr="Interface members are not allowed to be private">private</error> def privateMethod() <error descr="Interface abstract methods must not have body">{}</error>
                         
                           <error descr="Interface must have no static method">static</error> def staticMethod() <error descr="Interface abstract methods must not have body">{}</error>
                         
                           <error descr="Modifier 'default' is available in Groovy 3.0 or later">default</error> def defaultMethod() {}
                         }

                         abstract class B {
                           abstract foo()<error descr="Abstract methods must not have body">{}</error>
                         }

                         class X {
                           def foo(){}
                         }
                         """);
  }

  public void testTupleVariableDeclarationWithRecursion() {
    doTestHighlighting("def (a, b) = [a, a]");
  }

  public void testSwitchInLoopNoSoe() {
    doTestHighlighting("""
                         def foo(File f) {
                           while (true) {
                             switch (f.name) {
                               case 'foo': f = new File('bar')
                             }
                             if (f) {
                               return
                             }
                           }
                         }""");
  }

  public void testInstanceMethodUsedInStaticClosure() {
    doTestHighlighting("""
                         class A {
                             static staticClosure = {
                                 foo()
                             }

                             def staticMethod() {
                                 foo()
                             }

                             def foo() { }
                         }
                         """);
  }

  public void testStaticOk() {
    doTestHighlighting("""
                         class A {
                           class B {}
                         }

                         A.B foo = new A.B()
                         """, GrUnresolvedAccessInspection.class);
  }

  public void testDuplicatedVar0() {
    doTestHighlighting("""
                         def a = 5
                         def <error descr="Variable 'a' already defined">a</error> = 7
                         """);
  }

  public void testDuplicatedVarInIf() {
    doTestHighlighting("""
                         def a = 5
                         if (cond)
                           def <error descr="Variable 'a' already defined">a</error> = 7
                         """);
  }

  public void testDuplicatedVarInAnonymous() {
    doTestHighlighting("""
                         def foo() {
                           def a = 5

                           new Runnable() {
                             void run() {
                               def <error descr="Variable 'a' already defined">a</error> = 7
                             }
                           }
                         }
                         """);
  }

  public void testDuplicatedVarInClosure() {
    doTestHighlighting("""
                         def foo() {
                           def a = 5

                           [1, 2, 3].each {
                             def <error descr="Variable 'a' already defined">a</error> = 7
                           }
                         }
                         """);
  }

  public void testDuplicatedVarInClosureParameter() {
    doTestHighlighting("""
                         def foo() {
                           def a = 5

                           [1, 2, 3].each {<error descr="Variable 'a' already defined">a</error> ->
                             print a
                           }
                         }
                         """);
  }

  public void testDuplicatedVarInAnonymousParameter() {
    doTestHighlighting("""
                         def a = -1

                         def foo() {
                           def a = 5

                           new Object() {
                             void bar(int <error descr="Variable 'a' already defined">a</error>) {}
                           }
                         }
                         """);
  }

  public void testNoDuplicateErrors() {
    doTestHighlighting("""
                         class Foo {
                           def foo

                           def abr(def foo) {}
                         }

                         class Test {
                           def foo

                           def bar() {
                             def foo
                           }
