Skip to content

Commit

Permalink
Restore missing commit and inline nameof ident check.
Browse files Browse the repository at this point in the history
  • Loading branch information
nojaf committed Jan 19, 2024
1 parent a7c1228 commit 123b619
Show file tree
Hide file tree
Showing 5 changed files with 122 additions and 20 deletions.
15 changes: 14 additions & 1 deletion src/Compiler/Driver/GraphChecking/DependencyResolution.fs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
module internal FSharp.Compiler.GraphChecking.DependencyResolution

open FSharp.Compiler.Syntax
open Internal.Utilities.Library

/// <summary>Find a path from a starting TrieNode and return the end node or None</summary>
let queryTriePartial (trie: TrieNode) (path: LongIdentifier) : TrieNode option =
Expand Down Expand Up @@ -118,6 +117,20 @@ let rec processStateEntry (trie: TrieNode) (state: FileContentQueryState) (entry
FoundDependencies = foundDependencies
}

| ModuleName name ->
// We need to check if the module name is a hit in the Trie.
let state' =
let queryResult = queryTrie trie [ name ]
processIdentifier queryResult state

match state.OwnNamespace with
| None -> state'
| Some ns ->
// If there we currently have our own namespace,
// the combination of that namespace + module name should be checked as well.
let queryResult = queryTrieDual trie ns [ name ]
processIdentifier queryResult state'

/// <summary>
/// For a given file's content, collect all missing ("ghost") file dependencies that the core resolution algorithm didn't return,
/// but are required to satisfy the type-checker.
Expand Down
41 changes: 22 additions & 19 deletions src/Compiler/Driver/GraphChecking/FileContentMapping.fs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ let longIdentToPath (skipLast: bool) (longId: LongIdent) : LongIdentifier =
let synLongIdentToPath (skipLast: bool) (synLongIdent: SynLongIdent) =
longIdentToPath skipLast synLongIdent.LongIdent

/// In some rare cases we are interested in the name of a single Ident.
/// For example `nameof ModuleName` in expressions or patterns.
let visitIdentAsPotentialModuleName (moduleNameIdent: Ident) =
FileContentEntry.ModuleName moduleNameIdent.idText

let visitSynLongIdent (lid: SynLongIdent) : FileContentEntry list = visitLongIdent lid.LongIdent

let visitLongIdent (lid: LongIdent) =
Expand Down Expand Up @@ -302,6 +307,10 @@ let visitSynTypeConstraint (tc: SynTypeConstraint) : FileContentEntry list =
| SynTypeConstraint.WhereTyparIsEnum(typeArgs = typeArgs) -> List.collect visitSynType typeArgs
| SynTypeConstraint.WhereTyparIsDelegate(typeArgs = typeArgs) -> List.collect visitSynType typeArgs

[<return: Struct>]
let inline (|NameofIdent|_|) (ident: Ident) =
if ident.idText = "nameof" then ValueSome() else ValueNone

/// Special case of `nameof Module` type of expression
let (|NameofExpr|_|) (e: SynExpr) =
let rec stripParen (e: SynExpr) =
Expand All @@ -310,13 +319,10 @@ let (|NameofExpr|_|) (e: SynExpr) =
| _ -> e

match e with
| SynExpr.App(flag = ExprAtomicFlag.NonAtomic; isInfix = false; funcExpr = SynExpr.Ident nameofIdent; argExpr = moduleNameExpr) ->
if nameofIdent.idText <> "nameof" then
None
else
match stripParen moduleNameExpr with
| SynExpr.Ident moduleNameIdent -> Some moduleNameIdent
| _ -> None
| SynExpr.App(flag = ExprAtomicFlag.NonAtomic; isInfix = false; funcExpr = SynExpr.Ident NameofIdent; argExpr = moduleNameExpr) ->
match stripParen moduleNameExpr with
| SynExpr.Ident moduleNameIdent -> Some moduleNameIdent
| _ -> None
| _ -> None

let visitSynExpr (e: SynExpr) : FileContentEntry list =
Expand Down Expand Up @@ -543,18 +549,15 @@ let (|NameofPat|_|) (pat: SynPat) =
| _ -> p

match pat with
| SynPat.LongIdent(longDotId = SynLongIdent(id = [ nameofIdent ]); typarDecls = None; argPats = SynArgPats.Pats [ moduleNamePat ]) ->
if nameofIdent.idText <> "nameof" then
None
else
match stripPats moduleNamePat with
| SynPat.LongIdent(
longDotId = SynLongIdent.SynLongIdent(id = [ moduleNameIdent ]; dotRanges = []; trivia = [ None ])
extraId = None
typarDecls = None
argPats = SynArgPats.Pats []
accessibility = None) -> Some moduleNameIdent
| _ -> None
| SynPat.LongIdent(longDotId = SynLongIdent(id = [ NameofIdent ]); typarDecls = None; argPats = SynArgPats.Pats [ moduleNamePat ]) ->
match stripPats moduleNamePat with
| SynPat.LongIdent(
longDotId = SynLongIdent.SynLongIdent(id = [ moduleNameIdent ]; dotRanges = []; trivia = [ None ])
extraId = None
typarDecls = None
argPats = SynArgPats.Pats []
accessibility = None) -> Some moduleNameIdent
| _ -> None
| _ -> None

let visitPat (p: SynPat) : FileContentEntry list =
Expand Down
3 changes: 3 additions & 0 deletions src/Compiler/Driver/GraphChecking/Types.fs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ type internal FileContentEntry =
/// Being explicit about nested modules allows for easier reasoning what namespaces (paths) are open.
/// We can scope an `OpenStatement` to the everything that is happening inside the nested module.
| NestedModule of name: string * nestedContent: FileContentEntry list
/// A single identifier that could be the name of a module.
/// Example use-case: `let x = nameof Foo` where `Foo` is a module.
| ModuleName of name: Identifier

type internal FileContent =
{
Expand Down
3 changes: 3 additions & 0 deletions src/Compiler/Driver/GraphChecking/Types.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ type internal FileContentEntry =
/// Being explicit about nested modules allows for easier reasoning what namespaces (paths) are open.
/// For example we can limit the scope of an `OpenStatement` to symbols defined inside the nested module.
| NestedModule of name: string * nestedContent: FileContentEntry list
/// A single identifier that could be the name of a module.
/// Example use-case: `let x = nameof Foo` where `Foo` is a module.
| ModuleName of name: Identifier

/// File identifiers and its content extract for dependency resolution
type internal FileContent =
Expand Down
80 changes: 80 additions & 0 deletions tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/Scenarios.fs
Original file line number Diff line number Diff line change
Expand Up @@ -800,6 +800,86 @@ printfn "Hello"
"""
Set.empty
]
scenario
"Nameof module with namespace"
[
sourceFile
"A.fs"
"""
namespace X.Y.Z
module Foo =
let x = 2
"""
Set.empty
sourceFile
"B.fs"
"""
namespace X.Y.Z
module Point =
let y = nameof Foo
"""
(set [| 0 |])
]
scenario
"Nameof module without namespace"
[
sourceFile
"A.fs"
"""
module Foo
let x = 2
"""
Set.empty
sourceFile
"B.fs"
"""
module Point
let y = nameof Foo
"""
(set [| 0 |])
]
scenario
"Single module name should always be checked, regardless of own namespace"
[
sourceFile "X.fs" "namespace X.Y" Set.empty
sourceFile
"A.fs"
"""
module Foo
let x = 2
"""
Set.empty
sourceFile
"B.fs"
"""
namespace X.Y
type T() =
let _ = nameof Foo
"""
(set [| 1 |])
]
scenario
"nameof pattern"
[
sourceFile "A.fs" "module Foo" Set.empty
sourceFile
"B.fs"
"""
module Bar
do
match "" with
| nameof Foo -> ()
| _ -> ()
"""
(set [| 0 |])
]
scenario
"parentheses around module name in nameof pattern"
[
Expand Down

0 comments on commit 123b619

Please sign in to comment.