Skip to content

Commit 54b2c7d

Browse files
committed
Fix CSA kotlin issue
1 parent d0e7953 commit 54b2c7d

File tree

2 files changed

+185
-1
lines changed

2 files changed

+185
-1
lines changed

src/main/java/org/openrewrite/staticanalysis/CovariantEquals.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,16 @@ public TreeVisitor<?, ExecutionContext> getVisitor() {
6565
@Override
6666
public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext ctx) {
6767
J.MethodDeclaration m = super.visitMethodDeclaration(method, ctx);
68-
J.ClassDeclaration enclosingClass = getCursor().dropParentUntil(p -> p instanceof J.ClassDeclaration).getValue();
68+
69+
// Find the enclosing type declaration - could be class, interface, enum, etc.
70+
// For Kotlin, methods might be top-level (no class parent at all)
71+
Object parent = getCursor().getParent(J.ClassDeclaration.class);
72+
if (parent == null) {
73+
// Not in a J.ClassDeclaration - could be interface, enum, or Kotlin structure
74+
return m;
75+
}
76+
77+
J.ClassDeclaration enclosingClass = (J.ClassDeclaration) parent;
6978

7079
/*
7180
* Looking for "public boolean equals(EnclosingClassType)" as the method signature match.
Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
/*
2+
* Copyright 2024 the original author or authors.
3+
* <p>
4+
* Licensed under the Moderne Source Available License (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
* <p>
8+
* https://docs.moderne.io/licensing/moderne-source-available-license
9+
* <p>
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.openrewrite.staticanalysis.kotlin;
17+
18+
import org.junit.jupiter.api.Test;
19+
import org.openrewrite.Issue;
20+
import org.openrewrite.staticanalysis.CovariantEquals;
21+
import org.openrewrite.test.RecipeSpec;
22+
import org.openrewrite.test.RewriteTest;
23+
24+
import static org.openrewrite.kotlin.Assertions.kotlin;
25+
26+
class CovariantEqualsTest implements RewriteTest {
27+
28+
@Override
29+
public void defaults(RecipeSpec spec) {
30+
spec.recipe(new CovariantEquals());
31+
}
32+
33+
@Test
34+
void topLevelFunctionDoesNotCrash() {
35+
// This test verifies that the recipe doesn't crash when encountering a top-level Kotlin function
36+
// Top-level functions don't have a class parent, which was causing the dropParentUntil error
37+
rewriteRun(
38+
kotlin(
39+
"""
40+
// Top-level equals function - should not cause a crash
41+
fun equals(other: String): Boolean {
42+
return false
43+
}
44+
45+
fun main() {
46+
println("Test")
47+
}
48+
"""
49+
)
50+
);
51+
}
52+
53+
@Test
54+
void kotlinClassWithCovariantEquals() {
55+
// Test that Kotlin classes with covariant equals are left unchanged
56+
// (Kotlin has its own mechanisms for handling equals)
57+
rewriteRun(
58+
kotlin(
59+
"""
60+
class Test {
61+
var n: Int = 0
62+
63+
fun equals(other: Test): Boolean {
64+
return n == other.n
65+
}
66+
}
67+
"""
68+
)
69+
);
70+
}
71+
72+
@Test
73+
void kotlinDataClassUnchanged() {
74+
// Data classes have compiler-generated equals methods
75+
rewriteRun(
76+
kotlin(
77+
"""
78+
data class Person(val name: String, val age: Int)
79+
"""
80+
)
81+
);
82+
}
83+
84+
@Test
85+
void kotlinInterfaceWithEquals() {
86+
// Test that interfaces with equals methods don't cause crashes
87+
rewriteRun(
88+
kotlin(
89+
"""
90+
interface TestInterface {
91+
fun equals(other: TestInterface): Boolean
92+
}
93+
94+
class TestImpl : TestInterface {
95+
override fun equals(other: TestInterface): Boolean {
96+
return true
97+
}
98+
}
99+
"""
100+
)
101+
);
102+
}
103+
104+
@Test
105+
void kotlinObjectDeclaration() {
106+
// Test that object declarations (singletons) don't cause crashes
107+
rewriteRun(
108+
kotlin(
109+
"""
110+
object Singleton {
111+
fun equals(other: Singleton): Boolean {
112+
return true
113+
}
114+
}
115+
"""
116+
)
117+
);
118+
}
119+
120+
@Test
121+
void kotlinEnumWithEquals() {
122+
// Test that enum classes with equals methods don't cause crashes
123+
rewriteRun(
124+
kotlin(
125+
"""
126+
enum class Color {
127+
RED, GREEN, BLUE;
128+
129+
fun equals(other: Color): Boolean {
130+
return this == other
131+
}
132+
}
133+
"""
134+
)
135+
);
136+
}
137+
138+
@Test
139+
void kotlinNestedClassWithEquals() {
140+
// Test nested classes with equals methods
141+
rewriteRun(
142+
kotlin(
143+
"""
144+
class Outer {
145+
class Inner {
146+
var value: Int = 0
147+
148+
fun equals(other: Inner): Boolean {
149+
return value == other.value
150+
}
151+
}
152+
}
153+
"""
154+
)
155+
);
156+
}
157+
158+
@Test
159+
void kotlinCompanionObjectWithEquals() {
160+
// Test companion objects with equals methods
161+
rewriteRun(
162+
kotlin(
163+
"""
164+
class MyClass {
165+
companion object {
166+
fun equals(other: MyClass): Boolean {
167+
return false
168+
}
169+
}
170+
}
171+
"""
172+
)
173+
);
174+
}
175+
}

0 commit comments

Comments
 (0)