@@ -27,6 +27,7 @@ import dotty.tools.dotc.{semanticdb => s}
2727import dotty .tools .io .{AbstractFile , JarArchive }
2828import dotty .tools .dotc .util .Property
2929import dotty .tools .dotc .semanticdb .DiagnosticOps .*
30+ import scala .util .{Using , Failure , Success }
3031
3132
3233/** Extract symbol references and uses to semanticdb files.
@@ -36,17 +37,15 @@ import dotty.tools.dotc.semanticdb.DiagnosticOps.*
3637 * Here, we define two phases for "ExtractSemanticDB", "PostTyper" and "PostInlining".
3738 *
3839 * The "PostTyper" phase extracts SemanticDB information such as symbol
39- * definitions, symbol occurrences, type information, and synthetics.
40- * This phase does not write the information to a .semanticdb file;
41- * instead, it attaches the SemanticDB information to the top-level tree.
40+ * definitions, symbol occurrences, type information, and synthetics
41+ * and write .semanticdb file.
4242 *
4343 * The "PostInlining" phase extracts diagnostics from "ctx.reporter" and
4444 * attaches them to the SemanticDB information extracted in the "PostTyper" phase.
45- * Afterwards, it writes the SemanticDB to a ".semanticdb" file.
4645 * We need to run this phase after the "CheckUnused.PostInlining" phase
4746 * so that we can extract the warnings generated by "-Wunused".
4847 */
49- class ExtractSemanticDB private (phaseMode : ExtractSemanticDB .PhaseMode , suffix : String , _key : Property . Key [ TextDocument ] ) extends Phase :
48+ class ExtractSemanticDB private (phaseMode : ExtractSemanticDB .PhaseMode , suffix : String ) extends Phase :
5049
5150 override val phaseName : String = ExtractSemanticDB .phaseNamePrefix + suffix
5251
@@ -65,15 +64,13 @@ class ExtractSemanticDB private (phaseMode: ExtractSemanticDB.PhaseMode, suffix:
6564 if (phaseMode == ExtractSemanticDB .PhaseMode .PostTyper )
6665 val extractor = ExtractSemanticDB .Extractor ()
6766 extractor.extract(unit.tpdTree)
68- unit.tpdTree.putAttachment(_key , extractor.toTextDocument(unit.source) )
67+ ExtractSemanticDB .write( unit.source, extractor.occurrences.toList , extractor.symbolInfos.toList, extractor.synthetics.toList )
6968 else
70- unit.tpdTree.getAttachment(_key) match
71- case None =>
72- case Some (doc) =>
73- val warnings = ctx.reporter.allWarnings.collect {
74- case w if w.pos.source == ctx.source => w.toSemanticDiagnostic
75- }
76- ExtractSemanticDB .write(unit.source, doc.copy(diagnostics = warnings))
69+ val warnings = ctx.reporter.allWarnings.collect {
70+ case w if w.pos.source == ctx.source => w.toSemanticDiagnostic
71+ }
72+ if (warnings.nonEmpty)
73+ ExtractSemanticDB .appendDiagnostics(unit.source, warnings)
7774end ExtractSemanticDB
7875
7976object ExtractSemanticDB :
@@ -88,15 +85,9 @@ object ExtractSemanticDB:
8885 case PostTyper
8986 case PostInlining
9087
91- /**
92- * The key used to retrieve the "unused entity" analysis metadata,
93- * from the compilation `Context`
94- */
95- private val _key = Property .StickyKey [TextDocument ]
88+ class PostTyper extends ExtractSemanticDB (PhaseMode .PostTyper , " PostTyper" )
9689
97- class PostTyper extends ExtractSemanticDB (PhaseMode .PostTyper , " PostTyper" , _key)
98-
99- class PostInlining extends ExtractSemanticDB (PhaseMode .PostInlining , " PostInlining" , _key)
90+ class PostInlining extends ExtractSemanticDB (PhaseMode .PostInlining , " PostInlining" )
10091
10192 private def semanticdbTarget (using Context ): Option [Path ] =
10293 Option (ctx.settings.semanticdbTarget.value)
@@ -109,15 +100,22 @@ object ExtractSemanticDB:
109100
110101 private def write (
111102 source : SourceFile ,
112- doc : TextDocument
103+ occurrences : List [SymbolOccurrence ],
104+ symbolInfos : List [SymbolInformation ],
105+ synthetics : List [Synthetic ],
113106 )(using Context ): Unit =
114- val relPath = SourceFile .relativePath(source, ctx.settings.sourceroot.value)
115- val outpath = absolutePath(semanticdbTarget.getOrElse(outputDirectory.jpath))
116- .resolve(" META-INF" )
117- .resolve(" semanticdb" )
118- .resolve(relPath)
119- .resolveSibling(source.name + " .semanticdb" )
107+ val outpath = semanticdbPath(source)
120108 Files .createDirectories(outpath.getParent())
109+ val doc : TextDocument = TextDocument (
110+ schema = Schema .SEMANTICDB4 ,
111+ language = Language .SCALA ,
112+ uri = Tools .mkURIstring(Paths .get(relPath(source))),
113+ text = " " ,
114+ md5 = internal.MD5 .compute(String (source.content)),
115+ symbols = symbolInfos,
116+ occurrences = occurrences,
117+ synthetics = synthetics,
118+ )
121119 val docs = TextDocuments (List (doc))
122120 val out = Files .newOutputStream(outpath)
123121 try
@@ -128,6 +126,34 @@ object ExtractSemanticDB:
128126 out.close()
129127 end write
130128
129+ private def appendDiagnostics (
130+ source : SourceFile ,
131+ diagnostics : Seq [Diagnostic ]
132+ )(using Context ): Unit =
133+ val path = semanticdbPath(source)
134+ Using .Manager { use =>
135+ val in = use(Files .newInputStream(path))
136+ val sin = internal.SemanticdbInputStream .newInstance(in)
137+ val docs = TextDocuments .parseFrom(sin)
138+
139+ val out = use(Files .newOutputStream(path))
140+ val sout = internal.SemanticdbOutputStream .newInstance(out)
141+ TextDocuments (docs.documents.map(_.withDiagnostics(diagnostics))).writeTo(sout)
142+ sout.flush()
143+ } match
144+ case Failure (ex) => // failed somehow, should we say something?
145+ case Success (_) => // success to update semanticdb, say nothing
146+ end appendDiagnostics
147+
148+ private def relPath (source : SourceFile )(using ctx : Context ) =
149+ SourceFile .relativePath(source, ctx.settings.sourceroot.value)
150+
151+ private def semanticdbPath (source : SourceFile )(using ctx : Context ) =
152+ absolutePath(semanticdbTarget.getOrElse(outputDirectory.jpath))
153+ .resolve(" META-INF" )
154+ .resolve(" semanticdb" )
155+ .resolve(relPath(source))
156+ .resolveSibling(source.name + " .semanticdb" )
131157
132158 /** Extractor of symbol occurrences from trees */
133159 private class Extractor extends TreeTraverser :
@@ -136,20 +162,6 @@ object ExtractSemanticDB:
136162 val synth = SyntheticsExtractor ()
137163 given converter : s.TypeOps = s.TypeOps ()
138164
139-
140- def toTextDocument (source : SourceFile )(using Context ): TextDocument =
141- val relPath = SourceFile .relativePath(source, ctx.settings.sourceroot.value)
142- TextDocument (
143- schema = Schema .SEMANTICDB4 ,
144- language = Language .SCALA ,
145- uri = Tools .mkURIstring(Paths .get(relPath)),
146- text = " " ,
147- md5 = internal.MD5 .compute(String (source.content)),
148- symbols = symbolInfos.toList,
149- occurrences = occurrences.toList,
150- synthetics = synthetics.toList,
151- )
152-
153165 /** The bodies of synthetic locals */
154166 private val localBodies = mutable.HashMap [Symbol , Tree ]()
155167
0 commit comments