diff --git a/SwiftCompilerSources/Sources/Parse/Regex.swift b/SwiftCompilerSources/Sources/Parse/Regex.swift index 22d0c3aa2bcf0..0553e755ac47a 100644 --- a/SwiftCompilerSources/Sources/Parse/Regex.swift +++ b/SwiftCompilerSources/Sources/Parse/Regex.swift @@ -15,14 +15,14 @@ import AST import Basic #if canImport(_CompilerRegexParser) -import _CompilerRegexParser +@_spi(CompilerInterface) import _CompilerRegexParser public func registerRegexParser() { Parser_registerRegexLiteralParsingFn(_RegexLiteralParsingFn) Parser_registerRegexLiteralLexingFn(_RegexLiteralLexingFn) } -/// Bridging between C++ lexer and _CompilerRegexParser.lexRegex() +/// Bridging between C++ lexer and swiftCompilerLexRegexLiteral. /// /// Attempt to lex a regex literal string. /// @@ -50,47 +50,29 @@ private func _RegexLiteralLexingFn( ) -> /*CompletelyErroneous*/ CBool { let inputPtr = curPtrPtr.pointee - do { - let (_, _, endPtr) = try lexRegex(start: inputPtr, end: bufferEndPtr) - curPtrPtr.pointee = endPtr.assumingMemoryBound(to: CChar.self) + guard let (resumePtr, error) = swiftCompilerLexRegexLiteral( + start: inputPtr, bufferEnd: bufferEndPtr, mustBeRegex: mustBeRegex + ) else { + // Not a regex literal, fallback without advancing the pointer. return false - } catch let error as DelimiterLexError { - if !mustBeRegex { - // This token can be something else. Let the client fallback. - return false; - } - if error.kind == .unknownDelimiter { - // An unknown delimiter should be recovered from, as we may want to try - // lex something else. - return false - } + } + // Advance the current pointer. + curPtrPtr.pointee = resumePtr.assumingMemoryBound(to: CChar.self) + + if let error = error { + // Emit diagnostic if diagnostics are enabled. if let diagEngine = DiagnosticEngine(bridged: bridgedDiagnosticEngine) { - // Emit diagnostic. let startLoc = SourceLoc( - locationInFile: UnsafeRawPointer(inputPtr).assumingMemoryBound(to: UInt8.self))! - diagEngine.diagnose(startLoc, .regex_literal_parsing_error, "\(error)") - } - - // Advance the current pointer. - curPtrPtr.pointee = error.resumePtr.assumingMemoryBound(to: CChar.self) - - switch error.kind { - case .unterminated, .multilineClosingNotOnNewline: - // These can be recovered from. - return false - case .unprintableASCII, .invalidUTF8: - // We don't currently have good recovery behavior for these. - return true - case .unknownDelimiter: - fatalError("Already handled") + locationInFile: error.location.assumingMemoryBound(to: UInt8.self))! + diagEngine.diagnose(startLoc, .regex_literal_parsing_error, error.message) } - } catch { - fatalError("Should be a DelimiterLexError") + return error.completelyErroneous } + return false } -/// Bridging between C++ parser and _CompilerRegexParser.parseWithDelimiters() +/// Bridging between C++ parser and swiftCompilerParseRegexLiteral. /// /// - Parameters: /// - inputPtr: A null-terminated C string. @@ -103,6 +85,8 @@ private func _RegexLiteralLexingFn( /// greater than or equal to `strlen(inputPtr)`. /// - bridgedDiagnosticBaseLoc: Source location of the start of the literal /// - bridgedDiagnosticEngine: Diagnostic engine to emit diagnostics. +/// +/// - Returns: `true` if there was a parse error, `false` otherwise. public func _RegexLiteralParsingFn( _ inputPtr: UnsafePointer, _ versionOut: UnsafeMutablePointer, @@ -111,30 +95,27 @@ public func _RegexLiteralParsingFn( _ bridgedDiagnosticBaseLoc: BridgedSourceLoc, _ bridgedDiagnosticEngine: BridgedDiagnosticEngine ) -> Bool { - versionOut.pointee = currentRegexLiteralFormatVersion - let str = String(cString: inputPtr) + let captureBuffer = UnsafeMutableRawBufferPointer( + start: captureStructureOut, count: Int(captureStructureSize)) do { - let ast = try parseWithDelimiters(str) - // Serialize the capture structure for later type inference. - assert(captureStructureSize >= str.utf8.count) - let buffer = UnsafeMutableRawBufferPointer( - start: captureStructureOut, count: Int(captureStructureSize)) - ast.captureStructure.encode(to: buffer) - return false; - } catch { + // FIXME: We need to plumb through the 'regexToEmit' result to the caller. + // For now, it is the same as the input. + let (_, version) = try swiftCompilerParseRegexLiteral( + str, captureBufferOut: captureBuffer) + versionOut.pointee = CUnsignedInt(version) + return false + } catch let error as CompilerParseError { var diagLoc = SourceLoc(bridged: bridgedDiagnosticBaseLoc) let diagEngine = DiagnosticEngine(bridged: bridgedDiagnosticEngine) - if let _diagLoc = diagLoc, - let locatedError = error as? LocatedErrorProtocol { - let offset = str.utf8.distance(from: str.startIndex, - to: locatedError.location.start) + if let _diagLoc = diagLoc, let errorLoc = error.location { + let offset = str.utf8.distance(from: str.startIndex, to: errorLoc) diagLoc = _diagLoc.advanced(by: offset) } - diagEngine.diagnose( - diagLoc, .regex_literal_parsing_error, - "cannot parse regular expression: \(String(describing: error))") + diagEngine.diagnose(diagLoc, .regex_literal_parsing_error, error.message) return true + } catch { + fatalError("Expected CompilerParseError") } } diff --git a/lib/Parse/ParseRegex.cpp b/lib/Parse/ParseRegex.cpp index d0d3e3eae944c..015e213b6e42a 100644 --- a/lib/Parse/ParseRegex.cpp +++ b/lib/Parse/ParseRegex.cpp @@ -43,7 +43,7 @@ ParserResult Parser::parseExprRegexLiteral() { // Let the Swift library parse the contents, returning an error, or null if // successful. - unsigned version; + unsigned version = 0; auto capturesBuf = Context.AllocateUninitialized( RegexLiteralExpr::getCaptureStructureSerializationAllocationSize( regexText.size())); @@ -57,6 +57,7 @@ ParserResult Parser::parseExprRegexLiteral() { if (hadError) { return makeParserResult(new (Context) ErrorExpr(loc)); } + assert(version >= 1); return makeParserResult(RegexLiteralExpr::createParsed( Context, loc, regexText, version, capturesBuf)); } diff --git a/test/StringProcessing/Parse/forward-slash-regex.swift b/test/StringProcessing/Parse/forward-slash-regex.swift index 26d0a3040829c..c7e36d97d8353 100644 --- a/test/StringProcessing/Parse/forward-slash-regex.swift +++ b/test/StringProcessing/Parse/forward-slash-regex.swift @@ -169,8 +169,6 @@ _ = 0 . / 1 / 2 // expected-error {{expected member name following '.'}} switch "" { case /x/: - // expected-error@-1 {{expression pattern of type 'Regex' cannot match values of type 'String'}} - // expected-note@-2 {{overloads for '~=' exist with these partially matching parameter lists: (Substring, String)}} break case _ where /x /: // expected-error@-1 {{cannot convert value of type 'Regex' to expected condition type 'Bool'}}