Skip to content

Backport "bugfix: Also save infos in semanticdb" to 3.3 LTS #535

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jul 29, 2025
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
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ object TastyFileUtil {
s"${className.lastPart.encode}"
else
s"${packageName.encode}.${className.lastPart.encode}"
}
}


10 changes: 9 additions & 1 deletion compiler/src/dotty/tools/dotc/reporting/Reporter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ abstract class Reporter extends interfaces.ReporterResult {

private var _errorCount = 0
private var _warningCount = 0
private var _infoCount = 0

/** The number of errors reported by this reporter (ignoring outer reporters) */
def errorCount: Int = _errorCount
Expand All @@ -112,12 +113,17 @@ abstract class Reporter extends interfaces.ReporterResult {

private var warnings: List[Warning] = Nil

private var infos: List[Info] = Nil

/** All errors reported by this reporter (ignoring outer reporters) */
def allErrors: List[Error] = errors

/** All warnings reported by this reporter (ignoring outer reporters) */
def allWarnings: List[Warning] = warnings

/** All infos reported by this reporter (ignoring outer reporters) */
def allInfos: List[Info] = infos

/** Were sticky errors reported? Overridden in StoreReporter. */
def hasStickyErrors: Boolean = false

Expand Down Expand Up @@ -171,7 +177,9 @@ abstract class Reporter extends interfaces.ReporterResult {
_errorCount += 1
if ctx.typerState.isGlobalCommittable then
ctx.base.errorsToBeReported = true
case _: Info => // nothing to do here
case i: Info =>
infos = i :: infos
_infoCount += 1
// match error if d is something else
}
markReported(dia)
Expand Down
10 changes: 5 additions & 5 deletions compiler/src/dotty/tools/dotc/semanticdb/ExtractSemanticDB.scala
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ class ExtractSemanticDB private (phaseMode: ExtractSemanticDB.PhaseMode) extends

private def computeDiagnostics(
sourceRoot: String,
warnings: Map[SourceFile, List[Warning]],
warnings: Map[SourceFile, List[dotty.tools.dotc.reporting.Diagnostic]],
append: ((Path, List[Diagnostic])) => Unit)(using Context): Boolean = monitor(phaseName) {
val unit = ctx.compilationUnit
warnings.get(unit.source).foreach { ws =>
Expand Down Expand Up @@ -104,14 +104,14 @@ class ExtractSemanticDB private (phaseMode: ExtractSemanticDB.PhaseMode) extends
val appendDiagnostics = phaseMode == ExtractSemanticDB.PhaseMode.AppendDiagnostics
val unitContexts = units.map(ctx.fresh.setCompilationUnit(_).withRootImports)
if (appendDiagnostics)
val warnings = ctx.reporter.allWarnings.groupBy(w => w.pos.source)
val warningsAndInfos = (ctx.reporter.allWarnings ++ ctx.reporter.allInfos).groupBy(w => w.pos.source)
val buf = mutable.ListBuffer.empty[(Path, Seq[Diagnostic])]
val units0 =
for unitCtx <- unitContexts if computeDiagnostics(sourceRoot, warnings, buf += _)(using unitCtx)
for unitCtx <- unitContexts if computeDiagnostics(sourceRoot, warningsAndInfos, buf += _)(using unitCtx)
yield unitCtx.compilationUnit
cancellable {
buf.toList.asJava.parallelStream().forEach { case (out, warnings) =>
ExtractSemanticDB.appendDiagnostics(warnings, out)
buf.toList.asJava.parallelStream().forEach { case (out, diagnostics) =>
ExtractSemanticDB.appendDiagnostics(diagnostics, out)
}
}
units0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,8 @@ final class InferredMethodProvider(
.mkString(", ")

def printSignature(
methodName: Name,
params: List[List[Type]],
methodName: Name,
params: List[List[Type]],
retTypeOpt: Option[Type]
): String =
val retTypeString = retTypeOpt match
Expand All @@ -129,7 +129,7 @@ final class InferredMethodProvider(
if pos > 0 then
val isSpace = text(pos) == ' '
val isTab = text(pos) == '\t'
val indent = countIndent(params.text(), pos, 0)
val indent = countIndent(params.text().nn, pos, 0)

if isSpace then " " * indent else if isTab then "\t" * indent else ""
else ""
Expand All @@ -145,8 +145,8 @@ final class InferredMethodProvider(
/**
* Returns the position to insert the method signature for a container.
* If the container has an empty body, the position is the end of the container.
* If the container has a non-empty body, the position is the end of the last element in the body.
*
* If the container has a non-empty body, the position is the end of the last element in the body.
*
* @param container the container to insert the method signature for
* @return the position to insert the method signature for the container and a boolean indicating if the container has an empty body
*/
Expand All @@ -170,9 +170,9 @@ final class InferredMethodProvider(

/**
* Extracts type information for a specific parameter in a method signature.
* If the parameter is a function type, extracts both the function's argument types
* If the parameter is a function type, extracts both the function's argument types
* and return type. Otherwise, extracts just the parameter type.
*
*
* @param methodType the method type to analyze
* @param argIndex the index of the parameter to extract information for
* @return a tuple of (argument types, return type) where:
Expand All @@ -192,10 +192,10 @@ final class InferredMethodProvider(
else
(None, Some(m.paramInfos(argIndex)))
case _ => (None, None)

def signatureEdits(signature: String): List[TextEdit] =
val pos = insertPosition()
val indent = indentation(params.text(), pos.start - 1)
val indent = indentation(params.text().nn, pos.start - 1)
val lspPos = pos.toLsp
lspPos.setEnd(lspPos.getStart())

Expand All @@ -211,7 +211,7 @@ final class InferredMethodProvider(
case Some((pos, hasEmptyBody)) =>
val lspPos = pos.toLsp
lspPos.setStart(lspPos.getEnd())
val indent = indentation(params.text(), pos.start - 1)
val indent = indentation(params.text().nn, pos.start - 1)

if hasEmptyBody then
List(
Expand All @@ -234,17 +234,17 @@ final class InferredMethodProvider(
* outerArgs
* ---------------------------
* method(..., errorMethod(args), ...)
*
*
*/
case (id @ Ident(errorMethod)) ::
(apply @ Apply(func, args)) ::
Apply(method, outerArgs) ::
case (id @ Ident(errorMethod)) ::
(apply @ Apply(func, args)) ::
Apply(method, outerArgs) ::
_ if id.symbol == NoSymbol && func == id && method != apply =>

val argTypes = args.map(_.typeOpt.widenDealias)

val argIndex = outerArgs.indexOf(apply)
val (allArgTypes, retTypeOpt) =
val (allArgTypes, retTypeOpt) =
extractParameterTypeInfo(method.tpe.widenDealias, argIndex) match
case (Some(argTypes2), retTypeOpt) => (List(argTypes, argTypes2), retTypeOpt)
case (None, retTypeOpt) => (List(argTypes), retTypeOpt)
Expand All @@ -257,12 +257,12 @@ final class InferredMethodProvider(
* outerArgs
* ---------------------
* method(..., errorMethod, ...)
*
*
*/
case (id @ Ident(errorMethod)) ::
Apply(method, outerArgs) ::
case (id @ Ident(errorMethod)) ::
Apply(method, outerArgs) ::
_ if id.symbol == NoSymbol && method != id =>

val argIndex = outerArgs.indexOf(id)

val (argTypes, retTypeOpt) = extractParameterTypeInfo(method.tpe.widenDealias, argIndex)
Expand All @@ -272,20 +272,20 @@ final class InferredMethodProvider(
case None => Nil

val signature = printSignature(errorMethod, allArgTypes, retTypeOpt)

signatureEdits(signature)

/**
* tpt body
* ----------- ----------------
* val value: DefinedType = errorMethod(args)
*
*
*/
case (id @ Ident(errorMethod)) ::
(apply @ Apply(func, args)) ::
ValDef(_, tpt, body) ::
case (id @ Ident(errorMethod)) ::
(apply @ Apply(func, args)) ::
ValDef(_, tpt, body) ::
_ if id.symbol == NoSymbol && func == id && apply == body =>

val retType = tpt.tpe.widenDealias
val argTypes = args.map(_.typeOpt.widenDealias)

Expand All @@ -296,24 +296,24 @@ final class InferredMethodProvider(
* tpt body
* ----------- -----------
* val value: DefinedType = errorMethod
*
*
*/
case (id @ Ident(errorMethod)) ::
ValDef(_, tpt, body) ::
case (id @ Ident(errorMethod)) ::
ValDef(_, tpt, body) ::
_ if id.symbol == NoSymbol && id == body =>

val retType = tpt.tpe.widenDealias

val signature = printSignature(errorMethod, Nil, Some(retType))
signatureEdits(signature)

/**
*
*
* errorMethod(args)
*
*
*/
case (id @ Ident(errorMethod)) ::
(apply @ Apply(func, args)) ::
case (id @ Ident(errorMethod)) ::
(apply @ Apply(func, args)) ::
_ if id.symbol == NoSymbol && func == id =>

val argTypes = args.map(_.typeOpt.widenDealias)
Expand All @@ -322,37 +322,37 @@ final class InferredMethodProvider(
signatureEdits(signature)

/**
*
*
* errorMethod
*
*
*/
case (id @ Ident(errorMethod)) ::
case (id @ Ident(errorMethod)) ::
_ if id.symbol == NoSymbol =>

val signature = printSignature(errorMethod, Nil, None)
signatureEdits(signature)

/**
*
*
* container.errorMethod(args)
*
*
*/
case (select @ Select(container, errorMethod)) ::
(apply @ Apply(func, args)) ::
case (select @ Select(container, errorMethod)) ::
(apply @ Apply(func, args)) ::
_ if select.symbol == NoSymbol && func == select =>

val argTypes = args.map(_.typeOpt.widenDealias)
val signature = printSignature(errorMethod, List(argTypes), None)
signatureEditsForContainer(signature, container)

/**
*
*
* container.errorMethod
*
*
*/
case (select @ Select(container, errorMethod)) ::
case (select @ Select(container, errorMethod)) ::
_ if select.symbol == NoSymbol =>

val signature = printSignature(errorMethod, Nil, None)
signatureEditsForContainer(signature, container)

Expand Down
14 changes: 14 additions & 0 deletions tests/semanticdb/expect/InfoMacro.expect.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import scala.quoted.*

object InfoMacro/*<-_empty_::InfoMacro.*/ {
inline def reportInfo/*<-_empty_::InfoMacro.reportInfo().*/(msg/*<-_empty_::InfoMacro.reportInfo().(msg)*/: String/*->scala::Predef.String#*/): Unit/*->scala::Unit#*/ = ${ reportInfoMacro/*->_empty_::InfoMacro.reportInfoMacro().*/('msg) }

def reportInfoMacro/*<-_empty_::InfoMacro.reportInfoMacro().*/(msg/*<-_empty_::InfoMacro.reportInfoMacro().(msg)*/: Expr/*->scala::quoted::Expr#*/[String/*->scala::Predef.String#*/])(using Quotes/*->scala::quoted::Quotes#*/): Expr/*->scala::quoted::Expr#*/[Unit/*->scala::Unit#*/] = {
import quotes/*->scala::quoted::Quotes$package.quotes().*/.reflect/*->scala::quoted::Quotes#reflect.*/.report/*->scala::quoted::Quotes#reflectModule#report.*/

// Report an info diagnostic
report/*->scala::quoted::Quotes#reflectModule#report.*/.info/*->scala::quoted::Quotes#reflectModule#reportModule#info().*/(s/*->scala::StringContext#s().*/"Info from macro: ${msg/*->_empty_::InfoMacro.reportInfoMacro().(msg)*/.valueOrAbort/*->scala::quoted::Quotes#valueOrAbort().*/}")

'{ () }
}
}
14 changes: 14 additions & 0 deletions tests/semanticdb/expect/InfoMacro.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import scala.quoted.*

object InfoMacro {
inline def reportInfo(msg: String): Unit = ${ reportInfoMacro('msg) }

def reportInfoMacro(msg: Expr[String])(using Quotes): Expr[Unit] = {
import quotes.reflect.report

// Report an info diagnostic
report.info(s"Info from macro: ${msg.valueOrAbort}")

'{ () }
}
}
7 changes: 7 additions & 0 deletions tests/semanticdb/expect/InfoMacroTest.expect.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@

object InfoMacroTest/*<-_empty_::InfoMacroTest.*/ {
def main/*<-_empty_::InfoMacroTest.main().*/(): Unit/*->scala::Unit#*/ = {
InfoMacro/*->_empty_::InfoMacro.*/.reportInfo/*->_empty_::InfoMacro.reportInfo().*/("This is a test info message")
InfoMacro/*->_empty_::InfoMacro.*/.reportInfo/*->_empty_::InfoMacro.reportInfo().*/("Another info message")
}
}
7 changes: 7 additions & 0 deletions tests/semanticdb/expect/InfoMacroTest.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@

object InfoMacroTest {
def main(): Unit = {
InfoMacro.reportInfo("This is a test info message")
InfoMacro.reportInfo("Another info message")
}
}
Loading