From f17fb5ffe47e5e54622d6b24a7391ced18430afe Mon Sep 17 00:00:00 2001 From: Rikito Taniguchi Date: Tue, 27 Jun 2023 20:38:17 +0900 Subject: [PATCH] ExtractSemanticDB.PostInlining now update the .semanticdb file on disk --- .../dotc/semanticdb/ExtractSemanticDB.scala | 96 +++++++++++-------- 1 file changed, 54 insertions(+), 42 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/semanticdb/ExtractSemanticDB.scala b/compiler/src/dotty/tools/dotc/semanticdb/ExtractSemanticDB.scala index 61c2dc2bb560..02af34c5687b 100644 --- a/compiler/src/dotty/tools/dotc/semanticdb/ExtractSemanticDB.scala +++ b/compiler/src/dotty/tools/dotc/semanticdb/ExtractSemanticDB.scala @@ -27,6 +27,7 @@ import dotty.tools.dotc.{semanticdb => s} import dotty.tools.io.{AbstractFile, JarArchive} import dotty.tools.dotc.util.Property import dotty.tools.dotc.semanticdb.DiagnosticOps.* +import scala.util.{Using, Failure, Success} /** Extract symbol references and uses to semanticdb files. @@ -36,17 +37,15 @@ import dotty.tools.dotc.semanticdb.DiagnosticOps.* * Here, we define two phases for "ExtractSemanticDB", "PostTyper" and "PostInlining". * * The "PostTyper" phase extracts SemanticDB information such as symbol - * definitions, symbol occurrences, type information, and synthetics. - * This phase does not write the information to a .semanticdb file; - * instead, it attaches the SemanticDB information to the top-level tree. + * definitions, symbol occurrences, type information, and synthetics + * and write .semanticdb file. * * The "PostInlining" phase extracts diagnostics from "ctx.reporter" and * attaches them to the SemanticDB information extracted in the "PostTyper" phase. - * Afterwards, it writes the SemanticDB to a ".semanticdb" file. * We need to run this phase after the "CheckUnused.PostInlining" phase * so that we can extract the warnings generated by "-Wunused". */ -class ExtractSemanticDB private (phaseMode: ExtractSemanticDB.PhaseMode, suffix: String, _key: Property.Key[TextDocument]) extends Phase: +class ExtractSemanticDB private (phaseMode: ExtractSemanticDB.PhaseMode, suffix: String) extends Phase: override val phaseName: String = ExtractSemanticDB.phaseNamePrefix + suffix @@ -65,15 +64,13 @@ class ExtractSemanticDB private (phaseMode: ExtractSemanticDB.PhaseMode, suffix: if (phaseMode == ExtractSemanticDB.PhaseMode.PostTyper) val extractor = ExtractSemanticDB.Extractor() extractor.extract(unit.tpdTree) - unit.tpdTree.putAttachment(_key, extractor.toTextDocument(unit.source)) + ExtractSemanticDB.write(unit.source, extractor.occurrences.toList, extractor.symbolInfos.toList, extractor.synthetics.toList) else - unit.tpdTree.getAttachment(_key) match - case None => - case Some(doc) => - val warnings = ctx.reporter.allWarnings.collect { - case w if w.pos.source == ctx.source => w.toSemanticDiagnostic - } - ExtractSemanticDB.write(unit.source, doc.copy(diagnostics = warnings)) + val warnings = ctx.reporter.allWarnings.collect { + case w if w.pos.source == ctx.source => w.toSemanticDiagnostic + } + if (warnings.nonEmpty) + ExtractSemanticDB.appendDiagnostics(unit.source, warnings) end ExtractSemanticDB object ExtractSemanticDB: @@ -88,15 +85,9 @@ object ExtractSemanticDB: case PostTyper case PostInlining - /** - * The key used to retrieve the "unused entity" analysis metadata, - * from the compilation `Context` - */ - private val _key = Property.StickyKey[TextDocument] + class PostTyper extends ExtractSemanticDB(PhaseMode.PostTyper, "PostTyper") - class PostTyper extends ExtractSemanticDB(PhaseMode.PostTyper, "PostTyper", _key) - - class PostInlining extends ExtractSemanticDB(PhaseMode.PostInlining, "PostInlining", _key) + class PostInlining extends ExtractSemanticDB(PhaseMode.PostInlining, "PostInlining") private def semanticdbTarget(using Context): Option[Path] = Option(ctx.settings.semanticdbTarget.value) @@ -109,15 +100,22 @@ object ExtractSemanticDB: private def write( source: SourceFile, - doc: TextDocument + occurrences: List[SymbolOccurrence], + symbolInfos: List[SymbolInformation], + synthetics: List[Synthetic], )(using Context): Unit = - val relPath = SourceFile.relativePath(source, ctx.settings.sourceroot.value) - val outpath = absolutePath(semanticdbTarget.getOrElse(outputDirectory.jpath)) - .resolve("META-INF") - .resolve("semanticdb") - .resolve(relPath) - .resolveSibling(source.name + ".semanticdb") + val outpath = semanticdbPath(source) Files.createDirectories(outpath.getParent()) + val doc: TextDocument = TextDocument( + schema = Schema.SEMANTICDB4, + language = Language.SCALA, + uri = Tools.mkURIstring(Paths.get(relPath(source))), + text = "", + md5 = internal.MD5.compute(String(source.content)), + symbols = symbolInfos, + occurrences = occurrences, + synthetics = synthetics, + ) val docs = TextDocuments(List(doc)) val out = Files.newOutputStream(outpath) try @@ -128,6 +126,34 @@ object ExtractSemanticDB: out.close() end write + private def appendDiagnostics( + source: SourceFile, + diagnostics: Seq[Diagnostic] + )(using Context): Unit = + val path = semanticdbPath(source) + Using.Manager { use => + val in = use(Files.newInputStream(path)) + val sin = internal.SemanticdbInputStream.newInstance(in) + val docs = TextDocuments.parseFrom(sin) + + val out = use(Files.newOutputStream(path)) + val sout = internal.SemanticdbOutputStream.newInstance(out) + TextDocuments(docs.documents.map(_.withDiagnostics(diagnostics))).writeTo(sout) + sout.flush() + } match + case Failure(ex) => // failed somehow, should we say something? + case Success(_) => // success to update semanticdb, say nothing + end appendDiagnostics + + private def relPath(source: SourceFile)(using ctx: Context) = + SourceFile.relativePath(source, ctx.settings.sourceroot.value) + + private def semanticdbPath(source: SourceFile)(using ctx: Context) = + absolutePath(semanticdbTarget.getOrElse(outputDirectory.jpath)) + .resolve("META-INF") + .resolve("semanticdb") + .resolve(relPath(source)) + .resolveSibling(source.name + ".semanticdb") /** Extractor of symbol occurrences from trees */ private class Extractor extends TreeTraverser: @@ -136,20 +162,6 @@ object ExtractSemanticDB: val synth = SyntheticsExtractor() given converter: s.TypeOps = s.TypeOps() - - def toTextDocument(source: SourceFile)(using Context): TextDocument = - val relPath = SourceFile.relativePath(source, ctx.settings.sourceroot.value) - TextDocument( - schema = Schema.SEMANTICDB4, - language = Language.SCALA, - uri = Tools.mkURIstring(Paths.get(relPath)), - text = "", - md5 = internal.MD5.compute(String(source.content)), - symbols = symbolInfos.toList, - occurrences = occurrences.toList, - synthetics = synthetics.toList, - ) - /** The bodies of synthetic locals */ private val localBodies = mutable.HashMap[Symbol, Tree]()