Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 46 additions & 1 deletion tests/FSharp.Compiler.Service.Tests/Checker.fs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@ namespace FSharp.Compiler.Service.Tests

open System
open FSharp.Compiler.CodeAnalysis
open FSharp.Compiler.Diagnostics
open FSharp.Compiler.EditorServices
open FSharp.Compiler.Text
open FSharp.Compiler.Text.Range
open FSharp.Compiler.Tokenization
open FSharp.Test.Assert
open FSharp.Test.Compiler.Assertions.TextBasedDiagnosticAsserts
open Xunit

type SourceContext =
{ Source: string
Expand Down Expand Up @@ -125,6 +129,9 @@ module CheckResultsExtensions =
member this.GetTooltip(context: ResolveContext, width) =
this.GetToolTip(context.Pos.Line, context.Pos.Column, context.LineText, context.Names, FSharpTokenTag.Identifier, width)

member this.GetDeclarationLocation(context: ResolveContext) =
this.GetDeclarationLocation(context.Pos.Line, context.Pos.Column, context.LineText, context.Names)

member this.GetCodeCompletionSuggestions(context: CodeCompletionContext, parseResults: FSharpParseFileResults, options: FSharpCodeCompletionOptions) =
this.GetDeclarationListInfo(Some parseResults, context.Pos.Line, context.LineText, context.PartialIdentifier, options = options)

Expand Down Expand Up @@ -173,6 +180,11 @@ module Checker =
let getCompletionInfo markedSource =
getCompletionInfoWithOptions FSharpCodeCompletionOptions.Default markedSource

let getCompletionInfoOfSignatureFile markedSource =
let context = getCompletionContext markedSource
let parseResults, checkResults = getParseAndCheckResultsOfSignatureFile context.Source
checkResults.GetCodeCompletionSuggestions(context, parseResults, FSharpCodeCompletionOptions.Default)

let getSymbolUses (markedSource: string) =
let context, checkResults = getCheckedResolveContext markedSource
checkResults.GetSymbolUses(context)
Expand All @@ -184,11 +196,44 @@ module Checker =
let getTooltipWithOptions (options: string array) (markedSource: string) =
let context = getResolveContext markedSource
let _, checkResults = getParseAndCheckResultsWithOptions options context.Source
checkResults.GetToolTip(context.Pos.Line, context.Pos.Column, context.LineText, context.Names, FSharpTokenTag.Identifier)
checkResults.GetTooltip(context)

let getTooltip (markedSource: string) =
getTooltipWithOptions [||] markedSource

let getMethodOverloads names (markedSource: string) =
let context, checkResults = getCheckedResolveContext markedSource
checkResults.GetMethodOverloads(context, names)

/// Shared assertion helpers reused by the completion, editor, tooltip and type-checker-recovery
/// test files. Defined here (an early-compiled file) so every consuming file sees a single
/// definition instead of redefining its own copy.
[<AutoOpen>]
module AssertHelpers =
let assertItemsWithNames contains names (completionInfo: DeclarationListInfo) =
let itemNames =
completionInfo.Items
|> Array.map _.NameInCode
|> Array.map normalizeNewLines
|> set

for name in names do
let name = normalizeNewLines name
Set.contains name itemNames |> shouldEqual contains

let assertHasItemWithNames names (completionInfo: DeclarationListInfo) =
assertItemsWithNames true names completionInfo

let assertHasNoItemsWithNames names (completionInfo: DeclarationListInfo) =
assertItemsWithNames false names completionInfo

let assertAndExtractTooltip (ToolTipText(items)) =
Assert.Equal(1, items.Length)
match items[0] with
| ToolTipElement.Group [ singleElement ] ->
let toolTipText =
singleElement.MainDescription
|> taggedTextToString
toolTipText, singleElement.XmlDoc, singleElement.Remarks |> Option.map taggedTextToString
| _ -> failwith $"Expected group, got {items[0]}"

24 changes: 16 additions & 8 deletions tests/FSharp.Compiler.Service.Tests/Common.fs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ open System.IO
open System.Collections.Generic
open System.Threading.Tasks
open FSharp.Compiler.CodeAnalysis
open FSharp.Compiler.Diagnostics
open FSharp.Compiler.IO
open FSharp.Compiler.Symbols
open FSharp.Compiler.Syntax
Expand Down Expand Up @@ -376,15 +377,22 @@ let getParseAndCheckResults80 (source: string) =
parseAndCheckScript80("Test.fsx", source)


let inline dumpDiagnostics (results: FSharpCheckFileResults) =
let normalizeDiagnosticMessage (d: FSharpDiagnostic) =
d.Message.Split('\n')
|> Array.map _.Trim()
|> Array.filter (fun s -> s.Length > 0)
|> String.concat " "

let formatDiagnostic (d: FSharpDiagnostic) =
sprintf "%s: %s" (d.Range.ToString()) (normalizeDiagnosticMessage d)

let dumpDiagnostics (results: FSharpCheckFileResults) =
results.Diagnostics |> Array.map formatDiagnostic |> List.ofArray

let dumpDiagnosticsOfSeverity (severity: FSharpDiagnosticSeverity) (results: FSharpCheckFileResults) =
results.Diagnostics
|> Array.map (fun e ->
let message =
e.Message.Split('\n')
|> Array.map _.Trim()
|> Array.filter (fun s -> s.Length > 0)
|> String.concat " "
sprintf "%s: %s" (e.Range.ToString()) message)
|> Array.filter (fun d -> d.Severity = severity)
|> Array.map formatDiagnostic
|> List.ofArray

let inline dumpDiagnosticNumbers (results: FSharpCheckFileResults) =
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
module FSharp.Compiler.Service.Tests.CompletionAccessibilityTests

open Xunit

[<Fact>]
let ``PrivateVisible`` () =
let info =
Checker.getCompletionInfo
"""
module CodeAccessibility

module Module1 =
let private fieldPrivate = 1
let private MethodPrivate x =
x+1
type private TypePrivate() =
member this.mem = 1
let a = (*Marker1*) {caret}"""

assertHasItemWithNames [ "fieldPrivate"; "MethodPrivate"; "TypePrivate" ] info

[<Fact>]
let ``InternalVisible`` () =
let info =
Checker.getCompletionInfo
"""
module CodeAccessibility

module Module1 =
let internal fieldInternal = 1
let internal MethodInternal x =
x+1
type internal TypeInternal() =
member this.mem = 1
let a = (*Marker1*) {caret}"""

assertHasItemWithNames [ "fieldInternal"; "MethodInternal"; "TypeInternal" ] info

let private widgetInheritanceSource =
"""
open System
//define the base class
type Widget() =
let mutable state = 0
member internal x.MethodInternal() = state
member public x.MethodPublic(n) = state <- state + n
member private x.MethodPrivate() = (state <> 0)
[<DefaultValue>]
val mutable internal fieldInternal:int
[<DefaultValue>]
val mutable public fieldPublic:int
[<DefaultValue>]
val mutable private fieldPrivate:int
//define the divided class which inherent "Widget"
type Divided() =
inherit Widget()
member x.myPrint() =
base.{caret}
Console.ReadKey(true)"""

[<Fact>]
let ``InheritedClass.BaseClassPrivateMethod.Negative`` () =
let info = Checker.getCompletionInfo widgetInheritanceSource
assertHasNoItemsWithNames [ "MethodPrivate"; "fieldPrivate" ] info

[<Fact>]
let ``InheritedClass.BaseClassPublicMethodAndProperty`` () =
let info = Checker.getCompletionInfo widgetInheritanceSource
assertHasItemWithNames [ "MethodPublic"; "fieldPublic" ] info

[<Fact>]
let ``Visibility.InternalNestedClass.Negative`` () =
let info = Checker.getCompletionInfo "System.Console.{caret}"

assertHasNoItemsWithNames [ "ControlCDelegateData" ] info

[<Fact>]
let ``Visibility.PrivateIdentifierInDiffModule.Negative`` () =
let info =
Checker.getCompletionInfo
"""
module Module1 =
let private fieldPrivate = 1
let private MethodPrivate x =
x+1
type private TypePrivate()=
member this.mem = 1
module Module2 =
Module1.{caret}"""

Assert.Equal(0, info.Items.Length)

[<Fact>]
let ``Visibility.PrivateIdentifierInDiffClass.Negative`` () =
let info =
Checker.getCompletionInfo
"""
open System
module Module1 =
type Type1()=
[<DefaultValue>]
val mutable private fieldPrivate:int
member private x.MethodPrivate() = 1
type Type2()=
let M1=
let type1 = new Type1()
type1.{caret}"""

assertHasNoItemsWithNames [ "fieldPrivate"; "MethodPrivate" ] info

[<Theory>]
[<InlineData("""
open System

module Module1 =
type Type1()=
[<DefaultValue>]
val mutable private PrivateField:int
static member private PrivateMethod() = 1
member this.Field1 with get () = this.{caret}
member x.MethodTest() = Type1(*MarkerMethodInType*)
let type1 = new Type1() """,
"PrivateField")>]
[<InlineData("""
open System

module Module1 =
type Type1()=
[<DefaultValue>]
val mutable private PrivateField:int
static member private PrivateMethod() = 1
member this.Field1 with get () = this(*MarkerFieldInType*)
member x.MethodTest() = Type1.{caret}
let type1 = new Type1() """,
"PrivateMethod")>]
let ``Visibility.PrivateMemberInSameClass`` (markedSource: string) (expected: string) =
let info = Checker.getCompletionInfo markedSource

assertHasItemWithNames [ expected ] info

[<Fact>]
let ``Visibility.InternalMethods.DefInSameAssembly`` () =
let info =
Checker.getCompletionInfo
"""
module CodeAccessibility
open System
module Module1 =
type Type1()=
[<DefaultValue>]
val mutable internal fieldInternal:int
member internal x.MethodInternal (x:int) = x+2
let type1 = new Type1()
type1.{caret}"""

assertHasItemWithNames [ "fieldInternal"; "MethodInternal" ] info

[<Theory>]
[<InlineData("""type Base =
val mutable baseField : int
val mutable private baseFieldPrivate : int
new () = { baseField = 0; baseFieldPrivate=1 }
type Derived =
val mutable derivedField : int
val mutable private derivedFieldPrivate : int
inherit Base
new () = { derivedField = 0;derivedFieldPrivate = 0 }
let derived = Derived()
derived.{caret}derivedField""")>]
[<InlineData("""type Base =
val mutable baseField : int
val mutable private baseFieldPrivate : int
new () = { baseField = 0; baseFieldPrivate=1 }
type Derived =
val mutable baseField : int
val mutable derivedField : int
val mutable private derivedFieldPrivate : int
inherit Base
new () = { baseField = 0; derivedField = 0; derivedFieldPrivate = 0 }
let derived = Derived()
derived.{caret}derivedField""")>]
let ``ObjInstance.InheritedClass.MethodsWithDiffAccessibility`` (markedSource: string) =
let info = Checker.getCompletionInfo markedSource
assertHasItemWithNames [ "baseField"; "derivedField" ] info
assertHasNoItemsWithNames [ "baseFieldPrivate"; "derivedFieldPrivate" ] info

[<Theory>]
[<InlineData("""type Base =
val mutable baseField : int
val mutable private baseFieldPrivate : int
new () = { baseField = 0; baseFieldPrivate=1 }
type Derived =
val mutable derivedField : int
val mutable private derivedFieldPrivate : int
inherit Base
new () = { derivedField = 0;derivedFieldPrivate = 0 }
member this.Method() =
(*marker*)this.{caret}baseField""")>]
[<InlineData("""type Base =
val mutable baseField : int
val mutable private baseFieldPrivate : int
new () = { baseField = 0; baseFieldPrivate=1 }
type Derived =
val mutable baseField : int
val mutable derivedField : int
val mutable private derivedFieldPrivate : int
inherit Base
new () = { baseField = 0; derivedField = 0; derivedFieldPrivate = 0 }
member this.Method() =
(*marker*)this.{caret}baseField""")>]
let ``Visibility.InheritedClass.MethodsWithDiffAccessibility`` (markedSource: string) =
let info = Checker.getCompletionInfo markedSource
assertHasItemWithNames [ "baseField"; "derivedField"; "derivedFieldPrivate" ] info
assertHasNoItemsWithNames [ "baseFieldPrivate" ] info

[<Fact>]
let ``Visibility.InheritedClass.MethodsWithSameNameMethod`` () =
let info =
Checker.getCompletionInfo
"""type MyClass =
val foo : int
new (foo) = { foo = foo }
type MyClass2 =
inherit MyClass
val foo : int
new (foo) = {
inherit MyClass(foo)
foo = foo
}
let x = new MyClass2(0)
(*marker*)x.{caret}foo"""

assertHasItemWithNames [ "foo" ] info
Loading
Loading