@@ -222,15 +222,14 @@ public final class SwiftToSkeleton {
222222 validatedJavaScriptModulePaths . insert ( path )
223223 }
224224
225- let exportErrors = exportCollector . errors . filter { $0 . severity == . error }
226225 let importErrorsFatal = importCollector . errors . filter {
227226 $0 . severity == . error && ! $0 . message . contains ( " Unsupported type ' " )
228227 }
229- let fileWarnings = ( exportCollector . errors + importCollector . errors ) . filter { $0 . severity == . warning }
228+ let fileWarnings = importCollector . errors . filter { $0 . severity == . warning }
230229 warnings . append ( contentsOf : fileWarnings . map { ( file : inputFilePath , diagnostic : $0 ) } )
231- if ! exportErrors . isEmpty || ! importErrorsFatal . isEmpty {
230+ if ! importErrorsFatal . isEmpty {
232231 perSourceErrors . append (
233- ( inputFilePath : inputFilePath , errors : exportErrors + importErrorsFatal )
232+ ( inputFilePath : inputFilePath , errors : importErrorsFatal )
234233 )
235234 }
236235
@@ -249,6 +248,18 @@ public final class SwiftToSkeleton {
249248 source . resolveDeferredExtensions ( against : exportCollectors )
250249 }
251250
251+ // We have to collect diagnostics after all deferred extensions are resolved, since they could generate some.
252+ for ( ( _, inputFilePath ) , exportCollector ) in zip ( sourceFiles , exportCollectors ) {
253+ let exportErrors = exportCollector . errors . filter { $0 . severity == . error }
254+ let fileWarnings = exportCollector . errors . filter { $0 . severity == . warning }
255+ warnings . append ( contentsOf : fileWarnings . map { ( file : inputFilePath , diagnostic : $0 ) } )
256+ if ! exportErrors . isEmpty {
257+ perSourceErrors . append (
258+ ( inputFilePath : inputFilePath , errors : exportErrors )
259+ )
260+ }
261+ }
262+
252263 for collector in exportCollectors {
253264 collector . finalize ( & exported )
254265 }
@@ -858,6 +869,17 @@ extension AttributeListSyntax {
858869 }
859870}
860871
872+ private final class JSAttributeFinder : SyntaxVisitor {
873+ private( set) var found = false
874+
875+ override func visit ( _ node : AttributeSyntax ) -> SyntaxVisitorContinueKind {
876+ if node . attributeNameText == " JS " {
877+ found = true
878+ }
879+ return . skipChildren
880+ }
881+ }
882+
861883private final class ExportSwiftAPICollector : SyntaxAnyVisitor {
862884 var exportedFunctions : [ ExportedFunction ] = [ ]
863885 /// The names of the exported classes, in the order they were written in the source file
@@ -1910,7 +1932,7 @@ private final class ExportSwiftAPICollector: SyntaxAnyVisitor {
19101932 break
19111933 }
19121934 }
1913- if ! resolved {
1935+ if ! resolved , containsJSAnnotatedDeclaration ( ext . memberBlock . members ) {
19141936 diagnose (
19151937 node : ext . extendedType ,
19161938 message : " Unsupported type ' \( ext . extendedType . trimmedDescription ) '. " ,
@@ -1920,6 +1942,12 @@ private final class ExportSwiftAPICollector: SyntaxAnyVisitor {
19201942 }
19211943 }
19221944
1945+ private func containsJSAnnotatedDeclaration ( _ members : MemberBlockItemListSyntax ) -> Bool {
1946+ let finder = JSAttributeFinder ( viewMode : . sourceAccurate )
1947+ finder . walk ( members )
1948+ return finder . found
1949+ }
1950+
19231951 /// Walks extension members under the matching type’s state, returning whether the type was found.
19241952 ///
19251953 /// Note: The lookup scans dictionaries keyed by `makeKey(name:namespace:)`, matching only by
0 commit comments