Skip to content

Commit 8d048d8

Browse files
authored
nameof fixes (#9498)
1 parent 1836eb5 commit 8d048d8

File tree

5 files changed

+79
-85
lines changed

5 files changed

+79
-85
lines changed

src/absil/illib.fs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,11 @@ module List =
385385
mn 0 xs
386386
let count pred xs = List.fold (fun n x -> if pred x then n+1 else n) 0 xs
387387

388+
let headAndTail l =
389+
match l with
390+
| [] -> failwith "headAndTail"
391+
| h::t -> (h,t)
392+
388393
// WARNING: not tail-recursive
389394
let mapHeadTail fhead ftail = function
390395
| [] -> []

src/fsharp/TypeChecker.fs

Lines changed: 61 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9271,38 +9271,66 @@ and TcNameOfExpr cenv env tpenv (synArg: SynExpr) =
92719271

92729272
let cleanSynArg = stripParens synArg
92739273
let m = cleanSynArg.Range
9274-
let rec check overallTyOpt expr (delayed: DelayedItem list) =
9274+
let rec check overallTyOpt resultOpt expr (delayed: DelayedItem list) =
92759275
match expr with
9276-
| LongOrSingleIdent (false, (LongIdentWithDots((id::rest) as longId, _) as lidd), _, _) ->
9276+
| LongOrSingleIdent (false, (LongIdentWithDots(longId, _) as lidd), _, _) ->
92779277
let ad = env.eAccessRights
9278-
match ResolveLongIndentAsModuleOrNamespaceOrStaticClass cenv.tcSink ResultCollectionSettings.AllResults cenv.amap m false true OpenQualified env.eNameResEnv ad id rest true with
9279-
| Result modref when delayed.IsEmpty && modref |> List.exists (p23 >> IsEntityAccessible cenv.amap m ad) ->
9280-
() // resolved to a module or namespace, done with checks
9281-
| _ ->
9282-
let (TypeNameResolutionInfo(_, staticArgsInfo)) = GetLongIdentTypeNameInfo delayed
9283-
match ResolveTypeLongIdent cenv.tcSink cenv.nameResolver ItemOccurence.UseInAttribute OpenQualified env.eNameResEnv ad longId staticArgsInfo PermitDirectReferenceToGeneratedType.No with
9284-
| Result tcref when IsEntityAccessible cenv.amap m ad tcref ->
9285-
() // resolved to a type name, done with checks
9286-
| _ ->
9287-
let overallTy = match overallTyOpt with None -> NewInferenceType() | Some t -> t
9288-
let _, _ = TcLongIdentThen cenv overallTy env tpenv lidd delayed
9289-
() // checked as an expression, done with checks
9290-
List.last longId
9278+
let result = defaultArg resultOpt (List.last longId)
9279+
let resolvedToModuleOrNamespaceName =
9280+
if delayed.IsEmpty then
9281+
let id,rest = List.headAndTail longId
9282+
match ResolveLongIndentAsModuleOrNamespaceOrStaticClass cenv.tcSink ResultCollectionSettings.AllResults cenv.amap m false true OpenQualified env.eNameResEnv ad id rest true with
9283+
| Result modref when delayed.IsEmpty && modref |> List.exists (p23 >> IsEntityAccessible cenv.amap m ad) ->
9284+
true // resolved to a module or namespace, done with checks
9285+
| _ ->
9286+
false
9287+
else
9288+
false
9289+
if resolvedToModuleOrNamespaceName then result else
9290+
9291+
let resolvedToTypeName =
9292+
if (match delayed with [DelayedTypeApp _] | [] -> true | _ -> false) then
9293+
let (TypeNameResolutionInfo(_, staticArgsInfo)) = GetLongIdentTypeNameInfo delayed
9294+
match ResolveTypeLongIdent cenv.tcSink cenv.nameResolver ItemOccurence.UseInAttribute OpenQualified env.eNameResEnv ad longId staticArgsInfo PermitDirectReferenceToGeneratedType.No with
9295+
| Result tcref when (match delayed with [DelayedTypeApp _] | [] -> true | _ -> false) && IsEntityAccessible cenv.amap m ad tcref ->
9296+
true // resolved to a type name, done with checks
9297+
| _ ->
9298+
false
9299+
else
9300+
false
9301+
if resolvedToTypeName then result else
92919302

9303+
let overallTy = match overallTyOpt with None -> NewInferenceType() | Some t -> t
9304+
9305+
// This will raise an error if resolution doesn't succeed
9306+
let _, _ = TcLongIdentThen cenv overallTy env tpenv lidd delayed
9307+
result // checked as an expression, done with checks
9308+
9309+
// expr<tyargs> allowed, even with qualifications
92929310
| SynExpr.TypeApp (hd, _, types, _, _, _, m) ->
9293-
check overallTyOpt hd (DelayedTypeApp(types, m, m) :: delayed)
9311+
check overallTyOpt resultOpt hd (DelayedTypeApp(types, m, m) :: delayed)
92949312

9295-
| SynExpr.Paren(expr, _, _, _) when overallTyOpt.IsNone && delayed.IsEmpty ->
9296-
check overallTyOpt expr []
9313+
// expr.ID allowed
9314+
| SynExpr.DotGet (hd, _, LongIdentWithDots(longId, _), _) ->
9315+
let result = defaultArg resultOpt (List.last longId)
9316+
check overallTyOpt (Some result) hd ((DelayedDotLookup (longId, expr.RangeSansAnyExtraDot)) :: delayed)
92979317

9318+
// "(expr)" allowed with no subsequent qualifications
9319+
| SynExpr.Paren(expr, _, _, _) when delayed.IsEmpty && overallTyOpt.IsNone ->
9320+
check overallTyOpt resultOpt expr delayed
9321+
9322+
// expr : type" allowed with no subsequent qualifications
92989323
| SynExpr.Typed (synBodyExpr, synType, _m) when delayed.IsEmpty && overallTyOpt.IsNone ->
92999324
let tgtTy, _tpenv = TcTypeAndRecover cenv NewTyparsOK CheckCxs ItemOccurence.UseInType env tpenv synType
9300-
check (Some tgtTy) synBodyExpr []
9325+
check (Some tgtTy) resultOpt synBodyExpr delayed
93019326

93029327
| _ ->
93039328
error (Error(FSComp.SR.expressionHasNoName(), m))
93049329

9305-
let lastIdent = check None cleanSynArg []
9330+
let lastIdent = check None None cleanSynArg []
9331+
TcNameOfExprResult cenv lastIdent m
9332+
9333+
and TcNameOfExprResult cenv (lastIdent: Ident) m =
93069334
let constRange = mkRange m.FileName m.Start (mkPos m.StartLine (m.StartColumn + lastIdent.idText.Length + 2)) // `2` are for quotes
93079335
Expr.Const(Const.String(lastIdent.idText), constRange, cenv.g.string_ty)
93089336

@@ -9825,8 +9853,21 @@ and TcItemThen cenv overallTy env tpenv (item, mItem, rest, afterResolution) del
98259853
// - it isn't a CtorValUsedAsSuperInit
98269854
// - it isn't a CtorValUsedAsSelfInit
98279855
// - it isn't a VSlotDirectCall (uses of base values do not take type arguments
9856+
// Allow `nameof<'T>` for a generic parameter
9857+
match vref with
9858+
| _ when isNameOfValRef cenv.g vref && cenv.g.langVersion.SupportsFeature LanguageFeature.NameOf ->
9859+
match tys with
9860+
| [SynType.Var((Typar(id, _, false) as tp), _m)] ->
9861+
let _tp', tpenv = TcTyparOrMeasurePar None cenv env ImplicitlyBoundTyparsAllowed.NoNewTypars tpenv tp
9862+
let vexp = TcNameOfExprResult cenv id mExprAndTypeArgs
9863+
let vexpFlex = MakeApplicableExprNoFlex cenv vexp
9864+
PropagateThenTcDelayed cenv overallTy env tpenv mExprAndTypeArgs vexpFlex cenv.g.string_ty ExprAtomicFlag.Atomic otherDelayed
9865+
| _ ->
9866+
error (Error(FSComp.SR.expressionHasNoName(), mExprAndTypeArgs))
9867+
| _ ->
98289868
let checkTys tpenv kinds = TcTypesOrMeasures (Some kinds) cenv NewTyparsOK CheckCxs ItemOccurence.UseInType env tpenv tys mItem
98299869
let _, vexp, isSpecial, _, _, tpenv = TcVal true cenv env tpenv vref (Some (NormalValUse, checkTys)) (Some afterResolution) mItem
9870+
98309871
let vexpFlex = (if isSpecial then MakeApplicableExprNoFlex cenv vexp else MakeApplicableExprWithFlex cenv env vexp)
98319872
// We need to eventually record the type resolution for an expression, but this is done
98329873
// inside PropagateThenTcDelayed, so we don't have to explicitly call 'CallExprHasTypeSink' here

src/fsharp/TypedTreeOps.fsi

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2279,7 +2279,11 @@ val (|EnumExpr|_|) : TcGlobals -> Expr -> Expr option
22792279
val (|TypeOfExpr|_|) : TcGlobals -> Expr -> TType option
22802280

22812281
val (|TypeDefOfExpr|_|) : TcGlobals -> Expr -> TType option
2282+
2283+
val isNameOfValRef: TcGlobals -> ValRef -> bool
2284+
22822285
val (|NameOfExpr|_|) : TcGlobals -> Expr -> TType option
2286+
22832287
val (|SeqExpr|_|) : TcGlobals -> Expr -> unit option
22842288

22852289
val EvalLiteralExprOrAttribArg: TcGlobals -> Expr -> Expr

tests/fsharp/core/nameof/preview/test.fsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,14 @@ type BasicNameOfTests() =
103103
let b = nameof(this.MemberMethod)
104104
Assert.AreEqual("MemberMethod", b)
105105

106+
member this.``can get name of instance member via unchecked`` () =
107+
let b = nameof(Unchecked.defaultof<BasicNameOfTests>.MemberMethod)
108+
Assert.AreEqual("MemberMethod", b)
109+
110+
member this.``can get name of method type parameter``<'TTT> () =
111+
let b = nameof<'TTT>
112+
Assert.AreEqual("TTT", b)
113+
106114
static member ``namespace name`` () =
107115
let b = nameof(FSharp.Core)
108116
Assert.AreEqual("Core",b)
@@ -297,6 +305,7 @@ do test "can get name from inside a local function (needs to be let rec)"
297305
do test "can get name from inside static member" (BasicNameOfTests.``can get name from inside static member`` ())
298306
do test "can get name from inside instance member" ((BasicNameOfTests()).``can get name from inside instance member`` ())
299307
do test "can get name of instance member" ((BasicNameOfTests()).``can get name of instance member`` ())
308+
do test "can get name of instance member via unchecked" ((BasicNameOfTests()).``can get name of instance member via unchecked`` ())
300309
do test "namespace name" (BasicNameOfTests.``namespace name`` ())
301310
do test "module name" (BasicNameOfTests.``module name`` ())
302311
do test "exception name" (BasicNameOfTests.``exception name`` ())

tests/fsharp/core/nameof/version46/test.fsx

Lines changed: 0 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -341,68 +341,3 @@ module Foo =
341341
let nameof = ()
342342
let x = name ()
343343

344-
do test "local variable name lookup" (BasicNameOfTests.``local variable name lookup`` ())
345-
do test "local int function name" (BasicNameOfTests.``local int function name`` ())
346-
do test "local curried function name" (BasicNameOfTests.``local curried function name`` ())
347-
do test "local tupled function name" (BasicNameOfTests.``local tupled function name`` ())
348-
do test "local unit function name" (BasicNameOfTests.``local unit function name`` ())
349-
do test "local function parameter name" (BasicNameOfTests.``local function parameter name`` ())
350-
do test "can get name from inside a local function (needs to be let rec)"
351-
(BasicNameOfTests.``can get name from inside a local function (needs to be let rec)`` ())
352-
do test "can get name from inside static member" (BasicNameOfTests.``can get name from inside static member`` ())
353-
do test "can get name from inside instance member" ((BasicNameOfTests()).``can get name from inside instance member`` ())
354-
do test "can get name of instance member" ((BasicNameOfTests()).``can get name of instance member`` ())
355-
do test "namespace name" (BasicNameOfTests.``namespace name`` ())
356-
do test "module name" (BasicNameOfTests.``module name`` ())
357-
do test "exception name" (BasicNameOfTests.``exception name`` ())
358-
do test "nested type name 1" (BasicNameOfTests.``nested type name 1`` ())
359-
do test "type name 2" (BasicNameOfTests.``type name 2`` ())
360-
do test "member function which is defined below" (BasicNameOfTests.``member function which is defined below`` ())
361-
do test "class member lookup" ((BasicNameOfTests()).``class member lookup`` ())
362-
do test "static member function name" (BasicNameOfTests.``static member function name`` ())
363-
do test "static property name" (BasicNameOfTests.``static property name`` ())
364-
do test "member method starting with get_" (BasicNameOfTests.``member method starting with get_`` ())
365-
do test "static method starting with get_" (BasicNameOfTests.``static method starting with get_`` ())
366-
do test "nameof local property with encapsulated name" (BasicNameOfTests.``nameof local property with encapsulated name`` ())
367-
368-
do test "single argument method group name lookup" ((MethodGroupNameOfTests()).``single argument method group name lookup`` ())
369-
do test "multiple argument method group name lookup" ((MethodGroupNameOfTests()).``multiple argument method group name lookup`` ())
370-
371-
do test "measure 1" ((UnionAndRecordNameOfTests()).``measure 1`` ())
372-
do test "record case 1" ((UnionAndRecordNameOfTests()).``record case 1`` ())
373-
do test "union case 1" ((UnionAndRecordNameOfTests()).``union case 1`` ())
374-
do test "union case 2" ((UnionAndRecordNameOfTests()).``union case 2`` ())
375-
376-
do test "ok in attribute" ((AttributeNameOfTests()).``ok in attribute`` ())
377-
378-
do test "lookup name of typeof operator" ((OperatorNameOfTests()).``lookup name of typeof operator`` ())
379-
do test "lookup name of + operator" ((OperatorNameOfTests()).``lookup name of + operator`` ())
380-
do test "lookup name of |> operator" ((OperatorNameOfTests()).``lookup name of |> operator`` ())
381-
do test "lookup name of nameof operator" ((OperatorNameOfTests()).``lookup name of nameof operator`` ())
382-
383-
do test "use it as a match case guard" ((PatternMatchingOfOperatorNameTests()).``use it as a match case guard`` ())
384-
385-
do test "se it in a quotation" ((NameOfOperatorInQuotations()).``use it in a quotation`` ())
386-
387-
do test "use it in a generic function" ((NameOfOperatorForGenerics()).``use it in a generic function`` ())
388-
do test "lookup name of a generic class" ((NameOfOperatorForGenerics()).``lookup name of a generic class`` ())
389-
390-
do test "user defined nameof should shadow the operator"(UserDefinedNameOfTests.``user defined nameof should shadow the operator`` ())
391-
392-
#if TESTS_AS_APP
393-
let RUN() =
394-
match !failures with
395-
| [] -> stdout.WriteLine "Test Passed"
396-
| _ -> stdout.WriteLine "Test Failed"
397-
#else
398-
let aa =
399-
match !failures with
400-
| [] ->
401-
stdout.WriteLine "Test Passed"
402-
System.IO.File.WriteAllText("test.ok","ok")
403-
exit 0
404-
| _ ->
405-
stdout.WriteLine "Test Failed"
406-
exit 1
407-
#endif
408-

0 commit comments

Comments
 (0)