Skip to content

Commit 01447df

Browse files
authored
fix: StringIndexOutOfBoundsException in presentation compiler's hasColon method (#23498)
This PR fixes a `StringIndexOutOfBoundsException` that could occur in the presentation compiler when checking for colons in override completions. ## Problem The `dotty.tools.pc.completions.OverrideCompletions.hasColon` method was accessing characters at specific indices without first verifying that the indices were within the text bounds. This could cause crashes when the span end positions were at or beyond the text length. ## Solution Added bounds checking before accessing characters: - For `TypeDef` cases: verify `td.rhs.span.end < text.length` before `text.charAt(td.rhs.span.end)` - For `Template` parent cases: filter to ensure `text.length > idx` before `text.charAt(idx)` ## Testing The existing test suite passes, and a new test case `no-new-line` was added to cover the edge case. [resolves scalameta/metals#7575](scalameta/metals#7575)
1 parent 97896fa commit 01447df

File tree

2 files changed

+48
-28
lines changed

2 files changed

+48
-28
lines changed

presentation-compiler/src/main/dotty/tools/pc/completions/OverrideCompletions.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -511,10 +511,10 @@ object OverrideCompletions:
511511
Context
512512
): Option[Int] =
513513
defn match
514-
case td: TypeDef if text.charAt(td.rhs.span.end) == ':' =>
514+
case td: TypeDef if (td.rhs.span.end < text.length) && text.charAt(td.rhs.span.end) == ':' =>
515515
Some(td.rhs.span.end)
516516
case TypeDef(_, temp : Template) =>
517-
temp.parentsOrDerived.lastOption.map(_.span.end).filter(text.charAt(_) == ':')
517+
temp.parentsOrDerived.lastOption.map(_.span.end).filter(idx => text.length > idx && text.charAt(idx) == ':')
518518
case _ => None
519519

520520
private def fallbackFromParent(parent: Tree, name: String)(using Context) =

presentation-compiler/test/dotty/tools/pc/tests/edit/AutoImplementAbstractMembersSuite.scala

Lines changed: 46 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -75,39 +75,59 @@ class AutoImplementAbstractMembersSuite extends BaseCodeActionSuite:
7575
|""".stripMargin
7676
)
7777

78-
@Test def `empty-lines-between-members` =
78+
@Test def `no-new-line` =
7979
checkEdit(
8080
"""|package a
81-
|
82-
|object A {
83-
| trait Base {
84-
| def foo(x: Int): Int
85-
| def bar(x: String): String
86-
| }
87-
| class <<Concrete>> extends Base {
88-
|
89-
| def bar(x: String): String = ???
90-
|
91-
| }
92-
|}
93-
|""".stripMargin,
81+
|
82+
|trait X:
83+
| def foo: Unit
84+
|
85+
|class <<Y>> extends X""".stripMargin,
9486
"""|package a
9587
|
96-
|object A {
97-
| trait Base {
98-
| def foo(x: Int): Int
99-
| def bar(x: String): String
100-
| }
101-
| class Concrete extends Base {
102-
|
88+
|trait X:
89+
| def foo: Unit
10390
|
104-
| override def foo(x: Int): Int = ???
91+
|class Y extends X {
10592
|
106-
| def bar(x: String): String = ???
93+
| override def foo: Unit = ???
10794
|
108-
| }
109-
|}
110-
|""".stripMargin
95+
|}""".stripMargin,
96+
)
97+
98+
@Test def `empty-lines-between-members` =
99+
checkEdit(
100+
"""|package a
101+
|
102+
|object A {
103+
| trait Base {
104+
| def foo(x: Int): Int
105+
| def bar(x: String): String
106+
| }
107+
| class <<Concrete>> extends Base {
108+
|
109+
| def bar(x: String): String = ???
110+
|
111+
| }
112+
|}
113+
|""".stripMargin,
114+
"""|package a
115+
|
116+
|object A {
117+
| trait Base {
118+
| def foo(x: Int): Int
119+
| def bar(x: String): String
120+
| }
121+
| class Concrete extends Base {
122+
|
123+
|
124+
| override def foo(x: Int): Int = ???
125+
|
126+
| def bar(x: String): String = ???
127+
|
128+
| }
129+
|}
130+
|""".stripMargin
111131
)
112132

113133
@Test def `objectdef` =

0 commit comments

Comments
 (0)