diff --git a/src/fsharp/CheckComputationExpressions.fs b/src/fsharp/CheckComputationExpressions.fs index a8bd308b7ed..31ca729a399 100644 --- a/src/fsharp/CheckComputationExpressions.fs +++ b/src/fsharp/CheckComputationExpressions.fs @@ -1917,7 +1917,7 @@ let TcArrayOrListSequenceExpression (cenv: cenv) env overallTy tpenv (isArray, c if nelems > 0 && List.forall (function SynExpr.Const (SynConst.UInt16 _, _) -> true | _ -> false) elems then SynExpr.Const (SynConst.UInt16s (Array.ofList (List.map (function SynExpr.Const (SynConst.UInt16 x, _) -> x | _ -> failwith "unreachable") elems)), m) elif nelems > 0 && List.forall (function SynExpr.Const (SynConst.Byte _, _) -> true | _ -> false) elems - then SynExpr.Const (SynConst.Bytes (Array.ofList (List.map (function SynExpr.Const (SynConst.Byte x, _) -> x | _ -> failwith "unreachable") elems), m), m) + then SynExpr.Const (SynConst.Bytes (Array.ofList (List.map (function SynExpr.Const (SynConst.Byte x, _) -> x | _ -> failwith "unreachable") elems), SynByteStringKind.Regular, m), m) else SynExpr.ArrayOrList (isArray, elems, m) else if elems.Length > 500 then diff --git a/src/fsharp/CheckExpressions.fs b/src/fsharp/CheckExpressions.fs index b30ae9cd30c..8be4fa32aa5 100644 --- a/src/fsharp/CheckExpressions.fs +++ b/src/fsharp/CheckExpressions.fs @@ -810,7 +810,7 @@ let TcConst cenv ty m env c = | SynConst.Measure(SynConst.UInt64 i, _) when expandedMeasurablesEnabled -> unifyMeasureArg (i=0UL) cenv.g.puint64_tcr c; Const.UInt64 i | SynConst.Measure(SynConst.UIntPtr i, _) when expandedMeasurablesEnabled -> unifyMeasureArg (i=0UL) cenv.g.punativeint_tcr c; Const.UIntPtr i | SynConst.Char c -> unif cenv.g.char_ty; Const.Char c - | SynConst.String (s, _) -> unif cenv.g.string_ty; Const.String s + | SynConst.String (s, _, _) -> unif cenv.g.string_ty; Const.String s | SynConst.UserNum _ -> error (InternalError(FSComp.SR.tcUnexpectedBigRationalConstant(), m)) | SynConst.Measure _ -> error (Error(FSComp.SR.tcInvalidTypeForUnitsOfMeasure(), m)) | SynConst.UInt16s _ -> error (InternalError(FSComp.SR.tcUnexpectedConstUint16Array(), m)) @@ -4414,7 +4414,7 @@ and TcStaticConstantParameter cenv (env: TcEnv) tpenv kind (StripParenTypes v) i | SynConst.Single n when typeEquiv g g.float32_ty kind -> record(g.float32_ty); box (n: single) | SynConst.Double n when typeEquiv g g.float_ty kind -> record(g.float_ty); box (n: double) | SynConst.Char n when typeEquiv g g.char_ty kind -> record(g.char_ty); box (n: char) - | SynConst.String (s, _) when s <> null && typeEquiv g g.string_ty kind -> record(g.string_ty); box (s: string) + | SynConst.String (s, _, _) when s <> null && typeEquiv g g.string_ty kind -> record(g.string_ty); box (s: string) | SynConst.Bool b when typeEquiv g g.bool_ty kind -> record(g.bool_ty); box (b: bool) | _ -> fail() v, tpenv @@ -4794,7 +4794,7 @@ and TcPat warnOnUpper cenv env topValInfo vFlags (tpenv, names, takenNames) ty p match pat with | SynPat.Const (c, m) -> match c with - | SynConst.Bytes (bytes, m) -> + | SynConst.Bytes (bytes, _, m) -> UnifyTypes cenv env m ty (mkByteArrayTy cenv.g) TcPat warnOnUpper cenv env None vFlags (tpenv, names, takenNames) ty (SynPat.ArrayOrList (true, [ for b in bytes -> SynPat.Const(SynConst.Byte b, m) ], m)) @@ -5425,11 +5425,11 @@ and TcExprUndelayed cenv overallTy env tpenv (synExpr: SynExpr) = | SynExpr.DotIndexedGet _ | SynExpr.DotIndexedSet _ | SynExpr.TypeApp _ | SynExpr.Ident _ | SynExpr.LongIdent _ | SynExpr.App _ | SynExpr.DotGet _ -> error(Error(FSComp.SR.tcExprUndelayed(), synExpr.Range)) - | SynExpr.Const (SynConst.String (s, m), _) -> + | SynExpr.Const (SynConst.String (s, _, m), _) -> CallExprHasTypeSink cenv.tcSink (m, env.NameEnv, overallTy, env.AccessRights) TcConstStringExpr cenv overallTy env m tpenv s - | SynExpr.InterpolatedString (parts, m) -> + | SynExpr.InterpolatedString (parts, _, m) -> checkLanguageFeatureError cenv.g.langVersion LanguageFeature.StringInterpolation m CallExprHasTypeSink cenv.tcSink (m, env.NameEnv, overallTy, env.AccessRights) @@ -6784,7 +6784,7 @@ and TcConstExpr cenv overallTy env m tpenv c = match c with // NOTE: these aren't "really" constants - | SynConst.Bytes (bytes, m) -> + | SynConst.Bytes (bytes, _, m) -> UnifyTypes cenv env m overallTy (mkByteArrayTy cenv.g) Expr.Op (TOp.Bytes bytes, [], [], m), tpenv @@ -6810,7 +6810,7 @@ and TcConstExpr cenv overallTy env m tpenv c = let i64 = int64 s SynExpr.App (ExprAtomicFlag.Atomic, false, mkSynLidGet m [modName] "FromInt64", SynExpr.Const (SynConst.Int64 i64, m), m) with _ -> - SynExpr.App (ExprAtomicFlag.Atomic, false, mkSynLidGet m [modName] "FromString", SynExpr.Const (SynConst.String (s, m), m), m) + SynExpr.App (ExprAtomicFlag.Atomic, false, mkSynLidGet m [modName] "FromString", SynExpr.Const (SynConst.String (s, SynStringKind.Regular, m), m), m) if suffix <> "I" then expr diff --git a/src/fsharp/SyntaxTree.fs b/src/fsharp/SyntaxTree.fs index 6e9b68280c6..7be3b52e981 100644 --- a/src/fsharp/SyntaxTree.fs +++ b/src/fsharp/SyntaxTree.fs @@ -81,6 +81,19 @@ type SynTypar = | Typar(id, _, _) -> id.idRange +/// Indicate if the string had a special format +[] +type SynStringKind = + | Regular + | Verbatim + | TripleQuote + +/// Indicate if the byte string had a special format +[] +type SynByteStringKind = + | Regular + | Verbatim + /// The unchecked abstract syntax tree of constants in F# types and expressions. [] type SynConst = @@ -139,13 +152,13 @@ type SynConst = | UserNum of value: string * suffix: string /// F# syntax: verbatim or regular string, e.g. "abc" - | String of text: string * range: range + | String of text: string * synStringKind :SynStringKind * range: range /// F# syntax: verbatim or regular byte string, e.g. "abc"B. /// /// Also used internally in the typechecker once an array of unit16 constants /// is detected, to allow more efficient processing of large arrays of uint16 constants. - | Bytes of bytes: byte[] * range: range + | Bytes of bytes: byte[] * synByteStringKind: SynByteStringKind * range: range /// Used internally in the typechecker once an array of unit16 constants /// is detected, to allow more efficient processing of large arrays of uint16 constants. @@ -157,7 +170,7 @@ type SynConst = /// Gets the syntax range of this construct member c.Range dflt = match c with - | SynConst.String (_, m0) | SynConst.Bytes (_, m0) -> m0 + | SynConst.String (_, _, m0) | SynConst.Bytes (_, _, m0) -> m0 | _ -> dflt /// Represents an unchecked syntax tree of F# unit of measure annotations. @@ -1015,6 +1028,7 @@ type SynExpr = /// Note the string ranges include the quotes, verbatim markers, dollar sign and braces | InterpolatedString of contents: SynInterpolatedStringPart list * + synStringKind :SynStringKind * range: range /// Gets the syntax range of this construct diff --git a/src/fsharp/SyntaxTreeOps.fs b/src/fsharp/SyntaxTreeOps.fs index 3fd1ce32224..c3287c241fd 100644 --- a/src/fsharp/SyntaxTreeOps.fs +++ b/src/fsharp/SyntaxTreeOps.fs @@ -735,7 +735,7 @@ let rec synExprContainsError inpExpr = | SynExpr.LetOrUseBang (rhs=e1;body=e2;andBangs=es) -> walkExpr e1 || walkExprs [ for (_,_,_,_,e,_) in es do yield e ] || walkExpr e2 - | SynExpr.InterpolatedString (parts, _m) -> + | SynExpr.InterpolatedString (parts, _, _m) -> walkExprs (parts |> List.choose (function | SynInterpolatedStringPart.String _ -> None diff --git a/src/fsharp/lex.fsl b/src/fsharp/lex.fsl index 8317f4f5a92..e48cb044552 100644 --- a/src/fsharp/lex.fsl +++ b/src/fsharp/lex.fsl @@ -136,25 +136,35 @@ let startString args (lexbuf: UnicodeLexing.Lexbuf) = let m = lexbuf.LexemeRange let startp = lexbuf.StartPos let fin = - LexerStringFinisher (fun buf kind isPart cont -> + LexerStringFinisher (fun buf kind context cont -> // Adjust the start-of-token mark back to the true start of the token lexbuf.StartPos <- startp - if kind.IsByteString then + let isPart = context.HasFlag(LexerStringFinisherContext.InterpolatedPart) + let isVerbatim = context.HasFlag(LexerStringFinisherContext.Verbatim) + let isTripleQuote = context.HasFlag(LexerStringFinisherContext.TripleQuote) + + if kind.IsByteString then + let synByteStringKind = if isVerbatim then SynByteStringKind.Verbatim else SynByteStringKind.Regular if kind.IsInterpolated then fail args lexbuf (FSComp.SR.lexByteStringMayNotBeInterpolated()) () - BYTEARRAY (Lexhelp.stringBufferAsBytes buf, cont) + BYTEARRAY (Lexhelp.stringBufferAsBytes buf, synByteStringKind, cont) elif Lexhelp.stringBufferIsBytes buf then - BYTEARRAY (Lexhelp.stringBufferAsBytes buf, cont) + BYTEARRAY (Lexhelp.stringBufferAsBytes buf, synByteStringKind, cont) else fail args lexbuf (FSComp.SR.lexByteArrayCannotEncode()) () - BYTEARRAY (Lexhelp.stringBufferAsBytes buf, cont) + BYTEARRAY (Lexhelp.stringBufferAsBytes buf, synByteStringKind, cont) elif kind.IsInterpolated then let s = Lexhelp.stringBufferAsString buf - if kind.IsInterpolatedFirst then + if kind.IsInterpolatedFirst then + let synStringKind = + if isTripleQuote then + SynStringKind.TripleQuote + else + SynStringKind.Regular if isPart then - INTERP_STRING_BEGIN_PART (s, cont) + INTERP_STRING_BEGIN_PART (s, synStringKind, cont) else - INTERP_STRING_BEGIN_END (s, cont) + INTERP_STRING_BEGIN_END (s, synStringKind, cont) else if isPart then INTERP_STRING_PART (s, cont) @@ -162,7 +172,14 @@ let startString args (lexbuf: UnicodeLexing.Lexbuf) = INTERP_STRING_END (s, cont) else let s = Lexhelp.stringBufferAsString buf - STRING (s, cont)) + let synStringKind = + if isVerbatim then + SynStringKind.Verbatim + elif isTripleQuote then + SynStringKind.TripleQuote + else + SynStringKind.Regular + STRING (s, synStringKind, cont)) buf,fin,m @@ -1124,13 +1141,13 @@ and singleQuoteString sargs skip = parse | '"' { let (buf, fin, _m, kind, args) = sargs let cont = LexCont.Token(args.ifdefStack, args.stringNest) - fin.Finish buf kind false cont + fin.Finish buf kind (enum(0)) cont } | '"''B' { let (buf, fin, _m, kind, args) = sargs let cont = LexCont.Token(args.ifdefStack, args.stringNest) - fin.Finish buf { kind with IsByteString = true } false cont + fin.Finish buf { kind with IsByteString = true } (enum(0)) cont } | ("{{" | "}}") @@ -1147,7 +1164,7 @@ and singleQuoteString sargs skip = parse let m2 = lexbuf.LexemeRange args.stringNest <- (1, LexerStringStyle.SingleQuote, m2) :: args.stringNest let cont = LexCont.Token(args.ifdefStack, args.stringNest) - fin.Finish buf kind true cont + fin.Finish buf kind LexerStringFinisherContext.InterpolatedPart cont else addUnicodeString buf (lexeme lexbuf) if not skip then STRING_TEXT (LexCont.String(args.ifdefStack, args.stringNest, LexerStringStyle.SingleQuote, kind, m)) @@ -1214,13 +1231,13 @@ and verbatimString sargs skip = parse | '"' { let (buf, fin, _m, kind, args) = sargs let cont = LexCont.Token(args.ifdefStack, args.stringNest) - fin.Finish buf kind false cont + fin.Finish buf kind LexerStringFinisherContext.Verbatim cont } | '"''B' { let (buf, fin, _m, kind, args) = sargs let cont = LexCont.Token(args.ifdefStack, args.stringNest) - fin.Finish buf { kind with IsByteString = true } false cont + fin.Finish buf { kind with IsByteString = true } LexerStringFinisherContext.Verbatim cont } | newline @@ -1244,7 +1261,7 @@ and verbatimString sargs skip = parse let m2 = lexbuf.LexemeRange args.stringNest <- (1, LexerStringStyle.Verbatim, m2) :: args.stringNest let cont = LexCont.Token(args.ifdefStack, args.stringNest) - fin.Finish buf kind true cont + fin.Finish buf kind (enum(3)) cont else addUnicodeString buf (lexeme lexbuf) if not skip then STRING_TEXT (LexCont.String(args.ifdefStack, args.stringNest, LexerStringStyle.Verbatim, kind, m)) @@ -1297,7 +1314,7 @@ and tripleQuoteString sargs skip = parse | '"' '"' '"' { let (buf, fin, _m, kind, args) = sargs let cont = LexCont.Token(args.ifdefStack, args.stringNest) - fin.Finish buf kind false cont } + fin.Finish buf kind (enum(4)) cont } | newline { let (buf, _fin, m, kind, args) = sargs @@ -1340,7 +1357,7 @@ and tripleQuoteString sargs skip = parse let m2 = lexbuf.LexemeRange args.stringNest <- (1, LexerStringStyle.TripleQuote, m2) :: args.stringNest let cont = LexCont.Token(args.ifdefStack, args.stringNest) - fin.Finish buf kind true cont + fin.Finish buf kind (enum(5)) cont else addUnicodeString buf (lexeme lexbuf) if not skip then STRING_TEXT (LexCont.String(args.ifdefStack, args.stringNest, LexerStringStyle.TripleQuote, kind, m)) diff --git a/src/fsharp/lexhelp.fs b/src/fsharp/lexhelp.fs index 767dcae116f..fe86184e0eb 100644 --- a/src/fsharp/lexhelp.fs +++ b/src/fsharp/lexhelp.fs @@ -18,6 +18,7 @@ open FSharp.Compiler.SourceCodeServices open FSharp.Compiler.SourceCodeServices.PrettyNaming open FSharp.Compiler.Text open FSharp.Compiler.Text.Range +open FSharp.Compiler.SyntaxTree /// The "mock" filename used by fsi.exe when reading from stdin. /// Has special treatment by the lexer, i.e. __SOURCE_DIRECTORY__ becomes GetCurrentDirectory() @@ -119,31 +120,54 @@ let stringBufferAsBytes (buf: ByteBuffer) = let bytes = buf.Close() Array.init (bytes.Length / 2) (fun i -> bytes.[i*2]) +[] +type LexerStringFinisherContext = + | InterpolatedPart = 1 + | Verbatim = 2 + | TripleQuote = 4 + type LexerStringFinisher = - | LexerStringFinisher of (ByteBuffer -> LexerStringKind -> bool -> LexerContinuation -> token) + | LexerStringFinisher of (ByteBuffer -> LexerStringKind -> LexerStringFinisherContext -> LexerContinuation -> token) - member fin.Finish (buf: ByteBuffer) kind isInterpolatedStringPart cont = + member fin.Finish (buf: ByteBuffer) kind context cont = let (LexerStringFinisher f) = fin - f buf kind isInterpolatedStringPart cont + f buf kind context cont static member Default = - LexerStringFinisher (fun buf kind isPart cont -> + LexerStringFinisher (fun buf kind context cont -> + let isPart = context.HasFlag(LexerStringFinisherContext.InterpolatedPart) + let isVerbatim = context.HasFlag(LexerStringFinisherContext.Verbatim) + let isTripleQuote = context.HasFlag(LexerStringFinisherContext.TripleQuote) + if kind.IsInterpolated then let s = stringBufferAsString buf - if kind.IsInterpolatedFirst then + if kind.IsInterpolatedFirst then + let synStringKind = + if isTripleQuote then + SynStringKind.TripleQuote + else + SynStringKind.Regular if isPart then - INTERP_STRING_BEGIN_PART (s, cont) + INTERP_STRING_BEGIN_PART (s, synStringKind, cont) else - INTERP_STRING_BEGIN_END (s, cont) + INTERP_STRING_BEGIN_END (s, synStringKind, cont) else if isPart then INTERP_STRING_PART (s, cont) else INTERP_STRING_END (s, cont) - elif kind.IsByteString then - BYTEARRAY (stringBufferAsBytes buf, cont) + elif kind.IsByteString then + let synByteStringKind = if isVerbatim then SynByteStringKind.Verbatim else SynByteStringKind.Regular + BYTEARRAY (stringBufferAsBytes buf, synByteStringKind, cont) else - STRING (stringBufferAsString buf, cont) + let synStringKind = + if isVerbatim then + SynStringKind.Verbatim + elif isTripleQuote then + SynStringKind.TripleQuote + else + SynStringKind.Regular + STRING (stringBufferAsString buf, synStringKind, cont) ) let addUnicodeString (buf: ByteBuffer) (x:string) = diff --git a/src/fsharp/lexhelp.fsi b/src/fsharp/lexhelp.fsi index f7cf0b25d6d..e3f1053fb24 100644 --- a/src/fsharp/lexhelp.fsi +++ b/src/fsharp/lexhelp.fsi @@ -54,10 +54,15 @@ val reusingLexbufForParsing: UnicodeLexing.Lexbuf -> (unit -> 'a) -> 'a val usingLexbufForParsing: UnicodeLexing.Lexbuf * string -> (UnicodeLexing.Lexbuf -> 'a) -> 'a +type LexerStringFinisherContext = + | InterpolatedPart = 1 + | Verbatim = 2 + | TripleQuote = 4 + type LexerStringFinisher = - | LexerStringFinisher of (ByteBuffer -> LexerStringKind -> bool -> LexerContinuation -> token) + | LexerStringFinisher of (ByteBuffer -> LexerStringKind -> LexerStringFinisherContext -> LexerContinuation -> token) - member Finish: buf: ByteBuffer -> kind: LexerStringKind -> isInterpolatedStringPart: bool -> cont: LexerContinuation -> token + member Finish: buf: ByteBuffer -> kind: LexerStringKind -> context: LexerStringFinisherContext -> cont: LexerContinuation -> token static member Default: LexerStringFinisher diff --git a/src/fsharp/pars.fsy b/src/fsharp/pars.fsy index cfce563057b..ef216619d09 100644 --- a/src/fsharp/pars.fsy +++ b/src/fsharp/pars.fsy @@ -195,10 +195,10 @@ let rangeOfLongIdent(lid:LongIdent) = %} // Producing these changes the lex state, e.g. string --> token, or nesting level of braces in interpolated strings -%token BYTEARRAY -%token STRING -%token INTERP_STRING_BEGIN_END -%token INTERP_STRING_BEGIN_PART +%token BYTEARRAY +%token STRING +%token INTERP_STRING_BEGIN_END +%token INTERP_STRING_BEGIN_PART %token INTERP_STRING_PART %token INTERP_STRING_END %token LBRACE RBRACE @@ -647,7 +647,8 @@ hashDirectiveArgs: /* One argument to a #directive */ hashDirectiveArg: | stringOrKeywordString - { $1 } + { let s, _ = $1 + s } /*--------------------------------------------------------------------------*/ @@ -2239,8 +2240,9 @@ braceBarFieldDeclListCore: inlineAssemblyTyconRepr: | HASH stringOrKeywordString HASH { libraryOnlyError (lhs parseState) - let lhsm = lhs parseState - let ilType = ParseAssemblyCodeType $2 parseState.LexBuffer.SupportsFeature (rhs parseState 2) + let lhsm = lhs parseState + let s, _ = $2 + let ilType = ParseAssemblyCodeType s parseState.LexBuffer.SupportsFeature (rhs parseState 2) SynTypeDefnSimpleRepr.LibraryOnlyILAssembly (box ilType, lhsm) } classOrInterfaceOrStruct: @@ -2665,7 +2667,7 @@ cPrototype: let rhsExpr = SynExpr.App (ExprAtomicFlag.NonAtomic, false, SynExpr.Ident (ident("failwith", rhs parseState 6)), - SynExpr.Const (SynConst.String("extern was not given a DllImport attribute", rhs parseState 8), rhs parseState 8), + SynExpr.Const (SynConst.String("extern was not given a DllImport attribute", SynStringKind.Regular, rhs parseState 8), rhs parseState 8), mRhs) (fun attrs _ -> let bindingId = SynPat.LongIdent (LongIdentWithDots([nm], []), None, Some noInferredTypars, SynArgPats.Pats [SynPat.Tuple(false, args, argsm)], vis, nmm) @@ -2909,10 +2911,12 @@ rawConstant: { SynConst.UserNum $1 } | stringOrKeywordString - { SynConst.String ($1, lhs parseState) } + { let s, synStringKind = $1 + SynConst.String (s, synStringKind, lhs parseState) } | BYTEARRAY - { SynConst.Bytes (fst $1, lhs parseState) } + { let (v, synByteStringKind, _) = $1 + SynConst.Bytes (v, synByteStringKind, lhs parseState) } rationalConstant: | INT32 INFIX_STAR_DIV_MOD_OP INT32 @@ -3851,7 +3855,7 @@ declExpr: dynamicArg: | IDENT - { let con = SynConst.String ($1, rhs parseState 1) + { let con = SynConst.String ($1, SynStringKind.Regular, rhs parseState 1) let arg2 = SynExpr.Const (con, con.Range (rhs parseState 1)) arg2 } @@ -4246,7 +4250,8 @@ atomicExprAfterType: { $1 } | interpolatedString - { SynExpr.InterpolatedString($1, rhs parseState 1) } + { let parts, synStringKind = $1 + SynExpr.InterpolatedString(parts, synStringKind, rhs parseState 1) } | NULL { SynExpr.Null (lhs parseState) } @@ -4491,7 +4496,7 @@ forLoopDirection: inlineAssemblyExpr: | HASH stringOrKeywordString opt_inlineAssemblyTypeArg optCurriedArgExprs optInlineAssemblyReturnTypes HASH { libraryOnlyWarning (lhs parseState) - let s, sm = $2, rhs parseState 2 + let (s, _), sm = $2, rhs parseState 2 (fun m -> let ilInstrs = ParseAssemblyCodeInstructions s parseState.LexBuffer.SupportsFeature sm SynExpr.LibraryOnlyILAssembly (box ilInstrs, $3, List.rev $4, $5, m)) } @@ -5072,7 +5077,7 @@ atomType: | NULL { let m = rhs parseState 1 - SynType.StaticConstant(SynConst.String (null, m), m) } + SynType.StaticConstant(SynConst.String (null, SynStringKind.Regular, m), m) } | CONST atomicExpr { let e, _ = $2 @@ -5528,8 +5533,10 @@ colonOrEquals: /* A literal string or a string from a keyword like __SOURCE_FILE__ */ stringOrKeywordString: - | STRING { fst $1 } - | KEYWORD_STRING { $1 } + | STRING + { let (s, synStringKind, _) = $1 + s, synStringKind } + | KEYWORD_STRING { $1, SynStringKind.Regular } interpolatedStringFill: | declExpr @@ -5557,17 +5564,20 @@ interpolatedStringParts: /* INTERP_STRING_BEGIN_PART int32 INTERP_STRING_PART int32 INTERP_STRING_END */ interpolatedString: | INTERP_STRING_BEGIN_PART interpolatedStringFill interpolatedStringParts - { SynInterpolatedStringPart.String (fst $1, rhs parseState 1) :: SynInterpolatedStringPart.FillExpr $2 :: $3 } + { let s, synStringKind, _ = $1 + SynInterpolatedStringPart.String (s, rhs parseState 1) :: SynInterpolatedStringPart.FillExpr $2 :: $3, synStringKind } | INTERP_STRING_BEGIN_END - { [ SynInterpolatedStringPart.String (fst $1, rhs parseState 1) ] } + { let s, synStringKind, _ = $1 + [ SynInterpolatedStringPart.String (s, rhs parseState 1) ], synStringKind } | INTERP_STRING_BEGIN_PART interpolatedStringParts { + let s, synStringKind, _ = $1 let rbrace = parseState.InputEndPosition 1 let lbrace = parseState.InputStartPosition 2 reportParseErrorAt (mkSynRange rbrace lbrace) (FSComp.SR.parsEmptyFillInInterpolatedString()) - SynInterpolatedStringPart.String (fst $1, rhs parseState 1) :: $2 } + SynInterpolatedStringPart.String (s, rhs parseState 1) :: $2, synStringKind } opt_HIGH_PRECEDENCE_APP: | HIGH_PRECEDENCE_BRACK_APP { } diff --git a/src/fsharp/service/ServiceLexing.fs b/src/fsharp/service/ServiceLexing.fs index 2e8931a697e..6747a94ee82 100755 --- a/src/fsharp/service/ServiceLexing.fs +++ b/src/fsharp/service/ServiceLexing.fs @@ -24,6 +24,7 @@ open FSharp.Compiler.SourceCodeServices open FSharp.Compiler.Text open FSharp.Compiler.Text.Pos open FSharp.Compiler.Text.Range +open FSharp.Compiler.SyntaxTree open Internal.Utilities @@ -34,12 +35,12 @@ type Positions = Position * Position module FSharpTokenTag = let Identifier = tagOfToken (IDENT "a") - let String = tagOfToken (STRING ("a", LexCont.Default)) + let String = tagOfToken (STRING ("a", SynStringKind.Regular, LexCont.Default)) let IDENT = tagOfToken (IDENT "a") let STRING = String - let INTERP_STRING_BEGIN_END = tagOfToken (INTERP_STRING_BEGIN_END ("a", LexCont.Default)) - let INTERP_STRING_BEGIN_PART = tagOfToken (INTERP_STRING_BEGIN_PART ("a", LexCont.Default)) + let INTERP_STRING_BEGIN_END = tagOfToken (INTERP_STRING_BEGIN_END ("a", SynStringKind.Regular, LexCont.Default)) + let INTERP_STRING_BEGIN_PART = tagOfToken (INTERP_STRING_BEGIN_PART ("a", SynStringKind.Regular, LexCont.Default)) let INTERP_STRING_PART = tagOfToken (INTERP_STRING_PART ("a", LexCont.Default)) let INTERP_STRING_END = tagOfToken (INTERP_STRING_END ("a", LexCont.Default)) let LPAREN = tagOfToken LPAREN @@ -379,14 +380,14 @@ module internal LexerStateEncoding = | LINE_COMMENT cont | STRING_TEXT cont | EOF cont - | INTERP_STRING_BEGIN_PART (_, cont) + | INTERP_STRING_BEGIN_PART (_, _, cont) | INTERP_STRING_PART (_, cont) - | INTERP_STRING_BEGIN_END (_, cont) + | INTERP_STRING_BEGIN_END (_, _, cont) | INTERP_STRING_END (_, cont) | LBRACE cont | RBRACE cont - | BYTEARRAY (_, cont) - | STRING (_, cont) -> cont + | BYTEARRAY (_, _, cont) + | STRING (_, _, cont) -> cont | _ -> prevLexcont // Note that this will discard all lexcont state, including the ifdefStack. diff --git a/src/fsharp/service/ServiceParseTreeWalk.fs b/src/fsharp/service/ServiceParseTreeWalk.fs index e7748415c3c..46a9b180937 100755 --- a/src/fsharp/service/ServiceParseTreeWalk.fs +++ b/src/fsharp/service/ServiceParseTreeWalk.fs @@ -214,7 +214,7 @@ module public AstTraversal = | SynExpr.Const (_synConst, _range) -> None - | SynExpr.InterpolatedString (parts, _) -> + | SynExpr.InterpolatedString (parts, _, _) -> [ for part in parts do match part with | SynInterpolatedStringPart.String _ -> () diff --git a/src/fsharp/service/ServiceUntypedParse.fs b/src/fsharp/service/ServiceUntypedParse.fs index 9a9d83f059a..40c3dc27a1b 100755 --- a/src/fsharp/service/ServiceUntypedParse.fs +++ b/src/fsharp/service/ServiceUntypedParse.fs @@ -445,7 +445,7 @@ type FSharpParseFileResults(errors: FSharpDiagnostic[], input: ParsedInput optio | SynExpr.Paren (e, _, _, _) -> yield! walkExpr false e - | SynExpr.InterpolatedString (parts, _) -> + | SynExpr.InterpolatedString (parts, _, _) -> yield! walkExprs [ for part in parts do match part with | SynInterpolatedStringPart.String _ -> () diff --git a/tests/FSharp.Compiler.Service.Tests/SurfaceArea.netstandard.fs b/tests/FSharp.Compiler.Service.Tests/SurfaceArea.netstandard.fs index 2f2f63d424f..cfcf10f9d6c 100644 --- a/tests/FSharp.Compiler.Service.Tests/SurfaceArea.netstandard.fs +++ b/tests/FSharp.Compiler.Service.Tests/SurfaceArea.netstandard.fs @@ -6612,6 +6612,28 @@ FSharp.Compiler.SyntaxTree+SynBindingReturnInfo: SynBindingReturnInfo NewSynBind FSharp.Compiler.SyntaxTree+SynBindingReturnInfo: SynType get_typeName() FSharp.Compiler.SyntaxTree+SynBindingReturnInfo: SynType typeName FSharp.Compiler.SyntaxTree+SynBindingReturnInfo: System.String ToString() +FSharp.Compiler.SyntaxTree+SynByteStringKind+Tags: Int32 Regular +FSharp.Compiler.SyntaxTree+SynByteStringKind+Tags: Int32 Verbatim +FSharp.Compiler.SyntaxTree+SynByteStringKind: Boolean Equals(SynByteStringKind) +FSharp.Compiler.SyntaxTree+SynByteStringKind: Boolean Equals(System.Object) +FSharp.Compiler.SyntaxTree+SynByteStringKind: Boolean Equals(System.Object, System.Collections.IEqualityComparer) +FSharp.Compiler.SyntaxTree+SynByteStringKind: Boolean IsRegular +FSharp.Compiler.SyntaxTree+SynByteStringKind: Boolean IsVerbatim +FSharp.Compiler.SyntaxTree+SynByteStringKind: Boolean get_IsRegular() +FSharp.Compiler.SyntaxTree+SynByteStringKind: Boolean get_IsVerbatim() +FSharp.Compiler.SyntaxTree+SynByteStringKind: FSharp.Compiler.SyntaxTree+SynByteStringKind+Tags +FSharp.Compiler.SyntaxTree+SynByteStringKind: Int32 CompareTo(SynByteStringKind) +FSharp.Compiler.SyntaxTree+SynByteStringKind: Int32 CompareTo(System.Object) +FSharp.Compiler.SyntaxTree+SynByteStringKind: Int32 CompareTo(System.Object, System.Collections.IComparer) +FSharp.Compiler.SyntaxTree+SynByteStringKind: Int32 GetHashCode() +FSharp.Compiler.SyntaxTree+SynByteStringKind: Int32 GetHashCode(System.Collections.IEqualityComparer) +FSharp.Compiler.SyntaxTree+SynByteStringKind: Int32 Tag +FSharp.Compiler.SyntaxTree+SynByteStringKind: Int32 get_Tag() +FSharp.Compiler.SyntaxTree+SynByteStringKind: SynByteStringKind Regular +FSharp.Compiler.SyntaxTree+SynByteStringKind: SynByteStringKind Verbatim +FSharp.Compiler.SyntaxTree+SynByteStringKind: SynByteStringKind get_Regular() +FSharp.Compiler.SyntaxTree+SynByteStringKind: SynByteStringKind get_Verbatim() +FSharp.Compiler.SyntaxTree+SynByteStringKind: System.String ToString() FSharp.Compiler.SyntaxTree+SynComponentInfo: Boolean get_preferPostfix() FSharp.Compiler.SyntaxTree+SynComponentInfo: Boolean preferPostfix FSharp.Compiler.SyntaxTree+SynComponentInfo: FSharp.Compiler.Text.Range Range @@ -6642,6 +6664,8 @@ FSharp.Compiler.SyntaxTree+SynConst+Bytes: Byte[] bytes FSharp.Compiler.SyntaxTree+SynConst+Bytes: Byte[] get_bytes() FSharp.Compiler.SyntaxTree+SynConst+Bytes: FSharp.Compiler.Text.Range get_range() FSharp.Compiler.SyntaxTree+SynConst+Bytes: FSharp.Compiler.Text.Range range +FSharp.Compiler.SyntaxTree+SynConst+Bytes: SynByteStringKind get_synByteStringKind() +FSharp.Compiler.SyntaxTree+SynConst+Bytes: SynByteStringKind synByteStringKind FSharp.Compiler.SyntaxTree+SynConst+Char: Char Item FSharp.Compiler.SyntaxTree+SynConst+Char: Char get_Item() FSharp.Compiler.SyntaxTree+SynConst+Decimal: System.Decimal Item @@ -6666,6 +6690,8 @@ FSharp.Compiler.SyntaxTree+SynConst+Single: Single Item FSharp.Compiler.SyntaxTree+SynConst+Single: Single get_Item() FSharp.Compiler.SyntaxTree+SynConst+String: FSharp.Compiler.Text.Range get_range() FSharp.Compiler.SyntaxTree+SynConst+String: FSharp.Compiler.Text.Range range +FSharp.Compiler.SyntaxTree+SynConst+String: SynStringKind get_synStringKind() +FSharp.Compiler.SyntaxTree+SynConst+String: SynStringKind synStringKind FSharp.Compiler.SyntaxTree+SynConst+String: System.String get_text() FSharp.Compiler.SyntaxTree+SynConst+String: System.String text FSharp.Compiler.SyntaxTree+SynConst+Tags: Int32 Bool @@ -6771,7 +6797,7 @@ FSharp.Compiler.SyntaxTree+SynConst: Int32 Tag FSharp.Compiler.SyntaxTree+SynConst: Int32 get_Tag() FSharp.Compiler.SyntaxTree+SynConst: SynConst NewBool(Boolean) FSharp.Compiler.SyntaxTree+SynConst: SynConst NewByte(Byte) -FSharp.Compiler.SyntaxTree+SynConst: SynConst NewBytes(Byte[], FSharp.Compiler.Text.Range) +FSharp.Compiler.SyntaxTree+SynConst: SynConst NewBytes(Byte[], SynByteStringKind, FSharp.Compiler.Text.Range) FSharp.Compiler.SyntaxTree+SynConst: SynConst NewChar(Char) FSharp.Compiler.SyntaxTree+SynConst: SynConst NewDecimal(System.Decimal) FSharp.Compiler.SyntaxTree+SynConst: SynConst NewDouble(Double) @@ -6782,7 +6808,7 @@ FSharp.Compiler.SyntaxTree+SynConst: SynConst NewIntPtr(Int64) FSharp.Compiler.SyntaxTree+SynConst: SynConst NewMeasure(SynConst, SynMeasure) FSharp.Compiler.SyntaxTree+SynConst: SynConst NewSByte(SByte) FSharp.Compiler.SyntaxTree+SynConst: SynConst NewSingle(Single) -FSharp.Compiler.SyntaxTree+SynConst: SynConst NewString(System.String, FSharp.Compiler.Text.Range) +FSharp.Compiler.SyntaxTree+SynConst: SynConst NewString(System.String, SynStringKind, FSharp.Compiler.Text.Range) FSharp.Compiler.SyntaxTree+SynConst: SynConst NewUInt16(UInt16) FSharp.Compiler.SyntaxTree+SynConst: SynConst NewUInt16s(UInt16[]) FSharp.Compiler.SyntaxTree+SynConst: SynConst NewUInt32(UInt32) @@ -7036,6 +7062,8 @@ FSharp.Compiler.SyntaxTree+SynExpr+InterpolatedString: FSharp.Compiler.Text.Rang FSharp.Compiler.SyntaxTree+SynExpr+InterpolatedString: FSharp.Compiler.Text.Range range FSharp.Compiler.SyntaxTree+SynExpr+InterpolatedString: Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.SyntaxTree+SynInterpolatedStringPart] contents FSharp.Compiler.SyntaxTree+SynExpr+InterpolatedString: Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.SyntaxTree+SynInterpolatedStringPart] get_contents() +FSharp.Compiler.SyntaxTree+SynExpr+InterpolatedString: SynStringKind get_synStringKind() +FSharp.Compiler.SyntaxTree+SynExpr+InterpolatedString: SynStringKind synStringKind FSharp.Compiler.SyntaxTree+SynExpr+JoinIn: FSharp.Compiler.Text.Range get_lhsRange() FSharp.Compiler.SyntaxTree+SynExpr+JoinIn: FSharp.Compiler.Text.Range get_range() FSharp.Compiler.SyntaxTree+SynExpr+JoinIn: FSharp.Compiler.Text.Range lhsRange @@ -7626,7 +7654,7 @@ FSharp.Compiler.SyntaxTree+SynExpr: SynExpr NewIfThenElse(SynExpr, SynExpr, Micr FSharp.Compiler.SyntaxTree+SynExpr: SynExpr NewImplicitZero(FSharp.Compiler.Text.Range) FSharp.Compiler.SyntaxTree+SynExpr: SynExpr NewInferredDowncast(SynExpr, FSharp.Compiler.Text.Range) FSharp.Compiler.SyntaxTree+SynExpr: SynExpr NewInferredUpcast(SynExpr, FSharp.Compiler.Text.Range) -FSharp.Compiler.SyntaxTree+SynExpr: SynExpr NewInterpolatedString(Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.SyntaxTree+SynInterpolatedStringPart], FSharp.Compiler.Text.Range) +FSharp.Compiler.SyntaxTree+SynExpr: SynExpr NewInterpolatedString(Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.SyntaxTree+SynInterpolatedStringPart], SynStringKind, FSharp.Compiler.Text.Range) FSharp.Compiler.SyntaxTree+SynExpr: SynExpr NewJoinIn(SynExpr, FSharp.Compiler.Text.Range, SynExpr, FSharp.Compiler.Text.Range) FSharp.Compiler.SyntaxTree+SynExpr: SynExpr NewLambda(Boolean, Boolean, SynSimplePats, SynExpr, Microsoft.FSharp.Core.FSharpOption`1[System.Tuple`2[Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.SyntaxTree+SynPat],FSharp.Compiler.SyntaxTree+SynExpr]], FSharp.Compiler.Text.Range) FSharp.Compiler.SyntaxTree+SynExpr: SynExpr NewLazy(SynExpr, FSharp.Compiler.Text.Range) @@ -8682,6 +8710,33 @@ FSharp.Compiler.SyntaxTree+SynStaticOptimizationConstraint: Int32 get_Tag() FSharp.Compiler.SyntaxTree+SynStaticOptimizationConstraint: SynStaticOptimizationConstraint NewWhenTyparIsStruct(SynTypar, FSharp.Compiler.Text.Range) FSharp.Compiler.SyntaxTree+SynStaticOptimizationConstraint: SynStaticOptimizationConstraint NewWhenTyparTyconEqualsTycon(SynTypar, SynType, FSharp.Compiler.Text.Range) FSharp.Compiler.SyntaxTree+SynStaticOptimizationConstraint: System.String ToString() +FSharp.Compiler.SyntaxTree+SynStringKind+Tags: Int32 Regular +FSharp.Compiler.SyntaxTree+SynStringKind+Tags: Int32 TripleQuote +FSharp.Compiler.SyntaxTree+SynStringKind+Tags: Int32 Verbatim +FSharp.Compiler.SyntaxTree+SynStringKind: Boolean Equals(SynStringKind) +FSharp.Compiler.SyntaxTree+SynStringKind: Boolean Equals(System.Object) +FSharp.Compiler.SyntaxTree+SynStringKind: Boolean Equals(System.Object, System.Collections.IEqualityComparer) +FSharp.Compiler.SyntaxTree+SynStringKind: Boolean IsRegular +FSharp.Compiler.SyntaxTree+SynStringKind: Boolean IsTripleQuote +FSharp.Compiler.SyntaxTree+SynStringKind: Boolean IsVerbatim +FSharp.Compiler.SyntaxTree+SynStringKind: Boolean get_IsRegular() +FSharp.Compiler.SyntaxTree+SynStringKind: Boolean get_IsTripleQuote() +FSharp.Compiler.SyntaxTree+SynStringKind: Boolean get_IsVerbatim() +FSharp.Compiler.SyntaxTree+SynStringKind: FSharp.Compiler.SyntaxTree+SynStringKind+Tags +FSharp.Compiler.SyntaxTree+SynStringKind: Int32 CompareTo(SynStringKind) +FSharp.Compiler.SyntaxTree+SynStringKind: Int32 CompareTo(System.Object) +FSharp.Compiler.SyntaxTree+SynStringKind: Int32 CompareTo(System.Object, System.Collections.IComparer) +FSharp.Compiler.SyntaxTree+SynStringKind: Int32 GetHashCode() +FSharp.Compiler.SyntaxTree+SynStringKind: Int32 GetHashCode(System.Collections.IEqualityComparer) +FSharp.Compiler.SyntaxTree+SynStringKind: Int32 Tag +FSharp.Compiler.SyntaxTree+SynStringKind: Int32 get_Tag() +FSharp.Compiler.SyntaxTree+SynStringKind: SynStringKind Regular +FSharp.Compiler.SyntaxTree+SynStringKind: SynStringKind TripleQuote +FSharp.Compiler.SyntaxTree+SynStringKind: SynStringKind Verbatim +FSharp.Compiler.SyntaxTree+SynStringKind: SynStringKind get_Regular() +FSharp.Compiler.SyntaxTree+SynStringKind: SynStringKind get_TripleQuote() +FSharp.Compiler.SyntaxTree+SynStringKind: SynStringKind get_Verbatim() +FSharp.Compiler.SyntaxTree+SynStringKind: System.String ToString() FSharp.Compiler.SyntaxTree+SynTypar: Boolean get_isCompGen() FSharp.Compiler.SyntaxTree+SynTypar: Boolean isCompGen FSharp.Compiler.SyntaxTree+SynTypar: FSharp.Compiler.Text.Range Range @@ -9413,6 +9468,7 @@ FSharp.Compiler.SyntaxTree: FSharp.Compiler.SyntaxTree+SynAttributeList FSharp.Compiler.SyntaxTree: FSharp.Compiler.SyntaxTree+SynBinding FSharp.Compiler.SyntaxTree: FSharp.Compiler.SyntaxTree+SynBindingKind FSharp.Compiler.SyntaxTree: FSharp.Compiler.SyntaxTree+SynBindingReturnInfo +FSharp.Compiler.SyntaxTree: FSharp.Compiler.SyntaxTree+SynByteStringKind FSharp.Compiler.SyntaxTree: FSharp.Compiler.SyntaxTree+SynComponentInfo FSharp.Compiler.SyntaxTree: FSharp.Compiler.SyntaxTree+SynConst FSharp.Compiler.SyntaxTree: FSharp.Compiler.SyntaxTree+SynEnumCase @@ -9441,6 +9497,7 @@ FSharp.Compiler.SyntaxTree: FSharp.Compiler.SyntaxTree+SynSimplePat FSharp.Compiler.SyntaxTree: FSharp.Compiler.SyntaxTree+SynSimplePatAlternativeIdInfo FSharp.Compiler.SyntaxTree: FSharp.Compiler.SyntaxTree+SynSimplePats FSharp.Compiler.SyntaxTree: FSharp.Compiler.SyntaxTree+SynStaticOptimizationConstraint +FSharp.Compiler.SyntaxTree: FSharp.Compiler.SyntaxTree+SynStringKind FSharp.Compiler.SyntaxTree: FSharp.Compiler.SyntaxTree+SynTypar FSharp.Compiler.SyntaxTree: FSharp.Compiler.SyntaxTree+SynTyparDecl FSharp.Compiler.SyntaxTree: FSharp.Compiler.SyntaxTree+SynType diff --git a/tests/service/Symbols.fs b/tests/service/Symbols.fs index 983d9555c8f..fe957fb4317 100644 --- a/tests/service/Symbols.fs +++ b/tests/service/Symbols.fs @@ -287,4 +287,127 @@ module SyntaxExpressions = assertRange 2 4 3 14 doRange assertRange 4 4 5 18 doBangRange | _ -> - failwith "Could not find SynExpr.Do" \ No newline at end of file + failwith "Could not find SynExpr.Do" + +module Strings = + let getBindingExpressionValue (parseResults: ParsedInput option) = + parseResults + |> Option.bind + (fun tree -> + match tree with + | ParsedInput.ImplFile (ParsedImplFileInput (modules = modules)) -> + modules + |> List.tryPick + (function + | SynModuleOrNamespace (decls = decls) -> + decls + |> List.tryPick + (function + | SynModuleDecl.Let (bindings = bindings) -> + bindings + |> List.tryPick + (function + | SynBinding.Binding (_, + _, + _, + _, + _, + _, + _, + SynPat.Named _, + _, + e, + _, + _) -> Some e + | _ -> None) + | _ -> None)) + | _ -> None) + + let getBindingConstValue parseResults = + match getBindingExpressionValue parseResults with + | Some (SynExpr.Const(c,_)) -> Some c + | _ -> None + + [] + let ``SynConst.String with SynStringKind.Regular`` () = + let parseResults = + getParseResults + """ + let s = "yo" + """ + + match getBindingConstValue parseResults with + | Some (SynConst.String (_, kind, _)) -> kind |> should equal SynStringKind.Regular + | _ -> failwithf "Couldn't find const" + + [] + let ``SynConst.String with SynStringKind.Verbatim`` () = + let parseResults = + getParseResults + """ + let s = @"yo" + """ + + match getBindingConstValue parseResults with + | Some (SynConst.String (_, kind, _)) -> kind |> should equal SynStringKind.Verbatim + | _ -> failwithf "Couldn't find const" + + [] + let ``SynConst.String with SynStringKind.TripleQuote`` () = + let parseResults = + getParseResults + " + let s = \"\"\"yo\"\"\" + " + + match getBindingConstValue parseResults with + | Some (SynConst.String (_, kind, _)) -> kind |> should equal SynStringKind.TripleQuote + | _ -> failwithf "Couldn't find const" + + [] + let ``SynConst.Bytes with SynByteStringKind.Regular`` () = + let parseResults = + getParseResults + """ +let bytes = "yo"B + """ + + match getBindingConstValue parseResults with + | Some (SynConst.Bytes (_, kind, _)) -> kind |> should equal SynByteStringKind.Regular + | _ -> failwithf "Couldn't find const" + + [] + let ``SynConst.Bytes with SynByteStringKind.Verbatim`` () = + let parseResults = + getParseResults + """ +let bytes = @"yo"B + """ + + match getBindingConstValue parseResults with + | Some (SynConst.Bytes (_, kind, _)) -> kind |> should equal SynByteStringKind.Verbatim + | _ -> failwithf "Couldn't find const" + + [] + let ``SynExpr.InterpolatedString with SynStringKind.TripleQuote`` () = + let parseResults = + getParseResults + " + let s = $\"\"\"yo {42}\"\"\" + " + + match getBindingExpressionValue parseResults with + | Some (SynExpr.InterpolatedString(_, kind, _)) -> kind |> should equal SynStringKind.TripleQuote + | _ -> failwithf "Couldn't find const" + + [] + let ``SynExpr.InterpolatedString with SynStringKind.Regular`` () = + let parseResults = + getParseResults + """ + let s = $"yo {42}" + """ + + match getBindingExpressionValue parseResults with + | Some (SynExpr.InterpolatedString(_, kind, _)) -> kind |> should equal SynStringKind.Regular + | _ -> failwithf "Couldn't find const" \ No newline at end of file