diff --git a/Dockerfile b/Dockerfile
index bf5b7d7..8a82b39 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,4 +1,4 @@
-FROM mcr.microsoft.com/dotnet/sdk:7.0.401-alpine3.18-amd64 AS build
+FROM mcr.microsoft.com/dotnet/sdk:8.0.201-alpine3.19-amd64 AS build
WORKDIR /tmp
@@ -19,6 +19,7 @@ RUN dotnet add package FsCheck -v 2.16.3
RUN dotnet add package FsCheck.Xunit -v 2.14.3
RUN dotnet add package FSharp.Core -v 6.0.1
RUN dotnet add package FSharp.Core -v 7.0.400
+RUN dotnet add package FSharp.Core -v 8.0.101
RUN dotnet add package FParsec -v 1.1.1
WORKDIR /app
@@ -32,7 +33,7 @@ COPY src/Exercism.TestRunner.FSharp/ ./
RUN dotnet publish -r linux-musl-x64 -c Release -o /opt/test-runner --no-restore --self-contained true
# Build runtime image
-FROM mcr.microsoft.com/dotnet/sdk:7.0.401-alpine3.18-amd64 AS runtime
+FROM mcr.microsoft.com/dotnet/sdk:8.0.201-alpine3.19-amd64 AS runtime
WORKDIR /opt/test-runner
# Enable rolling forward the .NET SDK used to be backwards-compatible
diff --git a/src/Exercism.TestRunner.FSharp/Exercism.TestRunner.FSharp.fsproj b/src/Exercism.TestRunner.FSharp/Exercism.TestRunner.FSharp.fsproj
index 5ab531a..3b3ccf5 100644
--- a/src/Exercism.TestRunner.FSharp/Exercism.TestRunner.FSharp.fsproj
+++ b/src/Exercism.TestRunner.FSharp/Exercism.TestRunner.FSharp.fsproj
@@ -2,7 +2,7 @@
Exe
- net7.0
+ net8.0
@@ -20,11 +20,10 @@
-
-
+
+
-
-
+
diff --git a/src/Exercism.TestRunner.FSharp/Rewrite.fs b/src/Exercism.TestRunner.FSharp/Rewrite.fs
index b8566f3..0cf2379 100644
--- a/src/Exercism.TestRunner.FSharp/Rewrite.fs
+++ b/src/Exercism.TestRunner.FSharp/Rewrite.fs
@@ -3,9 +3,9 @@ module Exercism.TestRunner.FSharp.Rewrite
open System.IO
open Exercism.TestRunner.FSharp.Core
open Exercism.TestRunner.FSharp.Visitor
-open FSharp.Compiler.Syntax
-open FSharp.Compiler.Text
open Fantomas.Core
+open Fantomas.FCS.Syntax
+open Fantomas.FCS.Text
open Fantomas.FCS.Parse
type ParseResult =
@@ -18,13 +18,6 @@ type RewriteResult =
type EnableAllTests() =
inherit SyntaxVisitor()
-
- default this.VisitSynAttributeList(attrs: SynAttributeList) : SynAttributeList =
- base.VisitSynAttributeList(
- { attrs with
- Attributes =
- attrs.Attributes
- |> List.filter (fun attr -> attr.TypeName.LongIdent.Head.idText <> "Ignore") })
override _.VisitSynAttribute(attr: SynAttribute) : SynAttribute =
let isSkipExpr expr =
@@ -33,32 +26,42 @@ type EnableAllTests() =
| _ -> false
match attr.ArgExpr with
- | SynExpr.Paren(expr, leftParenRange, rightParenRange, range) ->
+ | SynExpr.Paren(expr, leftParenRange, rightParenRange, range) ->
match expr with
- | SynExpr.App _ when isSkipExpr expr ->
- let newExpr = SynExpr.Const(SynConst.Unit, attr.ArgExpr.Range)
- base.VisitSynAttribute({ attr with ArgExpr = newExpr })
+ | SynExpr.App _ when isSkipExpr(expr) ->
+ let noAttributesArgExpr = SynExpr.Const(SynConst.Unit, attr.ArgExpr.Range)
+ base.VisitSynAttribute({ attr with ArgExpr = noAttributesArgExpr })
| SynExpr.Tuple(iStruct, exprs, commaRanges, tplRange) ->
- let newExpr =
- SynExpr.Paren(
- SynExpr.Tuple(iStruct, exprs |> List.filter (isSkipExpr >> not), commaRanges, tplRange), leftParenRange, rightParenRange, range)
- base.VisitSynAttribute({ attr with ArgExpr = newExpr })
+ match List.tryFindIndex isSkipExpr exprs with
+ | Some index ->
+ let newExpr =
+ SynExpr.Paren(
+ SynExpr.Tuple(
+ iStruct,
+ exprs |> List.removeAt index,
+ commaRanges |> List.removeAt (index - 1), tplRange
+ ),
+ leftParenRange,
+ rightParenRange,
+ range)
+ base.VisitSynAttribute({ attr with ArgExpr = newExpr })
+ | None -> base.VisitSynAttribute(attr)
| _ -> base.VisitSynAttribute(attr)
| _ -> base.VisitSynAttribute(attr)
let private parseFile (filePath: string) =
if File.Exists(filePath) then
let source = File.ReadAllText(filePath)
- let sourceText = source |> SourceText.ofString
- let tree, diagnostics = parseFile false sourceText []
- Some tree // TODO: use diagnostics to determine success
+ let sourceText = source |> SourceText.ofString
+ let tree, _ = CodeFormatter.ParseAsync(false, source) |> Async.RunSynchronously |> Array.head
+ Some tree
|> Option.map (fun tree -> ParseSuccess(source, sourceText, tree))
|> Option.defaultValue ParseError
else
ParseError
-let private toCode code tree =
- CodeFormatter.FormatASTAsync(tree, code, FormatConfig.FormatConfig.Default)
+let private toCode tree =
+ CodeFormatter.FormatASTAsync(tree)
|> Async.RunSynchronously
|> SourceText.ofString
@@ -67,12 +70,17 @@ let private enableAllTests parsedInput =
let private rewriteProjectFile (context: TestRunContext) =
let originalProjectFile = File.ReadAllText(context.ProjectFile)
- originalProjectFile, originalProjectFile.Replace("net5.0", "net6.0").Replace("net6.0", "net7.0")
+ let rewrittenProjectFile =
+ originalProjectFile
+ .Replace("net5.0", "net8.0")
+ .Replace("net6.0", "net8.0")
+ .Replace("net7.0", "net8.0")
+ originalProjectFile, rewrittenProjectFile
let rewriteTests (context: TestRunContext) =
match parseFile context.TestsFile with
- | ParseSuccess (originalSource, originalSourceText, originalTestTree) ->
- let rewrittenTestCode = originalTestTree |> enableAllTests |> toCode originalSource
- let (originalProjectFile, rewrittenProjectFile) = rewriteProjectFile context
+ | ParseSuccess (originalSource, originalSourceText, originalTestTree) ->
+ let rewrittenTestCode = originalTestTree |> enableAllTests |> toCode
+ let originalProjectFile, rewrittenProjectFile = rewriteProjectFile context
RewriteSuccess(originalSourceText, originalTestTree, rewrittenTestCode, originalProjectFile, rewrittenProjectFile)
| ParseError -> RewriteError
diff --git a/src/Exercism.TestRunner.FSharp/Testing.fs b/src/Exercism.TestRunner.FSharp/Testing.fs
index 1fc2f98..5e44883 100644
--- a/src/Exercism.TestRunner.FSharp/Testing.fs
+++ b/src/Exercism.TestRunner.FSharp/Testing.fs
@@ -1,6 +1,5 @@
module Exercism.TestRunner.FSharp.Testing
-open System
open System.Diagnostics
open System.IO
open System.Text.RegularExpressions
@@ -8,8 +7,8 @@ open System.Xml.Serialization
open Exercism.TestRunner.FSharp.Core
open Exercism.TestRunner.FSharp.Rewrite
open Exercism.TestRunner.FSharp.Visitor
-open FSharp.Compiler.Syntax
-open FSharp.Compiler.Text
+open Fantomas.FCS.Syntax
+open Fantomas.FCS.Text
module String =
let normalize (str: string) = str.Replace("\r\n", "\n").Trim()
@@ -17,7 +16,7 @@ module String =
let isNullOrWhiteSpace = System.String.IsNullOrWhiteSpace
module Process =
- let exec fileName arguments workingDirectory =
+ let exec (fileName: string) (arguments: string) workingDirectory =
let psi = ProcessStartInfo(fileName, arguments)
psi.WorkingDirectory <- workingDirectory
psi.RedirectStandardInput <- true
@@ -257,7 +256,9 @@ module DotnetCli =
let runTests originalTestCode originalTestTree context =
let solutionDir = Path.GetDirectoryName(context.TestsFile)
- Process.exec "dotnet" $"test --verbosity=quiet --logger \"trx;LogFileName=%s{Path.GetFileName(context.TestResultsFile)}\" /flp:v=q" solutionDir
+
+ Process.exec "dotnet" "restore --source /root/.nuget/packages/" solutionDir
+ Process.exec "dotnet" $"test --no-restore --verbosity=quiet --logger \"trx;LogFileName=%s{Path.GetFileName(context.TestResultsFile)}\" /flp:v=q" solutionDir
let buildErrors = parseBuildErrors context
diff --git a/src/Exercism.TestRunner.FSharp/Visitor.fs b/src/Exercism.TestRunner.FSharp/Visitor.fs
index fe5069d..ec61a7c 100644
--- a/src/Exercism.TestRunner.FSharp/Visitor.fs
+++ b/src/Exercism.TestRunner.FSharp/Visitor.fs
@@ -1,22 +1,22 @@
module Exercism.TestRunner.FSharp.Visitor
-open FSharp.Compiler.Syntax
-open FSharp.Compiler.SyntaxTrivia
-open FSharp.Compiler.Text
-open FSharp.Compiler.Xml
+open Fantomas.FCS.Syntax
+open Fantomas.FCS.SyntaxTrivia
+open Fantomas.FCS.Text
+open Fantomas.FCS.Xml
type SyntaxVisitor() =
abstract VisitInput: ParsedInput -> ParsedInput
default this.VisitInput(input: ParsedInput): ParsedInput =
match input with
- | ParsedInput.ImplFile(ParsedImplFileInput(file, isScript, qualName, pragmas, hashDirectives, modules, isLastCompiland, trivia)) ->
+ | ParsedInput.ImplFile(ParsedImplFileInput(file, isScript, qualName, pragmas, hashDirectives, modules, isLastCompiland, trivia, identifiers)) ->
ParsedInput.ImplFile
(ParsedImplFileInput
(file, isScript, qualName, pragmas, hashDirectives,
- List.map this.VisitSynModuleOrNamespace modules, isLastCompiland, this.VisitParsedImplFileInputTrivia(trivia)))
- | ParsedInput.SigFile(ParsedSigFileInput(file, qualifiedName, pragmas, directives, synModuleOrNamespaceSigs, trivia)) ->
- ParsedInput.SigFile(ParsedSigFileInput(file, qualifiedName, pragmas, directives, synModuleOrNamespaceSigs, trivia))
+ List.map this.VisitSynModuleOrNamespace modules, isLastCompiland, this.VisitParsedImplFileInputTrivia(trivia), identifiers))
+ | ParsedInput.SigFile(ParsedSigFileInput(file, qualifiedName, pragmas, directives, synModuleOrNamespaceSigs, trivia, identifiers)) ->
+ ParsedInput.SigFile(ParsedSigFileInput(file, qualifiedName, pragmas, directives, synModuleOrNamespaceSigs, trivia, identifiers))
abstract VisitParsedImplFileInputTrivia : ParsedImplFileInputTrivia -> ParsedImplFileInputTrivia
default this.VisitParsedImplFileInputTrivia(trivia: ParsedImplFileInputTrivia) = trivia
@@ -108,10 +108,10 @@ type SyntaxVisitor() =
(this.VisitSynType typ, this.VisitSynExpr expr, leftParenRange, sep, rightParentRange)),
copyInfo |> Option.map (fun (expr, opt) -> (this.VisitSynExpr expr, opt)),
recordFields |> List.map this.VisitRecordField, range)
- | SynExpr.AnonRecd(isStruct, copyInfo, recordFields, range) ->
+ | SynExpr.AnonRecd(isStruct, copyInfo, recordFields, range, trivia) ->
SynExpr.AnonRecd
(isStruct, copyInfo |> Option.map (fun (expr, opt) -> (this.VisitSynExpr expr, opt)),
- recordFields |> List.map this.VisitAnonRecordField, range)
+ recordFields |> List.map this.VisitAnonRecordField, range, trivia)
| SynExpr.New(isProtected, typeName, expr, range) ->
SynExpr.New(isProtected, this.VisitSynType typeName, this.VisitSynExpr expr, range)
| SynExpr.ObjExpr(objType, argOptions, withKeyword, bindings, members, extraImpls, newExprRange, range) ->
@@ -159,8 +159,8 @@ type SyntaxVisitor() =
SynExpr.TryFinally
(this.VisitSynExpr tryExpr, this.VisitSynExpr finallyExpr, range, trySeqPoint, withSeqPoint, trivia)
| SynExpr.Lazy(ex, range) -> SynExpr.Lazy(this.VisitSynExpr ex, range)
- | SynExpr.Sequential(seqPoint, isTrueSeq, expr1, expr2, range) ->
- SynExpr.Sequential(seqPoint, isTrueSeq, this.VisitSynExpr expr1, this.VisitSynExpr expr2, range)
+ | SynExpr.Sequential(seqPoint, isTrueSeq, expr1, expr2, range, trivia) ->
+ SynExpr.Sequential(seqPoint, isTrueSeq, this.VisitSynExpr expr1, this.VisitSynExpr expr2, range, trivia)
| SynExpr.SequentialOrImplicitYield(seqPoint, expr1, expr2, ifNotStmt, range) ->
SynExpr.SequentialOrImplicitYield
(seqPoint, this.VisitSynExpr expr1, this.VisitSynExpr expr2, this.VisitSynExpr ifNotStmt, range)
@@ -233,11 +233,13 @@ type SyntaxVisitor() =
(this.VisitSynExpr e1, this.VisitLongIdent longId, i, this.VisitSynExpr e2, range)
| SynExpr.ArbitraryAfterError(debugStr, range) -> SynExpr.ArbitraryAfterError(debugStr, range)
| SynExpr.FromParseError(expr, range) -> SynExpr.FromParseError(this.VisitSynExpr expr, range)
- | SynExpr.DiscardAfterMissingQualificationAfterDot(expr, range) ->
- SynExpr.DiscardAfterMissingQualificationAfterDot(this.VisitSynExpr expr, range)
+ | SynExpr.DiscardAfterMissingQualificationAfterDot(expr, dotRange, range) ->
+ SynExpr.DiscardAfterMissingQualificationAfterDot(this.VisitSynExpr expr, dotRange, range)
| SynExpr.Fixed(expr, range) -> SynExpr.Fixed(this.VisitSynExpr expr, range)
| SynExpr.InterpolatedString(contents, kind, range) -> SynExpr.InterpolatedString(List.map this.VisitSynInterpolatedStringPart contents, kind, range)
| SynExpr.Typar(synTypar, range) -> SynExpr.Typar(this.VisitSynTypar synTypar, range)
+ | SynExpr.DotLambda(expr, range, trivia) -> SynExpr.DotLambda(this.VisitSynExpr expr, range, trivia)
+ | SynExpr.WhileBang(whileDebugPoint, whileExpr, doExpr, range) -> SynExpr.WhileBang(whileDebugPoint, this.VisitSynExpr whileExpr, this.VisitSynExpr doExpr, range)
abstract VisitSynInterpolatedStringPart: SynInterpolatedStringPart -> SynInterpolatedStringPart
@@ -255,8 +257,8 @@ type SyntaxVisitor() =
abstract VisitRecordFieldName: RecordFieldName -> RecordFieldName
default this.VisitRecordFieldName((ident: SynLongIdent, correct: bool)) = (this.VisitSynLongIdent ident, correct)
- abstract VisitAnonRecordField: (Ident * range option * SynExpr) -> Ident * range option * SynExpr
- default this.VisitAnonRecordField((ident: Ident, r: range option, expr: SynExpr)) = (this.VisitIdent ident, r, this.VisitSynExpr expr)
+ abstract VisitAnonRecordField: (SynLongIdent * range option * SynExpr) -> SynLongIdent * range option * SynExpr
+ default this.VisitAnonRecordField((ident: SynLongIdent, r: range option, expr: SynExpr)) = (this.VisitSynLongIdent ident, r, this.VisitSynExpr expr)
abstract VisitAnonRecordTypeField: (Ident * SynType) -> Ident * SynType
default this.VisitAnonRecordTypeField((ident: Ident, t: SynType)) = (this.VisitIdent ident, this.VisitSynType t)
@@ -265,7 +267,7 @@ type SyntaxVisitor() =
default this.VisitSynMemberSig(ms: SynMemberSig): SynMemberSig =
match ms with
- | SynMemberSig.Member(valSig, flags, range) -> SynMemberSig.Member(this.VisitSynValSig valSig, flags, range)
+ | SynMemberSig.Member(valSig, flags, range, trivia) -> SynMemberSig.Member(this.VisitSynValSig valSig, flags, range, trivia)
| SynMemberSig.Interface(typeName, range) -> SynMemberSig.Interface(this.VisitSynType typeName, range)
| SynMemberSig.Inherit(typeName, range) -> SynMemberSig.Inherit(this.VisitSynType typeName, range)
| SynMemberSig.ValField(f, range) -> SynMemberSig.ValField(this.VisitSynField f, range)
@@ -323,18 +325,18 @@ type SyntaxVisitor() =
SynMemberDefn.GetSetMember(memberDefnForGet |> Option.map this.VisitSynBinding, memberDefnForSet |> Option.map this.VisitSynBinding, range1, synMemberGetSetTrivia)
| SynMemberDefn.Open(target, range) -> SynMemberDefn.Open(this.VisitSynOpenDeclTarget target, range)
| SynMemberDefn.Member(memberDefn, range) -> SynMemberDefn.Member(this.VisitSynBinding memberDefn, range)
- | SynMemberDefn.ImplicitCtor(access, attrs, ctorArgs, selfIdentifier, doc, range) ->
+ | SynMemberDefn.ImplicitCtor(access, attrs, ctorArgs, selfIdentifier, doc, range, trivia) ->
SynMemberDefn.ImplicitCtor
(Option.map this.VisitSynAccess access, attrs |> List.map this.VisitSynAttributeList,
- this.VisitSynSimplePats ctorArgs, Option.map this.VisitIdent selfIdentifier, this.VisitPreXmlDoc(doc), range)
+ this.VisitSynPat ctorArgs, Option.map this.VisitIdent selfIdentifier, this.VisitPreXmlDoc(doc), range, trivia)
| SynMemberDefn.ImplicitInherit(inheritType, inheritArgs, inheritAlias, range) ->
SynMemberDefn.ImplicitInherit
(this.VisitSynType inheritType, this.VisitSynExpr inheritArgs, Option.map this.VisitIdent inheritAlias,
range)
| SynMemberDefn.LetBindings(bindings, isStatic, isRecursive, range) ->
SynMemberDefn.LetBindings(bindings |> List.map this.VisitSynBinding, isStatic, isRecursive, range)
- | SynMemberDefn.AbstractSlot(valSig, flags, range) ->
- SynMemberDefn.AbstractSlot(this.VisitSynValSig valSig, flags, range)
+ | SynMemberDefn.AbstractSlot(valSig, flags, range, trivia) ->
+ SynMemberDefn.AbstractSlot(this.VisitSynValSig valSig, flags, range, trivia)
| SynMemberDefn.Interface(typ, withKeyword, members, range) ->
SynMemberDefn.Interface
(this.VisitSynType typ, withKeyword, Option.map (List.map this.VisitSynMemberDefn) members, range)
@@ -364,10 +366,8 @@ type SyntaxVisitor() =
default this.VisitSynSimplePats(sp: SynSimplePats): SynSimplePats =
match sp with
- | SynSimplePats.SimplePats(pats, range) ->
- SynSimplePats.SimplePats(pats |> List.map this.VisitSynSimplePat, range)
- | SynSimplePats.Typed(pats, typ, range) ->
- SynSimplePats.Typed(this.VisitSynSimplePats pats, this.VisitSynType typ, range)
+ | SynSimplePats.SimplePats(pats, commaRanges, range) ->
+ SynSimplePats.SimplePats(pats |> List.map this.VisitSynSimplePat, commaRanges, range)
abstract VisitSynBinding: SynBinding -> SynBinding
@@ -420,7 +420,8 @@ type SyntaxVisitor() =
default this.VisitSynTyparDecl(std: SynTyparDecl): SynTyparDecl =
match std with
- | SynTyparDecl(attrs, typar) -> SynTyparDecl(attrs |> List.map this.VisitSynAttributeList, this.VisitSynTypar typar)
+ | SynTyparDecl(attrs, typar, intersectionConstraints, trivia) ->
+ SynTyparDecl(attrs |> List.map this.VisitSynAttributeList, this.VisitSynTypar typar, intersectionConstraints |> List.map this.VisitSynType, trivia)
abstract VisitSynTypar: SynTypar -> SynTypar
@@ -459,7 +460,7 @@ type SyntaxVisitor() =
(this.VisitSynLongIdent longDotId, Option.map this.VisitIdent ident,
Option.map this.VisitSynValTyparDecls svtd, this.VisitSynArgPats ctorArgs,
Option.map this.VisitSynAccess access, range)
- | SynPat.Tuple(isStruct, pats, range) -> SynPat.Tuple(isStruct, pats |> List.map this.VisitSynPat, range)
+ | SynPat.Tuple(isStruct, pats, commaRanges, range) -> SynPat.Tuple(isStruct, pats |> List.map this.VisitSynPat, commaRanges, range)
| SynPat.Paren(pat, range) -> SynPat.Paren(this.VisitSynPat pat, range)
| SynPat.ArrayOrList(isList, pats, range) ->
SynPat.ArrayOrList(isList, pats |> List.map this.VisitSynPat, range)
@@ -473,7 +474,6 @@ type SyntaxVisitor() =
| SynPat.OptionalVal(ident, range) -> SynPat.OptionalVal(this.VisitIdent ident, range)
| SynPat.IsInst(typ, range) -> SynPat.IsInst(this.VisitSynType typ, range)
| SynPat.QuoteExpr(expr, range) -> SynPat.QuoteExpr(this.VisitSynExpr expr, range)
- | SynPat.DeprecatedCharRange(c, c2, range) -> SynPat.DeprecatedCharRange(c, c2, range)
| SynPat.InstanceMember(ident, ident2, ident3, access, range) ->
SynPat.InstanceMember
(this.VisitIdent ident, this.VisitIdent ident2, Option.map this.VisitIdent ident3,
@@ -580,10 +580,10 @@ type SyntaxVisitor() =
default this.VisitSynEnumCase(sec: SynEnumCase): SynEnumCase =
match sec with
- | SynEnumCase(attrs, ident, cnst, cnstRange, doc, trivia, range) ->
+ | SynEnumCase(attrs, ident, expr, doc, trivia, range) ->
SynEnumCase
- (attrs |> List.map this.VisitSynAttributeList, this.VisitSynIdent ident, this.VisitSynConst cnst,
- cnstRange, this.VisitPreXmlDoc(doc), trivia, range)
+ (attrs |> List.map this.VisitSynAttributeList, this.VisitSynIdent ident, this.VisitSynExpr expr,
+ this.VisitPreXmlDoc(doc), trivia, range)
abstract VisitSynField: SynField -> SynField
@@ -618,8 +618,6 @@ type SyntaxVisitor() =
| SynType.WithGlobalConstraints(typeName, constraints, range) ->
SynType.WithGlobalConstraints(this.VisitSynType typeName, constraints, range)
| SynType.HashConstraint(synType, range) -> SynType.HashConstraint(this.VisitSynType synType, range)
- | SynType.MeasureDivide(dividendType, divisorType, range) ->
- SynType.MeasureDivide(this.VisitSynType dividendType, this.VisitSynType divisorType, range)
| SynType.MeasurePower(measureType, cnst, range) ->
SynType.MeasurePower(this.VisitSynType measureType, cnst, range)
| SynType.StaticConstant(constant, range) -> SynType.StaticConstant(this.VisitSynConst constant, range)
@@ -632,6 +630,8 @@ type SyntaxVisitor() =
SynType.Or(this.VisitSynType lhsType, this.VisitSynType rhsType, range, this.VisitSynTypeOrTrivia synTypeOrTrivia)
| SynType.SignatureParameter(synAttributeLists, optional, identOption, usedType, range) ->
SynType.SignatureParameter(List.map this.VisitSynAttributeList synAttributeLists, optional, Option.map this.VisitIdent identOption, this.VisitSynType usedType, range)
+ | SynType.FromParseError range -> SynType.FromParseError range
+ | SynType.Intersection(typar, types, range, trivia) -> SynType.Intersection(Option.map this.VisitSynTypar typar, types |> List.map this.VisitSynType, range, trivia)
abstract VisitSynTypeOrTrivia: SynTypeOrTrivia -> SynTypeOrTrivia
default this.VisitSynTypeOrTrivia(synTypeOrTrivia: SynTypeOrTrivia): SynTypeOrTrivia = synTypeOrTrivia
diff --git a/tests/AllTestsWithTask/AllTestsWithTask.fsproj b/tests/AllTestsWithTask/AllTestsWithTask.fsproj
index 3a25184..3efb84f 100644
--- a/tests/AllTestsWithTask/AllTestsWithTask.fsproj
+++ b/tests/AllTestsWithTask/AllTestsWithTask.fsproj
@@ -1,7 +1,7 @@
- net7.0
+ net8.0
false
diff --git a/tests/ClassBasedTests/ClassBasedTests.fsproj b/tests/ClassBasedTests/ClassBasedTests.fsproj
index 0def9cc..98b0d99 100644
--- a/tests/ClassBasedTests/ClassBasedTests.fsproj
+++ b/tests/ClassBasedTests/ClassBasedTests.fsproj
@@ -1,7 +1,7 @@
- net7.0
+ net8.0
false
diff --git a/tests/DifferentTestCodeFormats/DifferentTestCodeFormats.fsproj b/tests/DifferentTestCodeFormats/DifferentTestCodeFormats.fsproj
index 94f53ac..5856853 100644
--- a/tests/DifferentTestCodeFormats/DifferentTestCodeFormats.fsproj
+++ b/tests/DifferentTestCodeFormats/DifferentTestCodeFormats.fsproj
@@ -1,7 +1,7 @@
- net7.0
+ net8.0
false
diff --git a/tests/DifferentTypesOfTests/DifferentTypesOfTests.fsproj b/tests/DifferentTypesOfTests/DifferentTypesOfTests.fsproj
index 69b3d54..947130b 100644
--- a/tests/DifferentTypesOfTests/DifferentTypesOfTests.fsproj
+++ b/tests/DifferentTypesOfTests/DifferentTypesOfTests.fsproj
@@ -1,7 +1,7 @@
- net7.0
+ net8.0
false
diff --git a/tests/DotnetSevenProject/DotnetSevenProject.fs b/tests/DotnetSevenProject/DotnetSevenProject.fs
new file mode 100644
index 0000000..863d2cc
--- /dev/null
+++ b/tests/DotnetSevenProject/DotnetSevenProject.fs
@@ -0,0 +1,7 @@
+module DotnetSixProject
+
+let add x y = x + y
+
+let sub x y = x - y
+
+let mul x y = x * y
diff --git a/tests/DotnetSevenProject/DotnetSevenProject.fsproj b/tests/DotnetSevenProject/DotnetSevenProject.fsproj
new file mode 100644
index 0000000..7ca81b5
--- /dev/null
+++ b/tests/DotnetSevenProject/DotnetSevenProject.fsproj
@@ -0,0 +1,22 @@
+
+
+
+ net7.0
+
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tests/DotnetSevenProject/DotnetSevenProjectTests.fs b/tests/DotnetSevenProject/DotnetSevenProjectTests.fs
new file mode 100644
index 0000000..fd93a7a
--- /dev/null
+++ b/tests/DotnetSevenProject/DotnetSevenProjectTests.fs
@@ -0,0 +1,15 @@
+module DotnetSixProjectTests
+
+open Xunit
+open FsUnit.Xunit
+open Exercism.Tests
+open DotnetSixProject
+
+[]
+let ``Add should add numbers`` () = add 1 1 |> should equal 2
+
+[]
+let ``Sub should subtract numbers`` () = sub 7 3 |> should equal 4
+
+[]
+let ``Mul should multiply numbers`` () = mul 2 3 |> should equal 6
diff --git a/tests/DotnetSevenProject/expected_results.json b/tests/DotnetSevenProject/expected_results.json
new file mode 100644
index 0000000..ce56be2
--- /dev/null
+++ b/tests/DotnetSevenProject/expected_results.json
@@ -0,0 +1,21 @@
+{
+ "version": 3,
+ "status": "pass",
+ "tests": [
+ {
+ "name": "Add should add numbers",
+ "status": "pass",
+ "test_code": "add 1 1 |\u003E should equal 2"
+ },
+ {
+ "name": "Sub should subtract numbers",
+ "status": "pass",
+ "test_code": "sub 7 3 |\u003E should equal 4"
+ },
+ {
+ "name": "Mul should multiply numbers",
+ "status": "pass",
+ "test_code": "mul 2 3 |\u003E should equal 6"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/tests/MultipleCompileErrors/MultipleCompileErrors.fsproj b/tests/MultipleCompileErrors/MultipleCompileErrors.fsproj
index 3b0de41..c4bcbf5 100644
--- a/tests/MultipleCompileErrors/MultipleCompileErrors.fsproj
+++ b/tests/MultipleCompileErrors/MultipleCompileErrors.fsproj
@@ -1,7 +1,7 @@
- net7.0
+ net8.0
false
diff --git a/tests/MultipleTestsWithAllPasses/MultipleTestsWithAllPasses.fsproj b/tests/MultipleTestsWithAllPasses/MultipleTestsWithAllPasses.fsproj
index d5c3d9b..fc69380 100644
--- a/tests/MultipleTestsWithAllPasses/MultipleTestsWithAllPasses.fsproj
+++ b/tests/MultipleTestsWithAllPasses/MultipleTestsWithAllPasses.fsproj
@@ -1,7 +1,7 @@
- net7.0
+ net8.0
false
diff --git a/tests/MultipleTestsWithMultipleFails/MultipleTestsWithMultipleFails.fsproj b/tests/MultipleTestsWithMultipleFails/MultipleTestsWithMultipleFails.fsproj
index 104c721..188305b 100644
--- a/tests/MultipleTestsWithMultipleFails/MultipleTestsWithMultipleFails.fsproj
+++ b/tests/MultipleTestsWithMultipleFails/MultipleTestsWithMultipleFails.fsproj
@@ -1,7 +1,7 @@
- net7.0
+ net8.0
false
diff --git a/tests/MultipleTestsWithSingleFail/MultipleTestsWithSingleFail.fsproj b/tests/MultipleTestsWithSingleFail/MultipleTestsWithSingleFail.fsproj
index 47a34c9..0c45b73 100644
--- a/tests/MultipleTestsWithSingleFail/MultipleTestsWithSingleFail.fsproj
+++ b/tests/MultipleTestsWithSingleFail/MultipleTestsWithSingleFail.fsproj
@@ -1,7 +1,7 @@
- net7.0
+ net8.0
false
diff --git a/tests/MultipleTestsWithTestOutput/MultipleTestsWithTestOutput.fsproj b/tests/MultipleTestsWithTestOutput/MultipleTestsWithTestOutput.fsproj
index 72542a2..d00b37f 100644
--- a/tests/MultipleTestsWithTestOutput/MultipleTestsWithTestOutput.fsproj
+++ b/tests/MultipleTestsWithTestOutput/MultipleTestsWithTestOutput.fsproj
@@ -1,7 +1,7 @@
- net7.0
+ net8.0
false
diff --git a/tests/MultipleTestsWithTestOutputExceedingLimit/MultipleTestsWithTestOutputExceedingLimit.fsproj b/tests/MultipleTestsWithTestOutputExceedingLimit/MultipleTestsWithTestOutputExceedingLimit.fsproj
index 9bc4e87..7695de6 100644
--- a/tests/MultipleTestsWithTestOutputExceedingLimit/MultipleTestsWithTestOutputExceedingLimit.fsproj
+++ b/tests/MultipleTestsWithTestOutputExceedingLimit/MultipleTestsWithTestOutputExceedingLimit.fsproj
@@ -1,7 +1,7 @@
- net7.0
+ net8.0
false
diff --git a/tests/NotImplemented/NotImplemented.fsproj b/tests/NotImplemented/NotImplemented.fsproj
index a09ba83..3b28590 100644
--- a/tests/NotImplemented/NotImplemented.fsproj
+++ b/tests/NotImplemented/NotImplemented.fsproj
@@ -1,7 +1,7 @@
- net7.0
+ net8.0
false
diff --git a/tests/QuotedAndNonQuotedTests/QuotedAndNonQuotedTests.fsproj b/tests/QuotedAndNonQuotedTests/QuotedAndNonQuotedTests.fsproj
index 1415c84..6bdf6f4 100644
--- a/tests/QuotedAndNonQuotedTests/QuotedAndNonQuotedTests.fsproj
+++ b/tests/QuotedAndNonQuotedTests/QuotedAndNonQuotedTests.fsproj
@@ -1,7 +1,7 @@
- net7.0
+ net8.0
false
diff --git a/tests/SingleCompileError/SingleCompileError.fsproj b/tests/SingleCompileError/SingleCompileError.fsproj
index d1b9636..7928d23 100644
--- a/tests/SingleCompileError/SingleCompileError.fsproj
+++ b/tests/SingleCompileError/SingleCompileError.fsproj
@@ -1,7 +1,7 @@
- net7.0
+ net8.0
false
diff --git a/tests/SingleTestThatFails/SingleTestThatFails.fsproj b/tests/SingleTestThatFails/SingleTestThatFails.fsproj
index c566666..76d8e7e 100644
--- a/tests/SingleTestThatFails/SingleTestThatFails.fsproj
+++ b/tests/SingleTestThatFails/SingleTestThatFails.fsproj
@@ -1,7 +1,7 @@
- net7.0
+ net8.0
false
diff --git a/tests/SingleTestThatPasses/SingleTestThatPasses.fsproj b/tests/SingleTestThatPasses/SingleTestThatPasses.fsproj
index ce76471..8eeddac 100644
--- a/tests/SingleTestThatPasses/SingleTestThatPasses.fsproj
+++ b/tests/SingleTestThatPasses/SingleTestThatPasses.fsproj
@@ -1,7 +1,7 @@
- net7.0
+ net8.0
false
diff --git a/tests/SomeTestsWithTask/SomeTestsWithTask.fsproj b/tests/SomeTestsWithTask/SomeTestsWithTask.fsproj
index 2d35683..e3e0928 100644
--- a/tests/SomeTestsWithTask/SomeTestsWithTask.fsproj
+++ b/tests/SomeTestsWithTask/SomeTestsWithTask.fsproj
@@ -1,7 +1,7 @@
- net7.0
+ net8.0
false
diff --git a/tests/TestWithParentheses/TestWithParentheses.fsproj b/tests/TestWithParentheses/TestWithParentheses.fsproj
index ecce244..45a0597 100644
--- a/tests/TestWithParentheses/TestWithParentheses.fsproj
+++ b/tests/TestWithParentheses/TestWithParentheses.fsproj
@@ -1,7 +1,7 @@
- net7.0
+ net8.0
false
diff --git a/tests/UseCultureAttribute/UseCultureAttribute.fsproj b/tests/UseCultureAttribute/UseCultureAttribute.fsproj
index 818308e..a4bdd7b 100644
--- a/tests/UseCultureAttribute/UseCultureAttribute.fsproj
+++ b/tests/UseCultureAttribute/UseCultureAttribute.fsproj
@@ -1,7 +1,7 @@
- net7.0
+ net8.0
false