@@ -27,6 +27,7 @@ import dotty.tools.dotc.{semanticdb => s}
27
27
import dotty .tools .io .{AbstractFile , JarArchive }
28
28
import dotty .tools .dotc .util .Property
29
29
import dotty .tools .dotc .semanticdb .DiagnosticOps .*
30
+ import scala .util .{Using , Failure , Success }
30
31
31
32
32
33
/** Extract symbol references and uses to semanticdb files.
@@ -36,17 +37,15 @@ import dotty.tools.dotc.semanticdb.DiagnosticOps.*
36
37
* Here, we define two phases for "ExtractSemanticDB", "PostTyper" and "PostInlining".
37
38
*
38
39
* 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.
42
42
*
43
43
* The "PostInlining" phase extracts diagnostics from "ctx.reporter" and
44
44
* attaches them to the SemanticDB information extracted in the "PostTyper" phase.
45
- * Afterwards, it writes the SemanticDB to a ".semanticdb" file.
46
45
* We need to run this phase after the "CheckUnused.PostInlining" phase
47
46
* so that we can extract the warnings generated by "-Wunused".
48
47
*/
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 :
50
49
51
50
override val phaseName : String = ExtractSemanticDB .phaseNamePrefix + suffix
52
51
@@ -65,15 +64,13 @@ class ExtractSemanticDB private (phaseMode: ExtractSemanticDB.PhaseMode, suffix:
65
64
if (phaseMode == ExtractSemanticDB .PhaseMode .PostTyper )
66
65
val extractor = ExtractSemanticDB .Extractor ()
67
66
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 )
69
68
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)
77
74
end ExtractSemanticDB
78
75
79
76
object ExtractSemanticDB :
@@ -88,15 +85,9 @@ object ExtractSemanticDB:
88
85
case PostTyper
89
86
case PostInlining
90
87
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" )
96
89
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" )
100
91
101
92
private def semanticdbTarget (using Context ): Option [Path ] =
102
93
Option (ctx.settings.semanticdbTarget.value)
@@ -109,15 +100,22 @@ object ExtractSemanticDB:
109
100
110
101
private def write (
111
102
source : SourceFile ,
112
- doc : TextDocument
103
+ occurrences : List [SymbolOccurrence ],
104
+ symbolInfos : List [SymbolInformation ],
105
+ synthetics : List [Synthetic ],
113
106
)(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)
120
108
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
+ )
121
119
val docs = TextDocuments (List (doc))
122
120
val out = Files .newOutputStream(outpath)
123
121
try
@@ -128,6 +126,34 @@ object ExtractSemanticDB:
128
126
out.close()
129
127
end write
130
128
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" )
131
157
132
158
/** Extractor of symbol occurrences from trees */
133
159
private class Extractor extends TreeTraverser :
@@ -136,20 +162,6 @@ object ExtractSemanticDB:
136
162
val synth = SyntheticsExtractor ()
137
163
given converter : s.TypeOps = s.TypeOps ()
138
164
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
-
153
165
/** The bodies of synthetic locals */
154
166
private val localBodies = mutable.HashMap [Symbol , Tree ]()
155
167
0 commit comments