Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 902d9d9

Browse files
authored
Add line number magic comment support (#23549)
- Added compiler setting to configure magic line number comments - Implemented line number offset reporting when magic comments are present fixes #21919.
2 parents d4e0aae + cc8b04a commit 902d9d9

16 files changed

+123
-4
lines changed

‎compiler/src/dotty/tools/dotc/ast/Positioned.scala‎

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ package dotc
33
package ast
44

55
import util.Spans.*
6-
import util.{SourceFile, SourcePosition, SrcPos}
6+
import util.{SourceFile, SourcePosition, SrcPos, WrappedSourceFile}
7+
import WrappedSourceFile.MagicHeaderInfo, MagicHeaderInfo.*
78
import core.Contexts.*
89
import core.Decorators.*
910
import core.NameOps.*
@@ -51,7 +52,15 @@ abstract class Positioned(implicit @constructorOnly src: SourceFile) extends Src
5152

5253
def source: SourceFile = mySource
5354

54-
def sourcePos(using Context): SourcePosition = source.atSpan(span)
55+
def sourcePos(using Context): SourcePosition =
56+
val info = WrappedSourceFile.locateMagicHeader(source)
57+
info match
58+
case HasHeader(offset, originalFile) =>
59+
if span.start >= offset then // This span is in user code
60+
originalFile.atSpan(span.shift(-offset))
61+
else // Otherwise, return the source position in the wrapper code
62+
source.atSpan(span)
63+
case _ => source.atSpan(span)
5564

5665
/** This positioned item, widened to `SrcPos`. Used to make clear we only need the
5766
* position, typically for error reporting.

‎compiler/src/dotty/tools/dotc/config/ScalaSettings.scala‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,8 @@ private sealed trait YSettings:
444444
val YbestEffort: Setting[Boolean] = BooleanSetting(ForkSetting, "Ybest-effort", "Enable best-effort compilation attempting to produce betasty to the META-INF/best-effort directory, regardless of errors, as part of the pickler phase.")
445445
val YwithBestEffortTasty: Setting[Boolean] = BooleanSetting(ForkSetting, "Ywith-best-effort-tasty", "Allow to compile using best-effort tasty files. If such file is used, the compiler will stop after the pickler phase.")
446446

447+
val YmagicOffsetHeader: Setting[String] = StringSetting(ForkSetting, "Ymagic-offset-header", "header", "Specify the magic header comment that marks the start of the actual code in generated wrapper scripts. Example: -Ymagic-offset-header:SOURCE_CODE_START. Then, in the source, the magic comment `///SOURCE_CODE_START:<ORIGINAL_FILE_PATH>` marks the start of user code. The comment should be suffixed by `:<ORIGINAL_FILE_PATH>` to indicate the original file.", "")
448+
447449
// Experimental language features
448450
@deprecated(message = "This flag has no effect and will be removed in a future version.", since = "3.7.0")
449451
val YnoKindPolymorphism: Setting[Boolean] = BooleanSetting(ForkSetting, "Yno-kind-polymorphism", "Disable kind polymorphism. (This flag has no effect)", deprecation = Deprecation.removed())

‎compiler/src/dotty/tools/dotc/reporting/MessageRendering.scala‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import core.Decorators.*
1111
import printing.Highlighting.{Blue, Red, Yellow}
1212
import printing.SyntaxHighlighting
1313
import Diagnostic.*
14-
import util.{SourcePosition, NoSourcePosition}
14+
import util.{SourcePosition, NoSourcePosition}
1515
import util.Chars.{ LF, CR, FF, SU }
1616
import scala.annotation.switch
1717

‎compiler/src/dotty/tools/dotc/rewrites/Rewrites.scala‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import core.Contexts.*
77
import collection.mutable
88
import scala.annotation.tailrec
99
import dotty.tools.dotc.reporting.Reporter
10-
import dotty.tools.dotc.util.SourcePosition;
10+
import dotty.tools.dotc.util.SourcePosition
1111

1212
import java.io.OutputStreamWriter
1313
import java.nio.charset.StandardCharsets.UTF_8

‎compiler/src/dotty/tools/dotc/util/SourceFile.scala‎

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import scala.language.unsafeNulls
77
import dotty.tools.io.*
88
import Spans.*
99
import core.Contexts.*
10+
import core.Decorators.*
1011

1112
import scala.io.Codec
1213
import Chars.*
@@ -61,6 +62,36 @@ object ScriptSourceFile {
6162
}
6263
}
6364

65+
object WrappedSourceFile:
66+
enum MagicHeaderInfo:
67+
case HasHeader(offset: Int, originalFile: SourceFile)
68+
case NoHeader
69+
import MagicHeaderInfo.*
70+
71+
private val cache: mutable.HashMap[SourceFile, MagicHeaderInfo] = mutable.HashMap.empty
72+
73+
def locateMagicHeader(sourceFile: SourceFile)(using Context): MagicHeaderInfo =
74+
def findOffset: MagicHeaderInfo =
75+
val magicHeader = ctx.settings.YmagicOffsetHeader.value
76+
if magicHeader.isEmpty then NoHeader
77+
else
78+
val text = new String(sourceFile.content)
79+
val headerQuoted = java.util.regex.Pattern.quote("///" + magicHeader)
80+
val regex = s"(?m)^$headerQuoted:(.+)$$".r
81+
regex.findFirstMatchIn(text) match
82+
case Some(m) =>
83+
val markerOffset = m.start
84+
val sourceStartOffset = sourceFile.nextLine(markerOffset)
85+
val file = ctx.getFile(m.group(1))
86+
if file.exists then
87+
HasHeader(sourceStartOffset, ctx.getSource(file))
88+
else
89+
report.warning(em"original source file not found: ${file.path}")
90+
NoHeader
91+
case None => NoHeader
92+
val result = cache.getOrElseUpdate(sourceFile, findOffset)
93+
result
94+
6495
class SourceFile(val file: AbstractFile, computeContent: => Array[Char]) extends interfaces.SourceFile {
6596
import SourceFile.*
6697

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
2+
def test1(): Int = "无穷" // error
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
-- [E007] Type Mismatch Error: tests/neg/magic-offset-header-a.scala:2:19 ----------------------------------------------
2+
2 |def test1(): Int = "无穷" // error
3+
| ^^^^
4+
| Found: ("无穷" : String)
5+
| Required: Int
6+
|
7+
| longer explanation available when compiling with `-explain`
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
//> using options -Ymagic-offset-header:TEST_MARKER
2+
val t1 = 1
3+
val t2 = 2
4+
val t3 = 3
5+
///TEST_MARKER:tests/neg/magic-offset-header-a.scala
6+
7+
def test1(): Int = "无穷" // anypos-error
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
2+
def y: Int = false // error
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
-- [E007] Type Mismatch Error: tests/neg/magic-offset-header-b_wrapper.scala:3:13 --------------------------------------
2+
3 |def x: Int = true // error
3+
| ^^^^
4+
| Found: (true : Boolean)
5+
| Required: Int
6+
|
7+
| longer explanation available when compiling with `-explain`
8+
-- [E007] Type Mismatch Error: tests/neg/magic-offset-header-b.scala:2:13 ----------------------------------------------
9+
2 |def y: Int = false // error
10+
| ^^^^^
11+
| Found: (false : Boolean)
12+
| Required: Int
13+
|
14+
| longer explanation available when compiling with `-explain`

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /