// Copyright (c) Microsoft Corporation.  All Rights Reserved.  See License.txt in the project root for license information.

namespace FSharp.Compiler.UnitTests

open System
open FSharp.Compiler.Diagnostics
open Xunit
open FSharp.Test

#if NETCOREAPP

module SpanTests =

    [<Fact>]
    let Script_SpanForInDo() =
        let script = 
            """
open System

let test () : unit =
    let span = Span([|1;2;3;4|])
    let result = ResizeArray()
    for item in span do
        result.Add(item)
    
    if result.[0] <> 1 || result.[1] <> 2 || result.[2] <> 3 || result.[3] <> 4 then
        failwith "SpanForInDo didn't work properly"

test ()
            """
        
        CompilerAssert.RunScript script []
    [<Fact>]
    let Script_SpanForInBoundsDo() =
        let script = 
            """
open System

let test () : unit =
    let span = Span([|1;2;3;4|])
    let result = ResizeArray()
    for i in 0 .. span.Length-1 do
        result.Add(span.[i])
    
    if result.[0] <> 1 || result.[1] <> 2 || result.[2] <> 3 || result.[3] <> 4 then
        failwith "SpanForInBoundsDo didn't work properly"

test ()
            """
        
        CompilerAssert.RunScript script []

    [<Fact>]
    let Script_EmptySpanForInBoundsDo() =
        let script = 
            """
open System

let test () : unit =
    let span = Span([||])
    let result = ResizeArray()
    for i in 0 .. span.Length-1 do
        result.Add(span.[i])
    
    if result.Count <> 0 then
        failwith "EmptySpanForInBoundsDo didn't work properly"

test ()
            """
        
        CompilerAssert.RunScript script []

    [<Fact>]
    let Script_ReadOnlySpanForInDo() =
        let script = 
            """
open System

let test () : unit =
    let span = ReadOnlySpan([|1;2;3;4|])
    let result = ResizeArray()
    for item in span do
        result.Add(item)

    if result.[0] <> 1 || result.[1] <> 2 || result.[2] <> 3 || result.[3] <> 4 then
        failwith "ReadOnlySpanForInDo didn't work properly"

test ()
            """
    
        CompilerAssert.RunScript script []

    [<Fact>]
    let Script_ReadOnlySpanForInBoundsDo() =
        let script = 
            """
open System

let test () : unit =
    let span = ReadOnlySpan([|1;2;3;4|])
    let result = ResizeArray()
    for i in 0 .. span.Length-1 do
        result.Add(span.[i])

    if result.[0] <> 1 || result.[1] <> 2 || result.[2] <> 3 || result.[3] <> 4 then
        failwith "Script_ReadOnlySpanForInBoundsDo didn't work properly"

test ()
            """
    
        CompilerAssert.RunScript script []


    [<Fact>]
    let ``Invalid usage of type abbreviated span should fail to compile``() =
        CompilerAssert.TypeCheckWithErrors """
open System

type Bytes = ReadOnlySpan<byte>

type Test() =

    member _.M1 (data: Bytes) =
        let x = 
            if false then
                failwith ""
            else
                data
        x

    member _.M2 (data: Bytes) =
        let x = 
            if false then
                failwithf ""
            else
                data
        x

let test () =
    let span = ReadOnlySpan<_>(Array.empty)
    let result = Test().M1(span)
    let result = Test().M2(span)
    0
            """
            [|
                FSharpDiagnosticSeverity.Error, 412, (11, 17, 11, 28), "A type instantiation involves a byref type. This is not permitted by the rules of Common IL."
                FSharpDiagnosticSeverity.Error, 412, (19, 17, 19, 29), "A type instantiation involves a byref type. This is not permitted by the rules of Common IL."
                FSharpDiagnosticSeverity.Error, 412, (19, 27, 19, 29), "A type instantiation involves a byref type. This is not permitted by the rules of Common IL."
            |]

    [<Fact>]
    let ``Type abbreviation that boxes a span should fail to compile``() =
        CompilerAssert.TypeCheckWithErrors """
open System

type TA = Span<int> * Span<int>

let f (x: TA) = ()
            """
            [|
                FSharpDiagnosticSeverity.Error, 3300, (6, 8, 6, 9), "The parameter 'x' has an invalid type 'TA'. This is not permitted by the rules of Common IL."
            |]

    [<Fact>]
    let ``A custom IsByRefLikeAttribute can define a ref struct``() =
        CompilerAssert.TypeCheckWithErrors """
namespace System.Runtime.CompilerServices

open System

[<AttributeUsage(AttributeTargets.Struct)>]
type IsByRefLikeAttribute() = inherit Attribute()

[<IsByRefLike>]
type T(span: Span<byte>) = struct end
             """
             [| |]

    [<Fact>]
    let ``A byref struct with custom attr can be passed as typar``() =
        CompilerAssert.TypeCheckWithErrors """
namespace System.Runtime.CompilerServices

open System

[<AttributeUsage(AttributeTargets.Struct)>]
type IsByRefLikeAttribute() = inherit Attribute()

[<IsByRefLike>]
type T(span: Span<byte>) = struct end

module WhatEver = 
    let processT (a: Action<T>, ie: seq<T>, asList: list<T>) = ()
             """
             [| FSharpDiagnosticSeverity.Error, 3300, (13, 45, 13, 51), "The parameter 'asList' has an invalid type 'T list'. This is not permitted by the rules of Common IL." |]
#endif
