diff --git a/include/swift/AST/DiagnosticBridge.h b/include/swift/AST/DiagnosticBridge.h index b110f530e9b6b..5c3f07c806080 100644 --- a/include/swift/AST/DiagnosticBridge.h +++ b/include/swift/AST/DiagnosticBridge.h @@ -22,6 +22,7 @@ #include "swift/Basic/SourceManager.h" #include "llvm/ADT/DenseMap.h" #include "llvm/Support/raw_ostream.h" +#include namespace swift { /// Declare the bridge between swift-syntax and swift-frontend for diagnostics @@ -44,7 +45,7 @@ class DiagnosticBridge { public: /// Enqueue diagnostics. void enqueueDiagnostic(SourceManager &SM, const DiagnosticInfo &Info, - unsigned innermostBufferID); + std::optional innermostBufferID); /// Flush all enqueued diagnostics. void flush(llvm::raw_ostream &OS, bool includeTrailingBreak, diff --git a/lib/AST/DiagnosticBridge.cpp b/lib/AST/DiagnosticBridge.cpp index 859acbb22c2d6..39ca86351042f 100644 --- a/lib/AST/DiagnosticBridge.cpp +++ b/lib/AST/DiagnosticBridge.cpp @@ -87,9 +87,10 @@ static void addQueueDiagnostic(void *queuedDiagnostics, } } -void DiagnosticBridge::enqueueDiagnostic(SourceManager &SM, - const DiagnosticInfo &Info, - unsigned innermostBufferID) { +void DiagnosticBridge::enqueueDiagnostic( + SourceManager &SM, + const DiagnosticInfo &Info, + std::optional innermostBufferID) { // If we didn't have per-frontend state before, create it now. if (!perFrontendState) { perFrontendState = swift_ASTGen_createPerFrontendDiagnosticState(); @@ -100,7 +101,8 @@ void DiagnosticBridge::enqueueDiagnostic(SourceManager &SM, if (!queuedDiagnostics) queuedDiagnostics = swift_ASTGen_createQueuedDiagnostics(); - queueBuffer(SM, innermostBufferID); + if (innermostBufferID) + queueBuffer(SM, *innermostBufferID); addQueueDiagnostic(queuedDiagnostics, perFrontendState, Info, SM); } diff --git a/lib/ASTGen/Sources/ASTGen/DiagnosticsBridge.swift b/lib/ASTGen/Sources/ASTGen/DiagnosticsBridge.swift index 6d733229c2e1a..73319094e5088 100644 --- a/lib/ASTGen/Sources/ASTGen/DiagnosticsBridge.swift +++ b/lib/ASTGen/Sources/ASTGen/DiagnosticsBridge.swift @@ -25,7 +25,7 @@ fileprivate func emitDiagnosticParts( sourceFileBuffer: UnsafeBufferPointer, message: String, severity: DiagnosticSeverity, - position: AbsolutePosition, + position: AbsolutePosition?, offset: Int, highlights: [Syntax] = [], edits: [SourceEdit] = [] @@ -33,7 +33,11 @@ fileprivate func emitDiagnosticParts( // Map severity let bridgedSeverity = severity.bridged - func bridgedSourceLoc(at position: AbsolutePosition) -> BridgedSourceLoc { + func bridgedSourceLoc(at position: AbsolutePosition?) -> BridgedSourceLoc { + guard let position else { + return nil + } + return BridgedSourceLoc(at: position.advanced(by: offset), in: sourceFileBuffer) } @@ -266,95 +270,125 @@ public func addQueuedDiagnostic( to: PerFrontendDiagnosticState.self ) - guard let rawPosition = cLoc.getOpaquePointerValue() else { - return - } + let rawPosition = cLoc.getOpaquePointerValue() + var sourceFile: ExportedSourceFile? = nil + if let rawPosition { + sourceFile = queuedDiagnostics.pointee.sourceFiles.first { sf in + guard let baseAddress = sf.buffer.baseAddress else { + return false + } - // Find the source file that contains this location. - let sourceFile = queuedDiagnostics.pointee.sourceFiles.first { sf in - guard let baseAddress = sf.buffer.baseAddress else { - return false + return rawPosition >= baseAddress && rawPosition <= baseAddress + sf.buffer.count } - - return rawPosition >= baseAddress && rawPosition <= baseAddress + sf.buffer.count - } - guard let sourceFile = sourceFile else { - // FIXME: Hard to report an error here... - return } - let sourceFileBaseAddress = UnsafeRawPointer(sourceFile.buffer.baseAddress!) - let sourceFileEndAddress = sourceFileBaseAddress + sourceFile.buffer.count - let offset = rawPosition - sourceFileBaseAddress - let position = AbsolutePosition(utf8Offset: offset) - - // Find the token at that offset. - let node: Syntax - if let token = sourceFile.syntax.token(at: position) { - node = Syntax(token) - } else if position == sourceFile.syntax.endPosition { - // FIXME: EOF token is not included in '.token(at: position)' - // We might want to include it, but want to avoid special handling. - // Also 'sourceFile.syntax' is not guaranteed to be 'SourceFileSyntax'. - if let token = sourceFile.syntax.lastToken(viewMode: .all) { + let node: Syntax? + let position: AbsolutePosition? + var highlights: [Syntax] = [] + let fixIts: [FixIt] + if let sourceFile, let rawPosition { + let sourceFileBaseAddress = UnsafeRawPointer(sourceFile.buffer.baseAddress!) + let sourceFileEndAddress = sourceFileBaseAddress + sourceFile.buffer.count + let offset = rawPosition - sourceFileBaseAddress + let myPosition = AbsolutePosition(utf8Offset: offset) + position = myPosition + + // Find the token at that offset. + if let token = sourceFile.syntax.token(at: myPosition) { node = Syntax(token) + } else if myPosition == sourceFile.syntax.endPosition { + // FIXME: EOF token is not included in '.token(at: position)' + // We might want to include it, but want to avoid special handling. + // Also 'sourceFile.syntax' is not guaranteed to be 'SourceFileSyntax'. + if let token = sourceFile.syntax.lastToken(viewMode: .all) { + node = Syntax(token) + } else { + node = sourceFile.syntax + } } else { - node = sourceFile.syntax + node = nil } - } else { - // position out of range. - return - } - // Map the highlights. - var highlights: [Syntax] = [] - let highlightRanges = UnsafeBufferPointer( - start: highlightRangesPtr, - count: numHighlightRanges - ) - for index in 0..( + start: highlightRangesPtr, + count: numHighlightRanges + ) + for index in 0..= sourceFileBaseAddress && start < sourceFileEndAddress, - end >= sourceFileBaseAddress && end <= sourceFileEndAddress - else { - continue - } + // Make sure both the start and the end land within this source file. + guard let start = range.start.getOpaquePointerValue(), + let end = range.start.advanced(by: range.byteLength).getOpaquePointerValue() + else { + continue + } - // Find start tokens in the source file. - let startPos = AbsolutePosition(utf8Offset: start - sourceFileBaseAddress) - guard let startToken = sourceFile.syntax.token(at: startPos) else { - continue - } + guard start >= sourceFileBaseAddress && start < sourceFileEndAddress, + end >= sourceFileBaseAddress && end <= sourceFileEndAddress + else { + continue + } - // Walk up from the start token until we find a syntax node that matches - // the highlight range. - let endPos = AbsolutePosition(utf8Offset: end - sourceFileBaseAddress) - var highlightSyntax = Syntax(startToken) - while true { - // If this syntax matches our starting/ending positions, add the - // highlight and we're done. - if highlightSyntax.positionAfterSkippingLeadingTrivia == startPos - && highlightSyntax.endPositionBeforeTrailingTrivia == endPos - { - highlights.append(highlightSyntax) - break + // Find start tokens in the source file. + let startPos = AbsolutePosition(utf8Offset: start - sourceFileBaseAddress) + guard let startToken = sourceFile.syntax.token(at: startPos) else { + continue } - // Go up to the parent. - guard let parent = highlightSyntax.parent else { - break + // Walk up from the start token until we find a syntax node that matches + // the highlight range. + let endPos = AbsolutePosition(utf8Offset: end - sourceFileBaseAddress) + var highlightSyntax = Syntax(startToken) + while true { + // If this syntax matches our starting/ending positions, add the + // highlight and we're done. + if highlightSyntax.positionAfterSkippingLeadingTrivia == startPos + && highlightSyntax.endPositionBeforeTrailingTrivia == endPos + { + highlights.append(highlightSyntax) + break + } + + // Go up to the parent. + guard let parent = highlightSyntax.parent else { + break + } + + highlightSyntax = parent } + } - highlightSyntax = parent + // Map the Fix-Its + let fixItChanges: [FixIt.Change] = fixItsUntyped.withElements(ofType: BridgedFixIt.self) { fixIts in + fixIts.compactMap { fixIt in + guard let startPos = sourceFile.position(of: fixIt.replacementRange.start), + let endPos = sourceFile.position( + of: fixIt.replacementRange.start.advanced( + by: fixIt.replacementRange.byteLength)) else { + return nil + } + + return FixIt.Change.replaceText( + range: startPos..( - for node: Node, + for node: Node?, at position: AbsolutePosition? = nil ) -> BridgedSourceLoc { + guard let node else { + return nil + } + // Find the source file and this node's position within it. let (rootNode, rootPosition) = rootSyntax(of: node) @@ -124,8 +128,8 @@ extension SourceManager { private func diagnoseSingle( message: String, severity: DiagnosticSeverity, - node: Node, - position: AbsolutePosition, + node: Node?, + position: AbsolutePosition?, highlights: [Syntax] = [], fixItChanges: [FixIt.Change] = [] ) { diff --git a/lib/Frontend/PrintingDiagnosticConsumer.cpp b/lib/Frontend/PrintingDiagnosticConsumer.cpp index 00806723d8c34..8830d5c8e466c 100644 --- a/lib/Frontend/PrintingDiagnosticConsumer.cpp +++ b/lib/Frontend/PrintingDiagnosticConsumer.cpp @@ -47,21 +47,31 @@ void PrintingDiagnosticConsumer::handleDiagnostic(SourceManager &SM, case DiagnosticOptions::FormattingStyle::Swift: { #if SWIFT_BUILD_SWIFT_SYNTAX // Use the swift-syntax formatter. + if (Info.Kind != DiagnosticKind::Note) { + DiagBridge.flush(Stream, /*includeTrailingBreak=*/true, + /*forceColors=*/ForceColors); + } + auto bufferStack = DiagnosticBridge::getSourceBufferStack(SM, Info.Loc); + std::optional innermostBufferID; if (!bufferStack.empty()) { - if (Info.Kind != DiagnosticKind::Note) - DiagBridge.flush(Stream, /*includeTrailingBreak=*/true, - /*forceColors=*/ForceColors); + innermostBufferID = bufferStack.front(); + } + + DiagBridge.enqueueDiagnostic(SM, Info, innermostBufferID); - DiagBridge.enqueueDiagnostic(SM, Info, bufferStack.front()); - break; + // If there was no source-location information, immediately flush the + // enqueued diagnostic. It won't ever be grouped with anything. + if (bufferStack.empty()) { + DiagBridge.flush(Stream, /*includeTrailingBreak=*/true, + /*forceColors=*/ForceColors); } -#endif + break; +#else // Fall through when we don't have the new diagnostics renderer available. - // This also happens if the location of the diagnostic is invalid, because - // the new rendered cannot cope with that. LLVM_FALLTHROUGH; +#endif } case DiagnosticOptions::FormattingStyle::LLVM: diff --git a/test/Unsafe/unsafe_command_line.swift b/test/Unsafe/unsafe_command_line.swift index fdb9d2651db76..f76e850d8dd88 100644 --- a/test/Unsafe/unsafe_command_line.swift +++ b/test/Unsafe/unsafe_command_line.swift @@ -1,4 +1,4 @@ // RUN: %target-swift-frontend -typecheck -strict-memory-safety -Ounchecked -disable-access-control %s 2>&1 | %FileCheck %s // CHECK: warning: '-Ounchecked' is not memory-safe -// CHECK: warning: '-disable-access-control' is not memory-safe +// CHECK: warning: '-disable-access-control' is not memory-safe and should not be combined with strict memory safety checking [#StrictMemorySafety]