Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 57 additions & 49 deletions compiler/src/dotty/tools/dotc/reporting/MessageRendering.scala
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,6 @@ trait MessageRendering {
def stripColor(str: String): String =
str.replaceAll("\u001b\\[.*?m", "")

/** List of all the inline calls that surround the position */
def inlinePosStack(pos: SourcePosition): List[SourcePosition] =
if pos.outer != null && pos.outer.exists then pos :: inlinePosStack(pos.outer)
else Nil

/** Get the sourcelines before and after the position, as well as the offset
* for rendering line numbers
*
Expand Down Expand Up @@ -236,17 +231,27 @@ trait MessageRendering {
if origin.nonEmpty then
addHelp("origin=")(origin)

/** The whole message rendered from `msg` */
def messageAndPos(dia: Diagnostic)(using Context): String = {
import dia.*
val pos1 = pos.nonInlined
val inlineStack = inlinePosStack(pos).filter(_ != pos1)
val maxLineNumber =
if pos.exists then (pos1 :: inlineStack).map(_.endLine).max + 1
else 0
given Level = Level(level)
given Offset = Offset(maxLineNumber.toString.length + 2)
val sb = StringBuilder()
/** The whole message rendered from `dia.msg`.
*
* For a position in an inline expansion, choose `pos1`
* which is the most specific position in the call written
* by the user. For a diagnostic at EOF, where the last char
* of source text is a newline, adjust the position to render
* before the newline, at the end of the last line of text.
*
* The rendering begins with a label and position (`posString`).
* Then `sourceLines` with embedded caret `positionMarker`
* and rendered message.
*
* Then an `Inline stack trace` showing context for inlined code.
* Inlined positions are taken which don't contain `pos1`.
* (That should probably be positions not contained by outermost.)
* Note that position equality includes `outer` position;
* usually we intend to test `contains` or `coincidesWith`.
*
*/
def messageAndPos(dia: Diagnostic)(using Context): String =
// adjust a pos at EOF if preceded by newline
def adjust(pos: SourcePosition): SourcePosition =
if pos.span.isSynthetic
&& pos.span.isZeroExtent
Expand All @@ -257,54 +262,57 @@ trait MessageRendering {
pos.withSpan(pos.span.shift(-1))
else
pos
val adjusted = adjust(pos)
val posString = posStr(adjusted, msg, diagnosticLevel(dia))
if (posString.nonEmpty) sb.append(posString).append(EOL)
if (pos.exists) {
val pos1 = pos.nonInlined
if (pos1.exists && pos1.source.file.exists) {
val readjusted =
if pos1 == pos then adjusted
else adjust(pos1)
val (srcBefore, srcAfter, offset) = sourceLines(readjusted)
val marker = positionMarker(readjusted)
val err = errorMsg(readjusted, msg.message)
sb.append((srcBefore ::: marker :: err :: srcAfter).mkString(EOL))

if inlineStack.nonEmpty then
sb.append(EOL).append(newBox())
sb.append(EOL).append(offsetBox).append(i"Inline stack trace")
for inlinedPos <- inlineStack if inlinedPos != pos1 do
sb.append(EOL).append(newBox(soft = true))
sb.append(EOL).append(offsetBox).append(i"This location contains code that was inlined from $pos")
if inlinedPos.source.file.exists then
val (srcBefore, srcAfter, _) = sourceLines(inlinedPos)
val marker = positionMarker(inlinedPos)
sb.append(EOL).append((srcBefore ::: marker :: srcAfter).mkString(EOL))
sb.append(EOL).append(endBox)
}
else sb.append(msg.message)
}
val msg = dia.msg
val pos = dia.pos
val pos1 = adjust(pos.nonInlined) // innermost pos contained by call.pos
val outermost = pos.outermost // call.pos
val inlineStack = pos.inlinePosStack.filterNot(outermost.contains(_))
given Level = Level(dia.level)
given Offset =
val maxLineNumber =
if pos.exists then (pos1 :: inlineStack).map(_.endLine).max + 1
else 0
Offset(maxLineNumber.toString.length + 2)
val sb = StringBuilder()
val posString = posStr(pos1, msg, diagnosticLevel(dia))
if posString.nonEmpty then sb.append(posString).append(EOL)
if pos.exists && pos1.exists && pos1.source.file.exists then
val (srcBefore, srcAfter, offset) = sourceLines(pos1)
val marker = positionMarker(pos1)
val err = errorMsg(pos1, msg.message)
sb.append((srcBefore ::: marker :: err :: srcAfter).mkString(EOL))

if inlineStack.nonEmpty then
sb.append(EOL).append(newBox())
sb.append(EOL).append(offsetBox).append(i"Inline stack trace")
for inlinedPos <- inlineStack do
sb.append(EOL).append(newBox(soft = true))
sb.append(EOL).append(offsetBox).append(i"This location contains code that was inlined from $pos")
if inlinedPos.source.file.exists then
val (srcBefore, srcAfter, _) = sourceLines(inlinedPos)
val marker = positionMarker(inlinedPos)
sb.append(EOL).append((srcBefore ::: marker :: srcAfter).mkString(EOL))
sb.append(EOL).append(endBox)
end if
else sb.append(msg.message)
if (dia.isVerbose)
if dia.isVerbose then
appendFilterHelp(dia, sb)

if Diagnostic.shouldExplain(dia) then
sb.append(EOL).append(newBox())
sb.append(EOL).append(offsetBox).append(" Explanation (enabled by `-explain`)")
sb.append(EOL).append(newBox(soft = true))
dia.msg.explanation.split(raw"\R").foreach { line =>
dia.msg.explanation.split(raw"\R").foreach: line =>
sb.append(EOL).append(offsetBox).append(if line.isEmpty then "" else " ").append(line)
}
sb.append(EOL).append(endBox)
else if dia.msg.canExplain then
sb.append(EOL).append(offsetBox)
sb.append(EOL).append(offsetBox).append(" longer explanation available when compiling with `-explain`")

sb.toString
}
end messageAndPos

private def hl(str: String)(using Context, Level): String =
private def hl(str: String)(using Context, Level): String =
summon[Level].value match
case interfaces.Diagnostic.ERROR => Red(str).show
case interfaces.Diagnostic.WARNING => Yellow(str).show
Expand Down
5 changes: 4 additions & 1 deletion compiler/src/dotty/tools/dotc/reporting/WConf.scala
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,10 @@ class Suppression(val annotPos: SourcePosition, val filters: List[MessageFilter]
_used = supersededState
def matches(dia: Diagnostic): Boolean =
val pos = dia.pos
pos.exists && start <= pos.start && pos.end <= end && filters.forall(_.matches(dia))
def posMatches =
start <= pos.start && pos.end <= end
|| pos.inlinePosStack.exists(p => start <= p.start && p.end <= end)
pos.exists && posMatches && filters.forall(_.matches(dia))

override def toString = s"Suppress in ${annotPos.source} $start..$end [${filters.mkString(", ")}]"
end Suppression
13 changes: 10 additions & 3 deletions compiler/src/dotty/tools/dotc/util/SourcePosition.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ import scala.annotation.internal.sharable

/** A source position is comprised of a span and a source file */
case class SourcePosition(source: SourceFile, span: Span, outer: SourcePosition = NoSourcePosition)
extends SrcPos, interfaces.SourcePosition, Showable {
extends SrcPos, interfaces.SourcePosition, Showable:

def sourcePos(using Context) = this

/** Is `that` a source position contained in this source position ?
/** Is `that` a source position contained in this source position?
* `outer` is not taken into account. */
def contains(that: SourcePosition): Boolean =
this.source == that.source && this.span.contains(that.span)
Expand Down Expand Up @@ -82,7 +82,14 @@ extends SrcPos, interfaces.SourcePosition, Showable {
s"${if (source.exists) source.file.toString else "(no source)"}:$span"

def toText(printer: Printer): Text = printer.toText(this)
}

object SourcePosition:
extension (pos: SourcePosition)
/** List of all the inline calls that surround the position. */
def inlinePosStack: List[SourcePosition] =
if pos.outer != null && pos.outer.exists then pos :: pos.outer.inlinePosStack
else pos :: Nil
end SourcePosition

/** A sentinel for a non-existing source position */
@sharable object NoSourcePosition extends SourcePosition(NoSource, NoSpan) {
Expand Down
6 changes: 3 additions & 3 deletions tests/neg/i23815.check
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@
| ':' or '{' expected, but 'end of statement' found
| Nested package statements that are not at the beginning of the file require braces or ':' with an indented body.
-- [E040] Syntax Error: tests/neg/i23815.scala:9:8 ---------------------------------------------------------------------
9 |// error
| ^
| '}' expected, but eof found
9 |// error
| ^
| '}' expected, but eof found
13 changes: 13 additions & 0 deletions tests/warn/i24082.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//> using options -deprecation -Werror

import annotation.*

@deprecated
case object A {
inline def use: Any = A
}

@nowarn
object test {
A.use
}