From 807e6f07fa6edfdaf18936c0337afed15840eea0 Mon Sep 17 00:00:00 2001 From: Eugene Auduchinok Date: Wed, 21 Jun 2023 14:55:32 +0200 Subject: [PATCH 1/4] Checker: recover on unfinished module and member declarations --- src/Compiler/Checking/CheckDeclarations.fs | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/Compiler/Checking/CheckDeclarations.fs b/src/Compiler/Checking/CheckDeclarations.fs index a37c39a7d4..66cd6cc992 100644 --- a/src/Compiler/Checking/CheckDeclarations.fs +++ b/src/Compiler/Checking/CheckDeclarations.fs @@ -914,7 +914,10 @@ module MutRecBindingChecking = // Phase2A: let-bindings - pass through let innerState = (incrClassCtorLhsOpt, envForTycon, tpenv, recBindIdx, uncheckedBindsRev) [Phase2AIncrClassBindings (tcref, letBinds, isStatic, isRec, m)], innerState - + + | SynMemberDefn.Member(SynBinding(headPat = SynPat.FromParseError(SynPat.Wild _, _)), _), _ -> + [], innerState + | SynMemberDefn.Member (bind, m), _ -> // Phase2A: member binding - create prelim valspec (for recursive reference) and RecursiveBindingInfo let NormalizedBinding(_, _, _, _, _, _, _, valSynData, _, _, _, _) as bind = BindingNormalization.NormalizeBinding ValOrMemberBinding cenv envForTycon bind @@ -4882,6 +4885,9 @@ let rec TcModuleOrNamespaceElementNonMutRec (cenv: cenv) parent typeNames scopem | SynModuleDecl.HashDirective _ -> return ([], [], []), env, env + | SynModuleDecl.NestedModule(moduleInfo = (SynComponentInfo(longId = []))) -> + return ([], [], []), env, env + | SynModuleDecl.NestedModule(compInfo, isRec, moduleDefs, isContinuingModule, m, trivia) -> // Treat 'module rec M = ...' as a single mutually recursive definition group 'module M = ...' @@ -4976,9 +4982,13 @@ let rec TcModuleOrNamespaceElementNonMutRec (cenv: cenv) parent typeNames scopem // to // namespace [rec] A.B // module M = ... - let enclosingNamespacePath, defs = - if kind.IsModule then - let nsp, modName = List.frontAndBack longId + let enclosingNamespacePath, defs = + if kind.IsModule then + let nsp, modName = + if not longId.IsEmpty then + List.frontAndBack longId + else + [], mkSynId m.EndRange "" let modDecl = [SynModuleDecl.NestedModule(SynComponentInfo(attribs, None, [], [modName], xml, false, vis, m), false, defs, true, m, SynModuleDeclNestedModuleTrivia.Zero)] nsp, modDecl else @@ -5079,6 +5089,9 @@ and TcModuleOrNamespaceElementsMutRec (cenv: cenv) parent typeNames m envInitial else List.map (List.singleton >> MutRecShape.Lets) binds binds, (false, false, attrs) + | SynModuleDecl.NestedModule(moduleInfo = (SynComponentInfo(longId = []))) -> + [], (openOk, moduleAbbrevOk, attrs) + | SynModuleDecl.NestedModule(moduleInfo=compInfo; isRecursive=isRec; decls=synDefs; range=moduleRange) -> if isRec then warning(Error(FSComp.SR.tcRecImplied(), compInfo.Range)) let mutRecDefs, (_, _, attrs) = loop false moduleRange attrs synDefs From c10df52f546373e8cb405ff1edbad078844f6b98 Mon Sep 17 00:00:00 2001 From: Eugene Auduchinok Date: Wed, 21 Jun 2023 14:56:53 +0200 Subject: [PATCH 2/4] Tests/syntax tree: check file to assert type checker doesn't fail --- tests/FSharp.Test.Utilities/CompilerAssert.fs | 5 ++++- tests/service/SyntaxTreeTests.fs | 4 ++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/tests/FSharp.Test.Utilities/CompilerAssert.fs b/tests/FSharp.Test.Utilities/CompilerAssert.fs index f209beab7d..f9af77086d 100644 --- a/tests/FSharp.Test.Utilities/CompilerAssert.fs +++ b/tests/FSharp.Test.Utilities/CompilerAssert.fs @@ -336,6 +336,9 @@ module rec CompilerAssertHelpers = Stamp = None } + let defaultProjectOptionsForFilePath path (targetFramework: TargetFramework) = + { defaultProjectOptions targetFramework with SourceFiles = [| path |] } + let rawCompile outputFilePath isExe options (targetFramework: TargetFramework) (sources: SourceCodeFileKind list) = let args = [| @@ -804,7 +807,7 @@ Updated automatically, please check diffs in your pull request, changes must be /// Parses and type checks the given source. Fails if type checker is aborted. static member ParseAndTypeCheck(options, name, source: string) = let parseResults, fileAnswer = - let defaultOptions = defaultProjectOptions TargetFramework.Current + let defaultOptions = defaultProjectOptionsForFilePath name TargetFramework.Current checker.ParseAndCheckFileInProject( name, 0, diff --git a/tests/service/SyntaxTreeTests.fs b/tests/service/SyntaxTreeTests.fs index 6de10a235b..b72b20d77d 100644 --- a/tests/service/SyntaxTreeTests.fs +++ b/tests/service/SyntaxTreeTests.fs @@ -6,6 +6,7 @@ open FSharp.Compiler.Diagnostics open FSharp.Compiler.Service.Tests.Common open FSharp.Compiler.Syntax open FSharp.Compiler.Text +open FSharp.Test open NUnit.Framework let testCasesDir = Path.Combine(__SOURCE_DIRECTORY__, "data", "SyntaxTree") @@ -197,3 +198,6 @@ let ParseFile fileName = File.Delete(actualPath) Assert.AreEqual(expected, actual) + + // Run type checker to assert that it doesn't fail with the tree produced by the parser + CompilerAssert.ParseAndTypeCheck([|"--langversion:preview"|], fileName, contents) |> ignore From 3258520c5145580e3ca97b929f82c4a207cb8c17 Mon Sep 17 00:00:00 2001 From: Eugene Auduchinok Date: Wed, 21 Jun 2023 16:07:40 +0200 Subject: [PATCH 3/4] More tests --- .../data/SyntaxTree/Expression/Id 01.fs | 3 ++ .../data/SyntaxTree/Expression/Id 01.fs.bsl | 9 ++++ .../data/SyntaxTree/Expression/Id 02.fs | 4 ++ .../data/SyntaxTree/Expression/Id 02.fs.bsl | 12 +++++ .../data/SyntaxTree/Expression/Id 03.fs | 4 ++ .../data/SyntaxTree/Expression/Id 03.fs.bsl | 13 +++++ .../data/SyntaxTree/Expression/Id 04.fs | 4 ++ .../data/SyntaxTree/Expression/Id 04.fs.bsl | 13 +++++ .../data/SyntaxTree/Expression/Id 05.fs | 3 ++ .../data/SyntaxTree/Expression/Id 05.fs.bsl | 15 ++++++ .../data/SyntaxTree/Expression/Id 06.fs | 1 + .../data/SyntaxTree/Expression/Id 06.fs.bsl | 12 +++++ .../data/SyntaxTree/Expression/Id 07.fs | 1 + .../data/SyntaxTree/Expression/Id 07.fs.bsl | 12 +++++ .../data/SyntaxTree/Member/Member 09.fs | 6 +++ .../data/SyntaxTree/Member/Member 09.fs.bsl | 48 +++++++++++++++++++ 16 files changed, 160 insertions(+) create mode 100644 tests/service/data/SyntaxTree/Expression/Id 01.fs create mode 100644 tests/service/data/SyntaxTree/Expression/Id 01.fs.bsl create mode 100644 tests/service/data/SyntaxTree/Expression/Id 02.fs create mode 100644 tests/service/data/SyntaxTree/Expression/Id 02.fs.bsl create mode 100644 tests/service/data/SyntaxTree/Expression/Id 03.fs create mode 100644 tests/service/data/SyntaxTree/Expression/Id 03.fs.bsl create mode 100644 tests/service/data/SyntaxTree/Expression/Id 04.fs create mode 100644 tests/service/data/SyntaxTree/Expression/Id 04.fs.bsl create mode 100644 tests/service/data/SyntaxTree/Expression/Id 05.fs create mode 100644 tests/service/data/SyntaxTree/Expression/Id 05.fs.bsl create mode 100644 tests/service/data/SyntaxTree/Expression/Id 06.fs create mode 100644 tests/service/data/SyntaxTree/Expression/Id 06.fs.bsl create mode 100644 tests/service/data/SyntaxTree/Expression/Id 07.fs create mode 100644 tests/service/data/SyntaxTree/Expression/Id 07.fs.bsl create mode 100644 tests/service/data/SyntaxTree/Member/Member 09.fs create mode 100644 tests/service/data/SyntaxTree/Member/Member 09.fs.bsl diff --git a/tests/service/data/SyntaxTree/Expression/Id 01.fs b/tests/service/data/SyntaxTree/Expression/Id 01.fs new file mode 100644 index 0000000000..b88fd9f4bf --- /dev/null +++ b/tests/service/data/SyntaxTree/Expression/Id 01.fs @@ -0,0 +1,3 @@ +module Module + +a diff --git a/tests/service/data/SyntaxTree/Expression/Id 01.fs.bsl b/tests/service/data/SyntaxTree/Expression/Id 01.fs.bsl new file mode 100644 index 0000000000..e24dcc6b23 --- /dev/null +++ b/tests/service/data/SyntaxTree/Expression/Id 01.fs.bsl @@ -0,0 +1,9 @@ +ImplFile + (ParsedImplFileInput + ("/root/Expression/Id 01.fs", false, QualifiedNameOfFile Module, [], [], + [SynModuleOrNamespace + ([Module], false, NamedModule, [Expr (Ident a, (3,0--3,1))], + PreXmlDoc ((1,0), FSharp.Compiler.Xml.XmlDocCollector), [], None, + (1,0--3,1), { LeadingKeyword = Module (1,0--1,6) })], (true, true), + { ConditionalDirectives = [] + CodeComments = [] }, set [])) diff --git a/tests/service/data/SyntaxTree/Expression/Id 02.fs b/tests/service/data/SyntaxTree/Expression/Id 02.fs new file mode 100644 index 0000000000..d8937204f7 --- /dev/null +++ b/tests/service/data/SyntaxTree/Expression/Id 02.fs @@ -0,0 +1,4 @@ +module Module + +``a +b diff --git a/tests/service/data/SyntaxTree/Expression/Id 02.fs.bsl b/tests/service/data/SyntaxTree/Expression/Id 02.fs.bsl new file mode 100644 index 0000000000..d46a4254b2 --- /dev/null +++ b/tests/service/data/SyntaxTree/Expression/Id 02.fs.bsl @@ -0,0 +1,12 @@ +ImplFile + (ParsedImplFileInput + ("/root/Expression/Id 02.fs", false, QualifiedNameOfFile Module, [], [], + [SynModuleOrNamespace + ([Module], false, NamedModule, + [Expr (Ident a, (3,0--3,3)); Expr (Ident b, (4,0--4,1))], + PreXmlDoc ((1,0), FSharp.Compiler.Xml.XmlDocCollector), [], None, + (1,0--4,1), { LeadingKeyword = Module (1,0--1,6) })], (true, true), + { ConditionalDirectives = [] + CodeComments = [] }, set [])) + +(3,0)-(3,3) parse error This is not a valid identifier diff --git a/tests/service/data/SyntaxTree/Expression/Id 03.fs b/tests/service/data/SyntaxTree/Expression/Id 03.fs new file mode 100644 index 0000000000..5bcb51b403 --- /dev/null +++ b/tests/service/data/SyntaxTree/Expression/Id 03.fs @@ -0,0 +1,4 @@ +module Module + +`` +b diff --git a/tests/service/data/SyntaxTree/Expression/Id 03.fs.bsl b/tests/service/data/SyntaxTree/Expression/Id 03.fs.bsl new file mode 100644 index 0000000000..26ef9dd71d --- /dev/null +++ b/tests/service/data/SyntaxTree/Expression/Id 03.fs.bsl @@ -0,0 +1,13 @@ +ImplFile + (ParsedImplFileInput + ("/root/Expression/Id 03.fs", false, QualifiedNameOfFile Module, [], [], + [SynModuleOrNamespace + ([Module], false, NamedModule, + [Expr (FromParseError (Ident , (3,0--3,2)), (3,0--3,2)); + Expr (Ident b, (4,0--4,1))], + PreXmlDoc ((1,0), FSharp.Compiler.Xml.XmlDocCollector), [], None, + (1,0--4,1), { LeadingKeyword = Module (1,0--1,6) })], (true, true), + { ConditionalDirectives = [] + CodeComments = [] }, set [])) + +(3,0)-(3,2) parse error This is not a valid identifier diff --git a/tests/service/data/SyntaxTree/Expression/Id 04.fs b/tests/service/data/SyntaxTree/Expression/Id 04.fs new file mode 100644 index 0000000000..85ebc6fa61 --- /dev/null +++ b/tests/service/data/SyntaxTree/Expression/Id 04.fs @@ -0,0 +1,4 @@ +module Module + +` +b diff --git a/tests/service/data/SyntaxTree/Expression/Id 04.fs.bsl b/tests/service/data/SyntaxTree/Expression/Id 04.fs.bsl new file mode 100644 index 0000000000..c76189328b --- /dev/null +++ b/tests/service/data/SyntaxTree/Expression/Id 04.fs.bsl @@ -0,0 +1,13 @@ +ImplFile + (ParsedImplFileInput + ("/root/Expression/Id 04.fs", false, QualifiedNameOfFile Module, [], [], + [SynModuleOrNamespace + ([Module], false, NamedModule, + [Expr (FromParseError (Ident , (3,0--3,1)), (3,0--3,1)); + Expr (Ident b, (4,0--4,1))], + PreXmlDoc ((1,0), FSharp.Compiler.Xml.XmlDocCollector), [], None, + (1,0--4,1), { LeadingKeyword = Module (1,0--1,6) })], (true, true), + { ConditionalDirectives = [] + CodeComments = [] }, set [])) + +(3,0)-(3,1) parse error This is not a valid identifier diff --git a/tests/service/data/SyntaxTree/Expression/Id 05.fs b/tests/service/data/SyntaxTree/Expression/Id 05.fs new file mode 100644 index 0000000000..90202cb7b4 --- /dev/null +++ b/tests/service/data/SyntaxTree/Expression/Id 05.fs @@ -0,0 +1,3 @@ +module Module + +`b diff --git a/tests/service/data/SyntaxTree/Expression/Id 05.fs.bsl b/tests/service/data/SyntaxTree/Expression/Id 05.fs.bsl new file mode 100644 index 0000000000..2e3f9d5568 --- /dev/null +++ b/tests/service/data/SyntaxTree/Expression/Id 05.fs.bsl @@ -0,0 +1,15 @@ +ImplFile + (ParsedImplFileInput + ("/root/Expression/Id 05.fs", false, QualifiedNameOfFile Module, [], [], + [SynModuleOrNamespace + ([Module], false, NamedModule, + [Expr + (App + (NonAtomic, false, FromParseError (Ident , (3,0--3,1)), Ident b, + (3,0--3,2)), (3,0--3,2))], + PreXmlDoc ((1,0), FSharp.Compiler.Xml.XmlDocCollector), [], None, + (1,0--3,2), { LeadingKeyword = Module (1,0--1,6) })], (true, true), + { ConditionalDirectives = [] + CodeComments = [] }, set [])) + +(3,0)-(3,1) parse error This is not a valid identifier diff --git a/tests/service/data/SyntaxTree/Expression/Id 06.fs b/tests/service/data/SyntaxTree/Expression/Id 06.fs new file mode 100644 index 0000000000..cf637de588 --- /dev/null +++ b/tests/service/data/SyntaxTree/Expression/Id 06.fs @@ -0,0 +1 @@ +`` diff --git a/tests/service/data/SyntaxTree/Expression/Id 06.fs.bsl b/tests/service/data/SyntaxTree/Expression/Id 06.fs.bsl new file mode 100644 index 0000000000..bade7e896e --- /dev/null +++ b/tests/service/data/SyntaxTree/Expression/Id 06.fs.bsl @@ -0,0 +1,12 @@ +ImplFile + (ParsedImplFileInput + ("/root/Expression/Id 06.fs", false, QualifiedNameOfFile Id 06, [], [], + [SynModuleOrNamespace + ([Id 06], false, AnonModule, + [Expr (FromParseError (Ident , (1,0--1,2)), (1,0--1,2))], + PreXmlDocEmpty, [], None, (1,0--1,2), { LeadingKeyword = None })], + (true, true), { ConditionalDirectives = [] + CodeComments = [] }, set [])) + +(1,0)-(1,2) parse error This is not a valid identifier +(1,0)-(1,2) parse warning The declarations in this file will be placed in an implicit module 'Id 06' based on the file name 'Id 06.fs'. However this is not a valid F# identifier, so the contents will not be accessible from other files. Consider renaming the file or adding a 'module' or 'namespace' declaration at the top of the file. diff --git a/tests/service/data/SyntaxTree/Expression/Id 07.fs b/tests/service/data/SyntaxTree/Expression/Id 07.fs new file mode 100644 index 0000000000..b37663f19a --- /dev/null +++ b/tests/service/data/SyntaxTree/Expression/Id 07.fs @@ -0,0 +1 @@ +` diff --git a/tests/service/data/SyntaxTree/Expression/Id 07.fs.bsl b/tests/service/data/SyntaxTree/Expression/Id 07.fs.bsl new file mode 100644 index 0000000000..60d8974e78 --- /dev/null +++ b/tests/service/data/SyntaxTree/Expression/Id 07.fs.bsl @@ -0,0 +1,12 @@ +ImplFile + (ParsedImplFileInput + ("/root/Expression/Id 07.fs", false, QualifiedNameOfFile Id 07, [], [], + [SynModuleOrNamespace + ([Id 07], false, AnonModule, + [Expr (FromParseError (Ident , (1,0--1,1)), (1,0--1,1))], + PreXmlDocEmpty, [], None, (1,0--1,1), { LeadingKeyword = None })], + (true, true), { ConditionalDirectives = [] + CodeComments = [] }, set [])) + +(1,0)-(1,1) parse error This is not a valid identifier +(1,0)-(1,1) parse warning The declarations in this file will be placed in an implicit module 'Id 07' based on the file name 'Id 07.fs'. However this is not a valid F# identifier, so the contents will not be accessible from other files. Consider renaming the file or adding a 'module' or 'namespace' declaration at the top of the file. diff --git a/tests/service/data/SyntaxTree/Member/Member 09.fs b/tests/service/data/SyntaxTree/Member/Member 09.fs new file mode 100644 index 0000000000..a732d06783 --- /dev/null +++ b/tests/service/data/SyntaxTree/Member/Member 09.fs @@ -0,0 +1,6 @@ +module Module + +type U = + | A + + member diff --git a/tests/service/data/SyntaxTree/Member/Member 09.fs.bsl b/tests/service/data/SyntaxTree/Member/Member 09.fs.bsl new file mode 100644 index 0000000000..e101bf1b6d --- /dev/null +++ b/tests/service/data/SyntaxTree/Member/Member 09.fs.bsl @@ -0,0 +1,48 @@ +ImplFile + (ParsedImplFileInput + ("/root/Member/Member 09.fs", false, QualifiedNameOfFile Module, [], [], + [SynModuleOrNamespace + ([Module], false, NamedModule, + [Types + ([SynTypeDefn + (SynComponentInfo + ([], None, [], [U], + PreXmlDoc ((3,0), FSharp.Compiler.Xml.XmlDocCollector), + false, None, (3,5--3,6)), + Simple + (Union + (None, + [SynUnionCase + ([], SynIdent (A, None), Fields [], + PreXmlDoc ((4,4), FSharp.Compiler.Xml.XmlDocCollector), + None, (4,6--4,7), { BarRange = Some (4,4--4,5) })], + (4,4--4,7)), (4,4--4,7)), + [Member + (SynBinding + (None, Normal, false, false, [], + PreXmlDoc ((6,4), FSharp.Compiler.Xml.XmlDocCollector), + SynValData + (Some { IsInstance = true + IsDispatchSlot = false + IsOverrideOrExplicitImpl = false + IsFinal = false + GetterOrSetterIsCompilerGenerated = false + MemberKind = Member }, + SynValInfo + ([[SynArgInfo ([], false, None)]; []], + SynArgInfo ([], false, None)), None), + FromParseError (Wild (6,10--6,10), (6,10--6,10)), None, + ArbitraryAfterError ("classDefnMember1", (6,10--6,10)), + (6,4--6,10), NoneAtInvisible, + { LeadingKeyword = Member (6,4--6,10) + InlineKeyword = None + EqualsRange = None }), (6,4--6,10))], None, + (3,5--6,10), { LeadingKeyword = Type (3,0--3,4) + EqualsRange = Some (3,7--3,8) + WithKeyword = None })], (3,0--6,10))], + PreXmlDoc ((1,0), FSharp.Compiler.Xml.XmlDocCollector), [], None, + (1,0--6,10), { LeadingKeyword = Module (1,0--1,6) })], (true, true), + { ConditionalDirectives = [] + CodeComments = [] }, set [])) + +(7,0)-(7,0) parse error Incomplete structured construct at or before this point in member definition From a01333d9c8e4a571dc5516b8b023935c9d718aa1 Mon Sep 17 00:00:00 2001 From: Eugene Auduchinok Date: Wed, 21 Jun 2023 16:12:35 +0200 Subject: [PATCH 4/4] Formatting --- src/Compiler/Checking/CheckDeclarations.fs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Compiler/Checking/CheckDeclarations.fs b/src/Compiler/Checking/CheckDeclarations.fs index 66cd6cc992..7d254fb838 100644 --- a/src/Compiler/Checking/CheckDeclarations.fs +++ b/src/Compiler/Checking/CheckDeclarations.fs @@ -4985,10 +4985,9 @@ let rec TcModuleOrNamespaceElementNonMutRec (cenv: cenv) parent typeNames scopem let enclosingNamespacePath, defs = if kind.IsModule then let nsp, modName = - if not longId.IsEmpty then - List.frontAndBack longId - else - [], mkSynId m.EndRange "" + match longId with + | [] -> [], mkSynId m.EndRange "" + | _ -> List.frontAndBack longId let modDecl = [SynModuleDecl.NestedModule(SynComponentInfo(attribs, None, [], [modName], xml, false, vis, m), false, defs, true, m, SynModuleDeclNestedModuleTrivia.Zero)] nsp, modDecl else