Skip to content
This repository has been archived by the owner on Dec 23, 2024. It is now read-only.

Commit

Permalink
Add parser recovery for unfinished interface implementation (dotnet#1…
Browse files Browse the repository at this point in the history
…0416)

* Add parser recovery for unfinished interface implementation

* Report indentation problem via parser

* Update public area
  • Loading branch information
auduchinok authored and nosami committed Feb 22, 2021
1 parent 4cb5aa1 commit 0855b78
Show file tree
Hide file tree
Showing 9 changed files with 79 additions and 16 deletions.
15 changes: 10 additions & 5 deletions src/fsharp/LexFilter.fs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ let outputPos os (p: Position) = Printf.fprintf os "(%d:%d)" p.OriginalLine p.Co
/// Used for warning strings, which should display columns as 1-based and display
/// the lines after taking '# line' directives into account (i.e. do not use
/// p.OriginalLine)
let warningStringOfPos (p: Position) = sprintf "(%d:%d)" p.Line (p.Column + 1)
let warningStringOfPosition (p: Position) = warningStringOfCoords p.Line p.Column

type Context =
// Position is position of keyword.
Expand Down Expand Up @@ -651,7 +651,7 @@ type LexFilterImpl (lightStatus: LightSyntaxStatus, compilingFsLib, lexer, lexbu
initialLookaheadTokenTup

let warn (s: TokenTup) msg =
warning(Lexhelp.IndentationProblem(msg, mkSynRange (startPosOfTokenTup s) s.LexbufState.EndPos))
warning(IndentationProblem(msg, mkSynRange (startPosOfTokenTup s) s.LexbufState.EndPos))

// 'query { join x in ys ... }'
// 'query { ...
Expand Down Expand Up @@ -865,9 +865,9 @@ type LexFilterImpl (lightStatus: LightSyntaxStatus, compilingFsLib, lexer, lexbu
warn tokenTup
(if debug then
sprintf "possible incorrect indentation: this token is offside of context at position %s, newCtxt = %A, stack = %A, newCtxtPos = %s, c1 = %d, c2 = %d"
(warningStringOfPos p1.Position) newCtxt offsideStack (stringOfPos (newCtxt.StartPos)) p1.Column c2
(warningStringOfPosition p1.Position) newCtxt offsideStack (stringOfPos (newCtxt.StartPos)) p1.Column c2
else
FSComp.SR.lexfltTokenIsOffsideOfContextStartedEarlier(warningStringOfPos p1.Position))
FSComp.SR.lexfltTokenIsOffsideOfContextStartedEarlier(warningStringOfPosition p1.Position))
let newOffsideStack = newCtxt :: offsideStack
if debug then dprintf "--> pushing, stack = %A\n" newOffsideStack
offsideStack <- newOffsideStack
Expand Down Expand Up @@ -1749,7 +1749,7 @@ type LexFilterImpl (lightStatus: LightSyntaxStatus, compilingFsLib, lexer, lexbu
let cond1 = tokenStartCol + (if leadingBar then 0 else 2) < offsidePos.Column
let cond2 = tokenStartCol + (if leadingBar then 1 else 2) < offsidePos.Column
if (cond1 <> cond2) then
errorR(Lexhelp.IndentationProblem(FSComp.SR.lexfltSeparatorTokensOfPatternMatchMisaligned(), mkSynRange (startPosOfTokenTup tokenTup) tokenTup.LexbufState.EndPos))
errorR(IndentationProblem(FSComp.SR.lexfltSeparatorTokensOfPatternMatchMisaligned(), mkSynRange (startPosOfTokenTup tokenTup) tokenTup.LexbufState.EndPos))
cond1
| END -> tokenStartCol + (if leadingBar then -1 else 1) < offsidePos.Column
| _ -> tokenStartCol + (if leadingBar then -1 else 1) < offsidePos.Column)) ->
Expand Down Expand Up @@ -2066,6 +2066,11 @@ type LexFilterImpl (lightStatus: LightSyntaxStatus, compilingFsLib, lexer, lexbu
let offsidePos = tokenStartPos
pushCtxt tokenTup (CtxtWithAsLet offsidePos)
returnToken tokenLexbufState OWITH

// Recovery for `interface ... with` member without further indented member implementations
elif lookaheadTokenStartPos.Column <= limCtxt.StartCol && (match limCtxt with CtxtInterfaceHead _ -> true | _ -> false) then
returnToken tokenLexbufState token

else
// In these situations
// interface I with
Expand Down
8 changes: 8 additions & 0 deletions src/fsharp/ParseHelpers.fs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,14 @@ open Internal.Utilities.Text.Parsing
[<NoEquality; NoComparison>]
exception SyntaxError of obj (* ParseErrorContext<_> *) * range: range

exception IndentationProblem of string * range

let warningStringOfCoords line column =
sprintf "(%d:%d)" line (column + 1)

let warningStringOfPos (p: pos) =
warningStringOfCoords p.Line p.Column

//------------------------------------------------------------------------
// Parsing: getting positions from the lexer
//------------------------------------------------------------------------
Expand Down
1 change: 0 additions & 1 deletion src/fsharp/lexhelp.fs
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,6 @@ let escape c =
//-----------------------------------------------------------------------

exception ReservedKeyword of string * range
exception IndentationProblem of string * range

module Keywords =
type private compatibilityMode =
Expand Down
2 changes: 0 additions & 2 deletions src/fsharp/lexhelp.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,6 @@ val escape: char -> char

exception ReservedKeyword of string * Range.range

exception IndentationProblem of string * Range.range

module Keywords =

val KeywordOrIdentifierToken: LexArgs -> UnicodeLexing.Lexbuf -> string -> token
Expand Down
26 changes: 18 additions & 8 deletions src/fsharp/pars.fsy
Original file line number Diff line number Diff line change
Expand Up @@ -1649,9 +1649,13 @@ classDefnMembers:


/* The members of an object type definition or type augmentation */
classDefnMembersAtLeastOne:
| classDefnMember opt_seps classDefnMembers
{ $1 @ $3 }
classDefnMembersAtLeastOne:
| classDefnMember opt_seps classDefnMembers
{ match $1, $3 with
| [ SynMemberDefn.Interface (_, Some [], m) ], nextMember :: _ ->
warning(IndentationProblem(FSComp.SR.lexfltTokenIsOffsideOfContextStartedEarlier(warningStringOfPos m.Start), nextMember.Range))
| _ -> ()
$1 @ $3 }


/* The "with get, set" part of a member definition */
Expand Down Expand Up @@ -1904,11 +1908,12 @@ classDefnMember:
| opt_attributes opt_declVisibility interfaceMember appType opt_interfaceImplDefn
{ if not (isNil $1) then errorR(Error(FSComp.SR.parsAttributesAreNotPermittedOnInterfaceImplementations(), rhs parseState 1))
if Option.isSome $2 then errorR(Error(FSComp.SR.parsInterfacesHaveSameVisibilityAsEnclosingType(), rhs parseState 3))
let mWhole =
let members = Option.map fst $5
let mWhole =
match $5 with
| None -> rhs2 parseState 1 4
| Some(mems) -> (rhs2 parseState 1 4, mems) ||> unionRangeWithListBy (fun (mem:SynMemberDefn) -> mem.Range)
[ SynMemberDefn.Interface ($4, $5, mWhole) ] }
| Some (_, m) -> unionRanges (rhs2 parseState 1 4) m
[ SynMemberDefn.Interface ($4, members, mWhole) ] }

| opt_attributes opt_declVisibility abstractMemberFlags opt_inline nameop opt_explicitValTyparDecls COLON topTypeWithTypeConstraints classMemberSpfnGetSet opt_ODECLEND
{ let ty, arity = $8
Expand Down Expand Up @@ -2042,9 +2047,14 @@ opt_declVisibility:
{ None }


opt_interfaceImplDefn:
opt_interfaceImplDefn:
| WITH objectImplementationBlock declEnd
{ Some($2) }
{ let members = $2
let m = (rhs parseState 1, members) ||> unionRangeWithListBy (fun (mem:SynMemberDefn) -> mem.Range)
Some (members, m) }

| WITH
{ Some ([], rhs parseState 1) }

| /* EMPTY */
{ None }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@
<Compile Include="..\service\ScriptOptionsTests.fs">
<Link>ScriptOptionsTests.fs</Link>
</Compile>
<Compile Include="..\service\ParserTests.fs" >
<Link>ParserTests.fs</Link>
</Compile>
<Compile Include="..\service\Program.fs">
<Link>Program.fs</Link>
</Compile>
Expand Down
14 changes: 14 additions & 0 deletions tests/FSharp.Compiler.Service.Tests/SurfaceArea.netstandard.fs
Original file line number Diff line number Diff line change
Expand Up @@ -20020,6 +20020,20 @@ FSharp.Compiler.ParseHelpers: FSharp.Compiler.ParseHelpers+LexerStringStyle
FSharp.Compiler.ParseHelpers: FSharp.Compiler.ParseHelpers+SyntaxError
FSharp.Compiler.ParseHelpers: ILInstr[] ParseAssemblyCodeInstructions(System.String, range)
FSharp.Compiler.ParseHelpers: ILType ParseAssemblyCodeType(System.String, range)
FSharp.Compiler.ParseHelpers+IndentationProblem: Boolean Equals(System.Exception)
FSharp.Compiler.ParseHelpers+IndentationProblem: Boolean Equals(System.Object)
FSharp.Compiler.ParseHelpers+IndentationProblem: Boolean Equals(System.Object, System.Collections.IEqualityComparer)
FSharp.Compiler.ParseHelpers+IndentationProblem: Int32 GetHashCode()
FSharp.Compiler.ParseHelpers+IndentationProblem: Int32 GetHashCode(System.Collections.IEqualityComparer)
FSharp.Compiler.ParseHelpers+IndentationProblem: System.String Data0
FSharp.Compiler.ParseHelpers+IndentationProblem: System.String get_Data0()
FSharp.Compiler.ParseHelpers+IndentationProblem: Void .ctor()
FSharp.Compiler.ParseHelpers+IndentationProblem: Void .ctor(System.String, range)
FSharp.Compiler.ParseHelpers+IndentationProblem: range Data1
FSharp.Compiler.ParseHelpers+IndentationProblem: range get_Data1()
FSharp.Compiler.ParseHelpers: FSharp.Compiler.ParseHelpers+IndentationProblem
FSharp.Compiler.ParseHelpers: System.String warningStringOfCoords(Int32, Int32)
FSharp.Compiler.ParseHelpers: System.String warningStringOfPos(pos)
FSharp.Compiler.PartialLongName: Boolean Equals(FSharp.Compiler.PartialLongName)
FSharp.Compiler.PartialLongName: Boolean Equals(System.Object)
FSharp.Compiler.PartialLongName: Boolean Equals(System.Object, System.Collections.IEqualityComparer)
Expand Down
5 changes: 5 additions & 0 deletions tests/service/Common.fs
Original file line number Diff line number Diff line change
Expand Up @@ -337,9 +337,14 @@ let rec allSymbolsInEntities compGen (entities: IList<FSharpEntity>) =
yield (x :> FSharpSymbol)
yield! allSymbolsInEntities compGen e.NestedEntities ]


let getParseResults (source: string) =
parseSourceCode("/home/user/Test.fsx", source)

let getParseAndCheckResults (source: string) =
parseAndCheckScript("/home/user/Test.fsx", source)


let inline dumpErrors results =
(^TResults: (member Errors: FSharpErrorInfo[]) results)
|> Array.map (fun e ->
Expand Down
21 changes: 21 additions & 0 deletions tests/service/ParserTests.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
module Tests.Parser

open FSharp.Compiler.Service.Tests.Common
open FSharp.Compiler.SyntaxTree
open NUnit.Framework

module Recovery =
[<Test>]
let ``Unfinished interface member`` () =
let parseResults = getParseResults """
type T =
interface I with
member x.P2 = ()
let x = ()
"""
let (SynModuleOrNamespace (decls = decls)) = getSingleModuleLikeDecl parseResults
match decls with
| [ SynModuleDecl.Types ([ TypeDefn (typeRepr = SynTypeDefnRepr.ObjectModel (members = [ _; _ ])) ], _)
SynModuleDecl.Let _ ] -> ()
| _ -> failwith "Unexpected tree"

0 comments on commit 0855b78

Please sign in to comment.