Skip to content
This repository was archived by the owner on Dec 23, 2024. It is now read-only.

Commit 7ff037e

Browse files
Tarmildsyme
authored andcommitted
Further enhancements to nameof (dotnet#8754)
* Implement `nameof x` as a constant pattern * Resolve ident to find `nameof` in patterns * fix build * fix build * re-enable tests * fix test * fix operators * align code * test and fix pattern matching * fix 8661, 7416 * fix tests and error messages * 'fix test' * 'fix test' * add message for C# and old compilers * fix build * suppress error 3501 when a compiler supports nameof Co-authored-by: Don Syme <[email protected]>
1 parent e81b2fa commit 7ff037e

File tree

10 files changed

+245
-123
lines changed

10 files changed

+245
-123
lines changed

src/fsharp/AttributeChecking.fs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,11 @@ let CheckFSharpAttributes (g:TcGlobals) attribs m =
299299
match namedArgs with
300300
| ExtractAttribNamedArg "IsError" (AttribBoolArg v) -> v
301301
| _ -> false
302-
if isError && (not g.compilingFslib || n <> 1204) then ErrorD msg else WarnD msg
302+
// If we are using a compiler that supports nameof then error 3501 is always suppressed.
303+
// See attribute on FSharp.Core 'nameof'
304+
if n = 3501 then CompleteD
305+
elif isError && (not g.compilingFslib || n <> 1204) then ErrorD msg
306+
else WarnD msg
303307
| _ ->
304308
CompleteD
305309
) ++ (fun () ->

src/fsharp/FSComp.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1515,4 +1515,5 @@ featureWitnessPassing,"witness passing for trait constraints in F# quotations"
15151515
3361,typrelInterfaceWithConcreteAndVariableObjectExpression,"You cannot implement the interface '%s' with the two instantiations '%s' and '%s' because they may unify."
15161516
featureInterfacesWithMultipleGenericInstantiation,"interfaces with multiple generic instantiation"
15171517
3362,tcLiteralFieldAssignmentWithArg,"Cannot assign '%s' to a value marked literal"
1518-
3363,tcLiteralFieldAssignmentNoArg,"Cannot assign a value to another value marked literal"
1518+
3363,tcLiteralFieldAssignmentNoArg,"Cannot assign a value to another value marked literal"
1519+
#3501 "This construct is not supported by your version of the F# compiler" CompilerMessage(ExperimentalAttributeMessages.NotSupportedYet, 3501, IsError=true)

src/fsharp/FSharp.Core/prim-types.fs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,9 @@ namespace Microsoft.FSharp.Core
251251
[<Literal>]
252252
let RequiresPreview : string = "Experimental library feature, requires '--langversion:preview'"
253253

254+
[<Literal>]
255+
let NotSupportedYet : string = "This construct is not supported by your version of the F# compiler"
256+
254257
[<AttributeUsage(AttributeTargets.All, AllowMultiple=false)>]
255258
[<Sealed>]
256259
type ExperimentalAttribute(message:string) =
@@ -4718,7 +4721,7 @@ namespace Microsoft.FSharp.Core
47184721
[<CompiledName("TypeOf")>]
47194722
let inline typeof<'T> = BasicInlinedOperations.typeof<'T>
47204723

4721-
[<CompiledName("NameOf")>]
4724+
[<CompiledName("NameOf"); CompilerMessage(ExperimentalAttributeMessages.NotSupportedYet, 3501, IsError=true)>]
47224725
let inline nameof (_: 'T) : string = raise (Exception "may not call directly, should always be optimized away")
47234726

47244727
[<CompiledName("MethodHandleOf")>]

src/fsharp/FSharp.Core/prim-types.fsi

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -653,10 +653,11 @@ namespace Microsoft.FSharp.Core
653653
/// <summary>Indicates one or more adjustments to the compiled representation of an F# type or member</summary>
654654
member Flags : CompilationRepresentationFlags
655655

656-
module internal ExperimentalAttributeMessages = begin
656+
module internal ExperimentalAttributeMessages =
657657
[<Literal>]
658658
val RequiresPreview : string = "Experimental library feature, requires '--langversion:preview'"
659-
end
659+
[<Literal>]
660+
val NotSupportedYet : string = "This construct is not supported by your version of the F# compiler"
660661

661662
/// <summary>This attribute is used to tag values that are part of an experimental library
662663
/// feature.</summary>
@@ -2846,7 +2847,7 @@ namespace Microsoft.FSharp.Core
28462847
val inline typeof<'T> : System.Type
28472848

28482849
/// <summary>Returns the name of the given symbol.</summary>
2849-
[<CompiledName("NameOf")>]
2850+
[<CompiledName("NameOf"); CompilerMessage(ExperimentalAttributeMessages.NotSupportedYet, 3501, IsError=true)>]
28502851
val inline nameof : 'T -> string
28512852

28522853
/// <summary>An internal, library-only compiler intrinsic for compile-time

src/fsharp/NameResolution.fs

Lines changed: 63 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -2579,7 +2579,7 @@ let rec ResolveExprLongIdentPrim sink (ncenv: NameResolver) first fullyQualified
25792579
if first && id.idText = MangledGlobalName then
25802580
match rest with
25812581
| [] ->
2582-
error (Error(FSComp.SR.nrGlobalUsedOnlyAsFirstName(), id.idRange))
2582+
raze (Error(FSComp.SR.nrGlobalUsedOnlyAsFirstName(), id.idRange))
25832583
| [next] ->
25842584
ResolveExprLongIdentPrim sink ncenv false fullyQualified m ad nenv typeNameResInfo next [] isOpenDecl
25852585
| id2 :: rest2 ->
@@ -2603,11 +2603,12 @@ let rec ResolveExprLongIdentPrim sink (ncenv: NameResolver) first fullyQualified
26032603

26042604
let search = ChooseTyconRefInExpr (ncenv, m, ad, nenv, id, typeNameResInfo, resInfo, tcrefs)
26052605
match AtMostOneResult m search with
2606-
| Result _ as res ->
2607-
let resInfo, item, rest = ForceRaise res
2606+
| Result (resInfo, item, rest) ->
26082607
ResolutionInfo.SendEntityPathToSink(sink, ncenv, nenv, ItemOccurence.Use, ad, resInfo, ResultTyparChecker(fun () -> CheckAllTyparsInferrable ncenv.amap m item))
26092608
Some(item, rest)
2610-
| Exception e -> typeError <- Some e; None
2609+
| Exception e ->
2610+
typeError <- Some e
2611+
None
26112612

26122613
| true, res ->
26132614
let fresh = FreshenUnqualifiedItem ncenv m res
@@ -2624,7 +2625,7 @@ let rec ResolveExprLongIdentPrim sink (ncenv: NameResolver) first fullyQualified
26242625
None
26252626

26262627
match envSearch with
2627-
| Some res -> res
2628+
| Some res -> success res
26282629
| None ->
26292630
let innerSearch =
26302631
// Check if it's a type name, e.g. a constructor call or a type instantiation
@@ -2640,9 +2641,9 @@ let rec ResolveExprLongIdentPrim sink (ncenv: NameResolver) first fullyQualified
26402641

26412642
ctorSearch +++ implicitOpSearch
26422643

2643-
let resInfo, item, rest =
2644+
let res =
26442645
match AtMostOneResult m innerSearch with
2645-
| Result _ as res -> ForceRaise res
2646+
| Result _ as res -> res
26462647
| _ ->
26472648
let failingCase =
26482649
match typeError with
@@ -2671,11 +2672,12 @@ let rec ResolveExprLongIdentPrim sink (ncenv: NameResolver) first fullyQualified
26712672
addToBuffer (e.Value.DisplayName + "." + id.idText)
26722673

26732674
raze (UndefinedName(0, FSComp.SR.undefinedNameValueOfConstructor, id, suggestNamesAndTypes))
2674-
ForceRaise failingCase
2675-
2675+
failingCase
2676+
match res with
2677+
| Exception e -> raze e
2678+
| Result (resInfo, item, rest) ->
26762679
ResolutionInfo.SendEntityPathToSink(sink, ncenv, nenv, ItemOccurence.Use, ad, resInfo, ResultTyparChecker(fun () -> CheckAllTyparsInferrable ncenv.amap m item))
2677-
item, rest
2678-
2680+
success (item, rest)
26792681

26802682
// A compound identifier.
26812683
// It still might be a value in the environment, or something in an F# module, namespace, type, or nested type
@@ -2693,13 +2695,13 @@ let rec ResolveExprLongIdentPrim sink (ncenv: NameResolver) first fullyQualified
26932695
| _ -> false
26942696

26952697
if ValIsInEnv id.idText then
2696-
nenv.eUnqualifiedItems.[id.idText], rest
2698+
success (nenv.eUnqualifiedItems.[id.idText], rest)
26972699
else
26982700
// Otherwise modules are searched first. REVIEW: modules and types should be searched together.
26992701
// For each module referenced by 'id', search the module as if it were an F# module and/or a .NET namespace.
27002702
let moduleSearch ad () =
2701-
ResolveLongIndentAsModuleOrNamespaceThen sink ResultCollectionSettings.AtMostOneResult ncenv.amap m fullyQualified nenv ad id rest isOpenDecl
2702-
(ResolveExprLongIdentInModuleOrNamespace ncenv nenv typeNameResInfo ad)
2703+
ResolveLongIndentAsModuleOrNamespaceThen sink ResultCollectionSettings.AtMostOneResult ncenv.amap m fullyQualified nenv ad id rest isOpenDecl
2704+
(ResolveExprLongIdentInModuleOrNamespace ncenv nenv typeNameResInfo ad)
27032705

27042706
// REVIEW: somewhat surprisingly, this shows up on performance traces, with tcrefs non-nil.
27052707
// This seems strange since we would expect in the vast majority of cases tcrefs is empty here.
@@ -2717,59 +2719,59 @@ let rec ResolveExprLongIdentPrim sink (ncenv: NameResolver) first fullyQualified
27172719
NoResultsOrUsefulErrors
27182720

27192721
let search =
2720-
let envSearch () =
2721-
match fullyQualified with
2722-
| FullyQualified ->
2723-
NoResultsOrUsefulErrors
2724-
| OpenQualified ->
2725-
match nenv.eUnqualifiedItems.TryGetValue id.idText with
2726-
| true, Item.UnqualifiedType _
2727-
| false, _ -> NoResultsOrUsefulErrors
2728-
| true, res -> OneSuccess (resInfo, FreshenUnqualifiedItem ncenv m res, rest)
2729-
2730-
moduleSearch ad () +++ tyconSearch ad +++ envSearch
2731-
2732-
let resInfo, item, rest =
2722+
let envSearch () =
2723+
match fullyQualified with
2724+
| FullyQualified ->
2725+
NoResultsOrUsefulErrors
2726+
| OpenQualified ->
2727+
match nenv.eUnqualifiedItems.TryGetValue id.idText with
2728+
| true, Item.UnqualifiedType _
2729+
| false, _ -> NoResultsOrUsefulErrors
2730+
| true, res -> OneSuccess (resInfo, FreshenUnqualifiedItem ncenv m res, rest)
2731+
2732+
moduleSearch ad () +++ tyconSearch ad +++ envSearch
2733+
2734+
let res =
27332735
match AtMostOneResult m search with
2734-
| Result _ as res -> ForceRaise res
2736+
| Result _ as res -> res
27352737
| _ ->
27362738
let innerSearch = search +++ (moduleSearch AccessibleFromSomeFSharpCode) +++ (tyconSearch AccessibleFromSomeFSharpCode)
27372739

27382740
let suggestEverythingInScope (addToBuffer: string -> unit) =
2739-
for kv in nenv.ModulesAndNamespaces fullyQualified do
2740-
for modref in kv.Value do
2741-
if IsEntityAccessible ncenv.amap m ad modref then
2742-
addToBuffer modref.DisplayName
2743-
addToBuffer modref.DemangledModuleOrNamespaceName
2741+
for kv in nenv.ModulesAndNamespaces fullyQualified do
2742+
for modref in kv.Value do
2743+
if IsEntityAccessible ncenv.amap m ad modref then
2744+
addToBuffer modref.DisplayName
2745+
addToBuffer modref.DemangledModuleOrNamespaceName
27442746

2745-
for e in nenv.TyconsByDemangledNameAndArity fullyQualified do
2746-
if IsEntityAccessible ncenv.amap m ad e.Value then
2747-
addToBuffer e.Value.DisplayName
2747+
for e in nenv.TyconsByDemangledNameAndArity fullyQualified do
2748+
if IsEntityAccessible ncenv.amap m ad e.Value then
2749+
addToBuffer e.Value.DisplayName
27482750

2749-
for e in nenv.eUnqualifiedItems do
2750-
if canSuggestThisItem e.Value then
2751-
addToBuffer e.Value.DisplayName
2751+
for e in nenv.eUnqualifiedItems do
2752+
if canSuggestThisItem e.Value then
2753+
addToBuffer e.Value.DisplayName
27522754

27532755
match innerSearch with
27542756
| Exception (UndefinedName(0, _, id1, suggestionsF)) when Range.equals id.idRange id1.idRange ->
2755-
let mergeSuggestions addToBuffer =
2756-
suggestionsF addToBuffer
2757-
suggestEverythingInScope addToBuffer
2758-
2759-
let failingCase = raze (UndefinedName(0, FSComp.SR.undefinedNameValueNamespaceTypeOrModule, id, mergeSuggestions))
2760-
ForceRaise failingCase
2761-
| Exception err -> ForceRaise(Exception err)
2762-
| Result (res :: _) -> ForceRaise(Result res)
2757+
let mergeSuggestions addToBuffer =
2758+
suggestionsF addToBuffer
2759+
suggestEverythingInScope addToBuffer
2760+
raze (UndefinedName(0, FSComp.SR.undefinedNameValueNamespaceTypeOrModule, id, mergeSuggestions))
2761+
| Exception err -> raze err
2762+
| Result (res :: _) -> success res
27632763
| Result [] ->
2764-
let failingCase = raze (UndefinedName(0, FSComp.SR.undefinedNameValueNamespaceTypeOrModule, id, suggestEverythingInScope))
2765-
ForceRaise failingCase
2764+
raze (UndefinedName(0, FSComp.SR.undefinedNameValueNamespaceTypeOrModule, id, suggestEverythingInScope))
27662765

2767-
ResolutionInfo.SendEntityPathToSink(sink, ncenv, nenv, ItemOccurence.Use, ad, resInfo, ResultTyparChecker(fun () -> CheckAllTyparsInferrable ncenv.amap m item))
2768-
item, rest
2766+
match res with
2767+
| Exception e -> raze e
2768+
| Result (resInfo, item, rest) ->
2769+
ResolutionInfo.SendEntityPathToSink(sink, ncenv, nenv, ItemOccurence.Use, ad, resInfo, ResultTyparChecker(fun () -> CheckAllTyparsInferrable ncenv.amap m item))
2770+
success (item, rest)
27692771

27702772
let ResolveExprLongIdent sink (ncenv: NameResolver) m ad nenv typeNameResInfo lid =
27712773
match lid with
2772-
| [] -> error (Error(FSComp.SR.nrInvalidExpression(textOfLid lid), m))
2774+
| [] -> raze (Error(FSComp.SR.nrInvalidExpression(textOfLid lid), m))
27732775
| id :: rest -> ResolveExprLongIdentPrim sink ncenv true OpenQualified m ad nenv typeNameResInfo id rest false
27742776

27752777
//-------------------------------------------------------------------------
@@ -3414,15 +3416,17 @@ type AfterResolution =
34143416
///
34153417
/// Called for 'TypeName.Bar' - for VS IntelliSense, we can filter out instance members from method groups
34163418
let ResolveLongIdentAsExprAndComputeRange (sink: TcResultsSink) (ncenv: NameResolver) wholem ad nenv typeNameResInfo lid =
3417-
let item1, rest = ResolveExprLongIdent sink ncenv wholem ad nenv typeNameResInfo lid
3419+
match ResolveExprLongIdent sink ncenv wholem ad nenv typeNameResInfo lid with
3420+
| Exception e -> Exception e
3421+
| Result (item1, rest) ->
34183422
let itemRange = ComputeItemRange wholem lid rest
34193423

34203424
let item = FilterMethodGroups ncenv itemRange item1 true
34213425

34223426
match item1, item with
34233427
| Item.MethodGroup(name, minfos1, _), Item.MethodGroup(_, [], _) when not (isNil minfos1) ->
3424-
error(Error(FSComp.SR.methodIsNotStatic name, wholem))
3425-
| _ -> ()
3428+
raze(Error(FSComp.SR.methodIsNotStatic name, wholem))
3429+
| _ ->
34263430

34273431
// Fake idents e.g. 'Microsoft.FSharp.Core.None' have identical ranges for each part
34283432
let isFakeIdents =
@@ -3462,16 +3466,14 @@ let ResolveLongIdentAsExprAndComputeRange (sink: TcResultsSink) (ncenv: NameReso
34623466
callSink (item, emptyTyparInst)
34633467
AfterResolution.DoNothing
34643468

3465-
item, itemRange, rest, afterResolution
3469+
success (item, itemRange, rest, afterResolution)
34663470

34673471
let (|NonOverridable|_|) namedItem =
34683472
match namedItem with
34693473
| Item.MethodGroup(_, minfos, _) when minfos |> List.exists(fun minfo -> minfo.IsVirtual || minfo.IsAbstract) -> None
34703474
| Item.Property(_, pinfos) when pinfos |> List.exists(fun pinfo -> pinfo.IsVirtualProperty) -> None
34713475
| _ -> Some ()
34723476

3473-
3474-
34753477
/// Called for 'expression.Bar' - for VS IntelliSense, we can filter out static members from method groups
34763478
/// Also called for 'GenericType<Args>.Bar' - for VS IntelliSense, we can filter out non-static members from method groups
34773479
let ResolveExprDotLongIdentAndComputeRange (sink: TcResultsSink) (ncenv: NameResolver) wholem ad nenv ty lid findFlag thisIsActuallyATyAppNotAnExpr =
@@ -3571,7 +3573,9 @@ let IsUnionCaseUnseen ad g amap m (ucref: UnionCaseRef) =
35713573

35723574
let ItemIsUnseen ad g amap m item =
35733575
match item with
3574-
| Item.Value x -> IsValUnseen ad g m x
3576+
| Item.Value x ->
3577+
let isUnseenNameOfOperator = valRefEq g g.nameof_vref x && not (g.langVersion.SupportsFeature LanguageFeature.NameOf)
3578+
isUnseenNameOfOperator || IsValUnseen ad g m x
35753579
| Item.UnionCase(x, _) -> IsUnionCaseUnseen ad g amap m x.UnionCaseRef
35763580
| Item.ExnCase x -> IsTyconUnseen ad g amap m x
35773581
| _ -> false

src/fsharp/NameResolution.fsi

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -545,7 +545,7 @@ val internal ResolveTypeLongIdent : TcResultsSink -> NameResol
545545
val internal ResolveField : TcResultsSink -> NameResolver -> NameResolutionEnv -> AccessorDomain -> TType -> Ident list * Ident -> Ident list -> FieldResolution list
546546

547547
/// Resolve a long identifier occurring in an expression position
548-
val internal ResolveExprLongIdent : TcResultsSink -> NameResolver -> range -> AccessorDomain -> NameResolutionEnv -> TypeNameResolutionInfo -> Ident list -> Item * Ident list
548+
val internal ResolveExprLongIdent : TcResultsSink -> NameResolver -> range -> AccessorDomain -> NameResolutionEnv -> TypeNameResolutionInfo -> Ident list -> ResultOrException<Item * Ident list>
549549

550550
/// Resolve a (possibly incomplete) long identifier to a loist of possible class or record fields
551551
val internal ResolvePartialLongIdentToClassOrRecdFields : NameResolver -> NameResolutionEnv -> range -> AccessorDomain -> string list -> bool -> Item list
@@ -571,7 +571,7 @@ type AfterResolution =
571571
| RecordResolution of Item option * (TyparInst -> unit) * (MethInfo * PropInfo option * TyparInst -> unit) * (unit -> unit)
572572

573573
/// Resolve a long identifier occurring in an expression position.
574-
val internal ResolveLongIdentAsExprAndComputeRange : TcResultsSink -> NameResolver -> range -> AccessorDomain -> NameResolutionEnv -> TypeNameResolutionInfo -> Ident list -> Item * range * Ident list * AfterResolution
574+
val internal ResolveLongIdentAsExprAndComputeRange : TcResultsSink -> NameResolver -> range -> AccessorDomain -> NameResolutionEnv -> TypeNameResolutionInfo -> Ident list -> ResultOrException<Item * range * Ident list * AfterResolution>
575575

576576
/// Resolve a long identifier occurring in an expression position, qualified by a type.
577577
val internal ResolveExprDotLongIdentAndComputeRange : TcResultsSink -> NameResolver -> range -> AccessorDomain -> NameResolutionEnv -> TType -> Ident list -> FindMemberFlag -> bool -> Item * range * Ident list * AfterResolution

0 commit comments

Comments
 (0)