From 6f6b8dd071b2a01ab3c8558c8abe63c2e21c09a1 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 3 Mar 2025 22:49:33 -0800 Subject: [PATCH 1/3] [Diagnostics] Provide category name + documentation path Bridge the category name and educational note computed for the Swift compiler's diagnostics to the newly-introduced category field for the swift-syntax diagnostics. This lets the swift-syntax renderer introduce the category name and (optionally) link to the documentation. --- include/swift/Bridging/ASTGen.h | 3 ++ lib/AST/DiagnosticBridge.cpp | 8 +++++ .../Sources/ASTGen/DiagnosticsBridge.swift | 30 ++++++++++++++++++- 3 files changed, 40 insertions(+), 1 deletion(-) diff --git a/include/swift/Bridging/ASTGen.h b/include/swift/Bridging/ASTGen.h index 37ea1e6a97e30..110cb4aba0b8b 100644 --- a/include/swift/Bridging/ASTGen.h +++ b/include/swift/Bridging/ASTGen.h @@ -28,6 +28,9 @@ void swift_ASTGen_addQueuedSourceFile( void swift_ASTGen_addQueuedDiagnostic( void *_Nonnull queued, const char *_Nonnull text, ptrdiff_t textLength, BridgedDiagnosticSeverity severity, const void *_Nullable sourceLoc, + const char *_Nullable categoryName, ptrdiff_t categoryNameLength, + const char *_Nullable documentationPath, + ptrdiff_t documentationPathLength, const void *_Nullable *_Nullable highlightRanges, ptrdiff_t numHighlightRanges); void swift_ASTGen_renderQueuedDiagnostics( diff --git a/lib/AST/DiagnosticBridge.cpp b/lib/AST/DiagnosticBridge.cpp index 58b6234fd3b40..f1f650aaf190a 100644 --- a/lib/AST/DiagnosticBridge.cpp +++ b/lib/AST/DiagnosticBridge.cpp @@ -64,9 +64,17 @@ static void addQueueDiagnostic(void *queuedDiagnostics, highlightRanges.push_back(range.getEnd().getOpaquePointerValue()); } + StringRef documentationPath; + if (info.EducationalNotePaths.size() > 0) + documentationPath = info.EducationalNotePaths[0]; + // FIXME: Translate Fix-Its. swift_ASTGen_addQueuedDiagnostic(queuedDiagnostics, text.data(), text.size(), severity, info.Loc.getOpaquePointerValue(), + info.Category.data(), + info.Category.size(), + documentationPath.data(), + documentationPath.size(), highlightRanges.data(), highlightRanges.size() / 2); } diff --git a/lib/ASTGen/Sources/ASTGen/DiagnosticsBridge.swift b/lib/ASTGen/Sources/ASTGen/DiagnosticsBridge.swift index ff7a953a59e0f..8e11dbf744fcc 100644 --- a/lib/ASTGen/Sources/ASTGen/DiagnosticsBridge.swift +++ b/lib/ASTGen/Sources/ASTGen/DiagnosticsBridge.swift @@ -165,6 +165,8 @@ fileprivate struct SimpleDiagnostic: DiagnosticMessage { let severity: DiagnosticSeverity + let category: DiagnosticCategory? + var diagnosticID: MessageID { .init(domain: "SwiftCompiler", id: "SimpleDiagnostic") } @@ -237,6 +239,10 @@ public func addQueuedDiagnostic( textLength: Int, severity: BridgedDiagnosticSeverity, cLoc: BridgedSourceLoc, + categoryName: UnsafePointer?, + categoryLength: Int, + documentationPath: UnsafePointer?, + documentationPathLength: Int, highlightRangesPtr: UnsafePointer?, numHighlightRanges: Int ) { @@ -333,13 +339,35 @@ public func addQueuedDiagnostic( } } + let category: DiagnosticCategory? = categoryName.map { categoryNamePtr in + let categoryNameBuffer = UnsafeBufferPointer( + start: categoryNamePtr, + count: categoryLength + ) + let categoryName = String(decoding: categoryNameBuffer, as: UTF8.self) + + let documentationPath = documentationPath.map { documentationPathPtr in + let documentationPathBuffer = UnsafeBufferPointer( + start: documentationPathPtr, + count: documentationPathLength + ) + return String(decoding: documentationPathBuffer, as: UTF8.self) + } + + return DiagnosticCategory( + name: categoryName, + documentationPath: documentationPath + ) + } + let textBuffer = UnsafeBufferPointer(start: text, count: textLength) let diagnostic = Diagnostic( node: node, position: position, message: SimpleDiagnostic( message: String(decoding: textBuffer, as: UTF8.self), - severity: severity.asSeverity + severity: severity.asSeverity, + category: category ), highlights: highlights ) From db2fa7bcdab6facc0e8f44f06dfbaa4bbd3cf3d0 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 4 Mar 2025 09:14:48 -0800 Subject: [PATCH 2/3] [Diagnostics] Use the educational note name for the category name --- lib/AST/DiagnosticEngine.cpp | 5 +++++ test/diagnostics/educational_notes_serialization.swift | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/AST/DiagnosticEngine.cpp b/lib/AST/DiagnosticEngine.cpp index 55532fd9c67df..effe0fd7614d8 100644 --- a/lib/AST/DiagnosticEngine.cpp +++ b/lib/AST/DiagnosticEngine.cpp @@ -1337,6 +1337,7 @@ DiagnosticEngine::diagnosticInfoForDiagnostic(const Diagnostic &diagnostic, auto groupID = diagnostic.getGroupID(); StringRef Category; + const char * const *associatedNotes = nullptr; if (isAPIDigesterBreakageDiagnostic(diagnostic.getID())) Category = "api-digester-breaking-change"; else if (isNoUsageDiagnostic(diagnostic.getID())) @@ -1345,6 +1346,10 @@ DiagnosticEngine::diagnosticInfoForDiagnostic(const Diagnostic &diagnostic, Category = getDiagGroupInfoByID(groupID).name; else if (isDeprecationDiagnostic(diagnostic.getID())) Category = "deprecation"; + else if ((associatedNotes = educationalNotes[(uint32_t)diagnostic.getID()]) && + *associatedNotes) { + Category = llvm::sys::path::stem(*associatedNotes); + } auto fixIts = diagnostic.getFixIts(); if (loc.isValid()) { diff --git a/test/diagnostics/educational_notes_serialization.swift b/test/diagnostics/educational_notes_serialization.swift index 0e36c54622c5f..84797c7daf04d 100644 --- a/test/diagnostics/educational_notes_serialization.swift +++ b/test/diagnostics/educational_notes_serialization.swift @@ -7,13 +7,13 @@ typealias Fn = () -> () extension Fn {} -// CHECK: [[@LINE-1]]:1: error: non-nominal type 'Fn' (aka '() -> ()') cannot be extended [{{.*}}nominal-types.md] [] +// CHECK: [[@LINE-1]]:1: error: non-nominal type 'Fn' (aka '() -> ()') cannot be extended [{{.*}}nominal-types.md] [nominal-types] // Shares the flag record with `Fn` typealias Dup = () -> () extension Dup {} -// CHECK: [[@LINE-1]]:1: error: non-nominal type 'Dup' (aka '() -> ()') cannot be extended [{{.*}}nominal-types.md] [] +// CHECK: [[@LINE-1]]:1: error: non-nominal type 'Dup' (aka '() -> ()') cannot be extended [{{.*}}nominal-types.md] [nominal-types] do { func noNote(_: Int) {} From 398e1ad5809b1a1a46539877d190e64159d099b7 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 4 Mar 2025 09:44:37 -0800 Subject: [PATCH 3/3] [Diagnostics] Always provide a URL for the category documentation --- .../Sources/ASTGen/DiagnosticsBridge.swift | 41 +++++++++++++++++-- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/lib/ASTGen/Sources/ASTGen/DiagnosticsBridge.swift b/lib/ASTGen/Sources/ASTGen/DiagnosticsBridge.swift index 8e11dbf744fcc..0a911139e0064 100644 --- a/lib/ASTGen/Sources/ASTGen/DiagnosticsBridge.swift +++ b/lib/ASTGen/Sources/ASTGen/DiagnosticsBridge.swift @@ -346,17 +346,25 @@ public func addQueuedDiagnostic( ) let categoryName = String(decoding: categoryNameBuffer, as: UTF8.self) - let documentationPath = documentationPath.map { documentationPathPtr in + let documentationURL = documentationPath.map { documentationPathPtr in let documentationPathBuffer = UnsafeBufferPointer( start: documentationPathPtr, count: documentationPathLength ) - return String(decoding: documentationPathBuffer, as: UTF8.self) + + let documentationPath = String(decoding: documentationPathBuffer, as: UTF8.self) + + // If this looks doesn't look like a URL, prepend file://. + if !documentationPath.looksLikeURL { + return "file://\(documentationPath)" + } + + return documentationPath } return DiagnosticCategory( name: categoryName, - documentationPath: documentationPath + documentationURL: documentationURL ) } @@ -389,3 +397,30 @@ public func renderQueuedDiagnostics( renderedStringOutPtr.pointee = allocateBridgedString(renderedStr) } + +extension String { + /// Simple check to determine whether the string looks like the start of a + /// URL. + fileprivate var looksLikeURL: Bool { + var forwardSlashes: Int = 0 + for c in self { + if c == "/" { + forwardSlashes += 1 + if forwardSlashes > 2 { + return true + } + + continue + } + + if c.isLetter || c.isNumber { + forwardSlashes = 0 + continue + } + + return false + } + + return false + } +}