diff --git a/docs/release-notes/.FSharp.Compiler.Service/10.0.300.md b/docs/release-notes/.FSharp.Compiler.Service/10.0.300.md index 858f45b424..5be4810e3d 100644 --- a/docs/release-notes/.FSharp.Compiler.Service/10.0.300.md +++ b/docs/release-notes/.FSharp.Compiler.Service/10.0.300.md @@ -25,6 +25,7 @@ ### Added * FSharpType: add ImportILType ([PR #19300](https://github.com/dotnet/fsharp/pull/19300)) * Type checker: recover on argument/overload checking ([PR #19314](https://github.com/dotnet/fsharp/pull/19314)) +Symbols: add ObsoleteDiagnosticInfo ([PR #19359](https://github.com/dotnet/fsharp/pull/19359)) ### Changed diff --git a/src/Compiler/Checking/AttributeChecking.fs b/src/Compiler/Checking/AttributeChecking.fs index 9388bc238a..9d789f9f46 100755 --- a/src/Compiler/Checking/AttributeChecking.fs +++ b/src/Compiler/Checking/AttributeChecking.fs @@ -6,11 +6,11 @@ module internal FSharp.Compiler.AttributeChecking open System open System.Collections.Generic +open FSharp.Compiler.Text.Range open Internal.Utilities.Library open FSharp.Compiler.AbstractIL.IL open FSharp.Compiler open FSharp.Compiler.DiagnosticsLogger -open FSharp.Compiler.Features open FSharp.Compiler.Import open FSharp.Compiler.Infos open FSharp.Compiler.TcGlobals @@ -231,26 +231,31 @@ let MethInfoHasAttribute g m attribSpec minfo = (fun _ -> Some ()) |> Option.isSome -let private CheckCompilerFeatureRequiredAttribute (g: TcGlobals) cattrs msg m = - // In some cases C# will generate both ObsoleteAttribute and CompilerFeatureRequiredAttribute. - // Specifically, when default constructor is generated for class with any required members in them. - // ObsoleteAttribute should be ignored if CompilerFeatureRequiredAttribute is present, and its name is "RequiredMembers". - let (AttribInfo(tref,_)) = g.attrib_CompilerFeatureRequiredAttribute - match TryDecodeILAttribute tref cattrs with - | Some([ILAttribElem.String (Some featureName) ], _) when featureName = "RequiredMembers" -> - CompleteD - | _ -> - ErrorD (ObsoleteDiagnostic(true, None, msg, None, m)) - +let private reportObsoleteDiagnostic m diagnostic = + match diagnostic with + | Some(ObsoleteDiagnosticInfo(isError, id, msg, urlFormat)) -> + let obsoleteDiagnostic = ObsoleteDiagnostic(isError, id, msg, urlFormat, m) + if isError then + ErrorD(obsoleteDiagnostic) + else + WarnD(obsoleteDiagnostic) + + | _ -> CompleteD + +let private HasCompilerFeatureRequiredAttribute (g: TcGlobals) cattrs = + match TryDecodeILAttribute g.attrib_CompilerFeatureRequiredAttribute.TypeRef cattrs with + | Some([ILAttribElem.String(Some featureName) ], _) when featureName = "RequiredMembers" -> true + | _ -> false + let private extractILAttribValueFrom name namedArgs = match namedArgs with | ExtractILAttributeNamedArg name (AttribElemStringArg v) -> Some v | _ -> None -let private extractILAttributeInfo namedArgs = +let private extractILObsoleteAttributeInfo namedArgs = let diagnosticId = extractILAttribValueFrom "DiagnosticId" namedArgs let urlFormat = extractILAttribValueFrom "UrlFormat" namedArgs - (diagnosticId, urlFormat) + diagnosticId, urlFormat let private CheckILExperimentalAttributes (g: TcGlobals) cattrs m = let (AttribInfo(tref,_)) = g.attrib_IlExperimentalAttribute @@ -275,46 +280,39 @@ let private CheckILExperimentalAttributes (g: TcGlobals) cattrs m = | Some _ | None -> CompleteD -let private CheckILObsoleteAttributes (g: TcGlobals) isByrefLikeTyconRef cattrs m = +let TryGetILObsoleteInfo (g: TcGlobals) isByrefLikeTyconRef cattrs : ObsoleteDiagnosticInfo option = if isByrefLikeTyconRef then - CompleteD + None else - let (AttribInfo(tref,_)) = g.attrib_SystemObsolete - match TryDecodeILAttribute tref cattrs with - // [Obsolete] - // [Obsolete("Message")] - // [Obsolete("Message", true)] - // [Obsolete("Message", DiagnosticId = "DiagnosticId")] - // [Obsolete("Message", DiagnosticId = "DiagnosticId", UrlFormat = "UrlFormat")] - // [Obsolete(DiagnosticId = "DiagnosticId")] - // [Obsolete(DiagnosticId = "DiagnosticId", UrlFormat = "UrlFormat")] - // [Obsolete("Message", true, DiagnosticId = "DiagnosticId")] - // [Obsolete("Message", true, DiagnosticId = "DiagnosticId", UrlFormat = "UrlFormat")] - // Constructors deciding on IsError and Message properties. - | Some ([ attribElement ], namedArgs) -> - let diagnosticId, urlFormat = extractILAttributeInfo namedArgs + match TryDecodeILAttribute g.attrib_SystemObsolete.TypeRef cattrs with + | Some([ attribElement ], namedArgs) -> + let diagnosticId, urlFormat = extractILObsoleteAttributeInfo namedArgs let msg = match attribElement with - | ILAttribElem.String (Some msg) -> Some msg + | ILAttribElem.String(Some msg) -> Some msg | ILAttribElem.String None | _ -> None - WarnD (ObsoleteDiagnostic(false, diagnosticId, msg, urlFormat, m)) - | Some ([ILAttribElem.String msg; ILAttribElem.Bool isError ], namedArgs) -> - let diagnosticId, urlFormat = extractILAttributeInfo namedArgs - if isError then - if g.langVersion.SupportsFeature(LanguageFeature.RequiredPropertiesSupport) then - CheckCompilerFeatureRequiredAttribute g cattrs msg m - else - ErrorD (ObsoleteDiagnostic(true, diagnosticId, msg, urlFormat, m)) - else - WarnD (ObsoleteDiagnostic(false, diagnosticId, msg, urlFormat, m)) - // Only DiagnosticId, UrlFormat - | Some (_, namedArgs) -> - let diagnosticId, urlFormat = extractILAttributeInfo namedArgs - WarnD(ObsoleteDiagnostic(false, diagnosticId, None, urlFormat, m)) - // No arguments - | None -> CompleteD + Some(ObsoleteDiagnosticInfo(false, diagnosticId, msg, urlFormat)) + + | Some([ILAttribElem.String msg; ILAttribElem.Bool isError ], namedArgs) -> + let diagnosticId, urlFormat = extractILObsoleteAttributeInfo namedArgs + Some(ObsoleteDiagnosticInfo(isError, diagnosticId, msg, urlFormat)) + + | Some(_, namedArgs) -> + let diagnosticId, urlFormat = extractILObsoleteAttributeInfo namedArgs + Some(ObsoleteDiagnosticInfo(false, diagnosticId, None, urlFormat)) + + | None -> None + +let private CheckILObsoleteAttributes (g: TcGlobals) isByrefLikeTyconRef cattrs m = + // In some cases C# will generate both ObsoleteAttribute and CompilerFeatureRequiredAttribute. + // Specifically, when default constructor is generated for class with any required members in them. + // ObsoleteAttribute should be ignored if CompilerFeatureRequiredAttribute is present, and its name is "RequiredMembers". + if isByrefLikeTyconRef || HasCompilerFeatureRequiredAttribute g cattrs then + CompleteD + else + TryGetILObsoleteInfo g isByrefLikeTyconRef cattrs |> reportObsoleteDiagnostic m /// Check IL attributes for Experimental, warnings as data let private CheckILAttributes (g: TcGlobals) isByrefLikeTyconRef cattrs m = @@ -332,33 +330,39 @@ let private extractObsoleteAttributeInfo namedArgs = let urlFormat = extractILAttribValueFrom "UrlFormat" namedArgs (diagnosticId, urlFormat) +let TryGetFSharpObsoleteInfo g attribs : ObsoleteDiagnosticInfo option = + // [] + // [] + // [] + // [] + // [] + // [] + // [] + // [] + // [] + // Constructors deciding on IsError and Message properties. + match TryFindFSharpAttribute g g.attrib_SystemObsolete attribs with + | Some(Attrib(unnamedArgs = [ AttribStringArg s ]; propVal = namedArgs)) -> + let diagnosticId, urlFormat = extractObsoleteAttributeInfo namedArgs + Some(ObsoleteDiagnosticInfo(false, diagnosticId, Some s, urlFormat)) + + | Some(Attrib(unnamedArgs = [ AttribStringArg s; AttribBoolArg(isError) ]; propVal = namedArgs)) -> + let diagnosticId, urlFormat = extractObsoleteAttributeInfo namedArgs + Some(ObsoleteDiagnosticInfo(isError, diagnosticId, Some s, urlFormat)) + + // Only DiagnosticId, UrlFormat + | Some(Attrib(propVal = namedArgs)) -> + let diagnosticId, urlFormat = extractObsoleteAttributeInfo namedArgs + Some(ObsoleteDiagnosticInfo(false, diagnosticId, None, urlFormat)) + + | None -> None + let private CheckObsoleteAttributes g attribs m = trackErrors { - match TryFindFSharpAttribute g g.attrib_SystemObsolete attribs with - // [] - // [] - // [] - // [] - // [] - // [] - // [] - // [] - // [] - // Constructors deciding on IsError and Message properties. - | Some(Attrib(unnamedArgs= [ AttribStringArg s ]; propVal= namedArgs)) -> - let diagnosticId, urlFormat = extractObsoleteAttributeInfo namedArgs - do! WarnD(ObsoleteDiagnostic(false, diagnosticId, Some s, urlFormat, m)) - | Some(Attrib(unnamedArgs= [ AttribStringArg s; AttribBoolArg(isError) ]; propVal= namedArgs)) -> - let diagnosticId, urlFormat = extractObsoleteAttributeInfo namedArgs - if isError then - do! ErrorD (ObsoleteDiagnostic(true, diagnosticId, Some s, urlFormat, m)) - else - do! WarnD (ObsoleteDiagnostic(false, diagnosticId, Some s, urlFormat, m)) - // Only DiagnosticId, UrlFormat - | Some(Attrib(propVal= namedArgs)) -> - let diagnosticId, urlFormat = extractObsoleteAttributeInfo namedArgs - do! WarnD(ObsoleteDiagnostic(false, diagnosticId, None, urlFormat, m)) - | None -> () + match TryGetFSharpObsoleteInfo g attribs with + | Some _ as diag -> + do! reportObsoleteDiagnostic m diag + | _ -> () } let private CheckCompilerMessageAttribute g attribs m = @@ -420,22 +424,23 @@ let CheckFSharpAttributes (g:TcGlobals) attribs m = } #if !NO_TYPEPROVIDERS -/// Check a list of provided attributes for 'ObsoleteAttribute', returning errors and warnings as data -let private CheckProvidedAttributes (g: TcGlobals) m (provAttribs: Tainted) = +let TryGetProvidedObsoleteInfo (g: TcGlobals) m (provAttribs: Tainted) : ObsoleteDiagnosticInfo option = let (AttribInfo(tref, _)) = g.attrib_SystemObsolete - match provAttribs.PUntaint((fun a -> a.GetAttributeConstructorArgs(provAttribs.TypeProvider.PUntaintNoFailure(id), tref.FullName)), m) with - | Some ([ Some (:? string as msg) ], _) -> WarnD(ObsoleteDiagnostic(false, None, Some msg, None, m)) - | Some ([ Some (:? string as msg); Some (:?bool as isError) ], _) -> - if isError then - ErrorD (ObsoleteDiagnostic(true, None, Some msg, None, m)) - else - WarnD (ObsoleteDiagnostic(false, None, Some msg, None, m)) - | Some ([ None ], _) -> - WarnD(ObsoleteDiagnostic(false, None, None, None, m)) - | Some _ -> - WarnD(ObsoleteDiagnostic(false, None, None, None, m)) - | None -> - CompleteD + match provAttribs.PUntaint(_.GetAttributeConstructorArgs(provAttribs.TypeProvider.PUntaintNoFailure(id), tref.FullName), m) with + | Some([ Some (:? string as msg) ], _) -> + Some(ObsoleteDiagnosticInfo(false, None, Some msg, None)) + + | Some([ Some (:? string as msg); Some (:? bool as isError) ], _) -> + Some(ObsoleteDiagnosticInfo(isError, None, Some msg, None)) + + | Some _ -> + Some(ObsoleteDiagnosticInfo(false, None, None, None)) + + | _ -> None + +/// Check a list of provided attributes for 'ObsoleteAttribute', returning errors and warnings as data +let private CheckProvidedAttributes (g: TcGlobals) m (provAttribs: Tainted) = + TryGetProvidedObsoleteInfo g m provAttribs |> reportObsoleteDiagnostic m #endif /// Indicate if a list of IL attributes contains 'ObsoleteAttribute'. Used to suppress the item in intellisense. @@ -496,12 +501,21 @@ let CheckPropInfoAttributes pinfo m = #if !NO_TYPEPROVIDERS | ProvidedProp (amap, pi, m) -> CheckProvidedAttributes amap.g m (pi.PApply((fun st -> (st :> IProvidedCustomAttributeProvider)), m)) - #endif +let TryGetPropObsoleteInfo pinfo = + match pinfo with + | ILProp(ILPropInfo(_, pdef)) -> TryGetILObsoleteInfo pinfo.TcGlobals false pdef.CustomAttrs + | FSProp(g, _, Some vref, _) + | FSProp(g, _, _, Some vref) -> TryGetFSharpObsoleteInfo g vref.Attribs + | FSProp _ -> failwith "CheckPropInfoAttributes: unreachable" +#if !NO_TYPEPROVIDERS + | ProvidedProp (amap, pi, m) -> + TryGetProvidedObsoleteInfo amap.g m (pi.PApply((fun st -> (st :> IProvidedCustomAttributeProvider)), m)) +#endif /// Check the attributes associated with a IL field, returning warnings and errors as data. -let CheckILFieldAttributes g (finfo:ILFieldInfo) m = +let CheckILFieldAttributes g (finfo: ILFieldInfo) m = match finfo with | ILFieldInfo(_, pd) -> CheckILAttributes g false pd.CustomAttrs m |> CommitOperationResult @@ -510,16 +524,35 @@ let CheckILFieldAttributes g (finfo:ILFieldInfo) m = CheckProvidedAttributes amap.g m (fi.PApply((fun st -> (st :> IProvidedCustomAttributeProvider)), m)) |> CommitOperationResult #endif +let TryGetILFieldObsoleteInfo g (finfo : ILFieldInfo) = + match finfo with + | ILFieldInfo(_, pd) -> TryGetILObsoleteInfo g false pd.CustomAttrs +#if !NO_TYPEPROVIDERS + | ProvidedField (amap, fi, m) -> + TryGetProvidedObsoleteInfo amap.g m (fi.PApply((fun st -> (st :> IProvidedCustomAttributeProvider)), m)) +#endif + /// Check the attributes on an entity, returning errors and warnings as data. let CheckEntityAttributes g (tcref: TyconRef) m = - if tcref.IsILTycon then + if tcref.IsILTycon then CheckILAttributes g (isByrefLikeTyconRef g m tcref) tcref.ILTyconRawMetadata.CustomAttrs m - else + else CheckFSharpAttributes g tcref.Attribs m - + +let TryGetEntityObsoleteInfo g (tcref: TyconRef) = + if tcref.IsILTycon then + TryGetILObsoleteInfo g (isByrefLikeTyconRef g range0 tcref) tcref.ILTyconRawMetadata.CustomAttrs + else + TryGetFSharpObsoleteInfo g tcref.Attribs + let CheckILEventAttributes g (tcref: TyconRef) cattrs m = CheckILAttributes g (isByrefLikeTyconRef g m tcref) cattrs m +let TryGetEventObsoleteInfo (einfo: EventInfo) = + match einfo with + | ILEvent(ILEventInfo(_, ilEventDef)) -> TryGetILObsoleteInfo einfo.TcGlobals false ilEventDef.CustomAttrs + | _ -> None + let CheckUnitOfMeasureAttributes g (measure: Measure) = let checkAttribs tm m = let attribs = @@ -569,6 +602,16 @@ let CheckMethInfoAttributes g m tyargsOpt (minfo: MethInfo) = | None -> () // no attribute = no errors } +let TryGetMethodObsoleteInfo minfo = + BindMethInfoAttributes range0 minfo + (TryGetILObsoleteInfo minfo.TcGlobals false) + (TryGetFSharpObsoleteInfo minfo.TcGlobals) +#if !NO_TYPEPROVIDERS + (TryGetProvidedObsoleteInfo minfo.TcGlobals range0) +#else + (fun _provAttribs -> None) +#endif + /// Indicate if a method has 'Obsolete', 'CompilerMessageAttribute' or 'TypeProviderEditorHideMethodsAttribute'. /// Used to suppress the item in intellisense. let MethInfoIsUnseen g (m: range) (ty: TType) minfo allowObsolete = diff --git a/src/Compiler/Checking/AttributeChecking.fsi b/src/Compiler/Checking/AttributeChecking.fsi index 8a9f0742a2..c2d9458488 100644 --- a/src/Compiler/Checking/AttributeChecking.fsi +++ b/src/Compiler/Checking/AttributeChecking.fsi @@ -55,6 +55,8 @@ val TryBindMethInfoAttribute: 'a option #endif +val TryGetMethodObsoleteInfo: minfo: MethInfo -> ObsoleteDiagnosticInfo option + val TryFindMethInfoStringAttribute: g: TcGlobals -> m: range -> attribSpec: BuiltinAttribInfo -> minfo: MethInfo -> string option @@ -66,14 +68,20 @@ val CheckILAttributesForUnseen: g: TcGlobals -> cattrs: ILAttributes -> _m: 'a - val CheckFSharpAttributesForHidden: g: TcGlobals -> attribs: Attrib list -> bool -val CheckFSharpAttributesForObsolete: g: TcGlobals -> attribs: Attrib list -> bool +val TryGetFSharpObsoleteInfo: g: TcGlobals -> attribs: Attrib list -> ObsoleteDiagnosticInfo option + +val CheckFSharpAttributesForObsolete: g: TcGlobals -> attribs: Attribs -> bool val CheckFSharpAttributesForUnseen: g: TcGlobals -> attribs: Attrib list -> _m: 'a -> allowObsolete: bool -> bool val CheckPropInfoAttributes: pinfo: PropInfo -> m: range -> OperationResult +val TryGetPropObsoleteInfo: pinfo: PropInfo -> ObsoleteDiagnosticInfo option + val CheckILFieldAttributes: g: TcGlobals -> finfo: ILFieldInfo -> m: range -> unit +val TryGetILFieldObsoleteInfo: g: TcGlobals -> finfo: ILFieldInfo -> ObsoleteDiagnosticInfo option + val CheckMethInfoAttributes: g: TcGlobals -> m: range -> tyargsOpt: 'a option -> minfo: MethInfo -> OperationResult @@ -83,6 +91,8 @@ val PropInfoIsUnseen: m: 'a -> allowObsolete: bool -> pinfo: PropInfo -> bool val CheckEntityAttributes: g: TcGlobals -> tcref: TyconRef -> m: range -> OperationResult +val TryGetEntityObsoleteInfo: g: TcGlobals -> tcref: TyconRef -> ObsoleteDiagnosticInfo option + val CheckUnionCaseAttributes: g: TcGlobals -> x: UnionCaseRef -> m: range -> OperationResult val CheckUnitOfMeasureAttributes: g: TcGlobals -> measure: Measure -> unit @@ -101,3 +111,5 @@ val IsSecurityCriticalAttribute: g: TcGlobals -> Attrib -> bool val IsAssemblyVersionAttribute: g: TcGlobals -> Attrib -> bool val CheckILEventAttributes: g: TcGlobals -> tcref: TyconRef -> cattrs: ILAttributes -> m: range -> OperationResult + +val TryGetEventObsoleteInfo: einfo: EventInfo -> ObsoleteDiagnosticInfo option diff --git a/src/Compiler/Facilities/DiagnosticsLogger.fs b/src/Compiler/Facilities/DiagnosticsLogger.fs index 118fe53d93..d5f87fb2e4 100644 --- a/src/Compiler/Facilities/DiagnosticsLogger.fs +++ b/src/Compiler/Facilities/DiagnosticsLogger.fs @@ -132,7 +132,9 @@ exception DiagnosticWithSuggestions of number: int * message: string * range: ra /// A diagnostic that is raised when enabled manually, or by default with a language feature exception DiagnosticEnabledWithLanguageFeature of number: int * message: string * range: range * enabledByLangFeature: bool -/// A diagnostic that is raised when a diagnostic is obsolete +type ObsoleteDiagnosticInfo = + | ObsoleteDiagnosticInfo of isError: bool * diagnosticId: string option * message: string option * urlFormat: string option + exception ObsoleteDiagnostic of isError: bool * diagnosticId: string option * diff --git a/src/Compiler/Facilities/DiagnosticsLogger.fsi b/src/Compiler/Facilities/DiagnosticsLogger.fsi index 9c099ab084..0f801b8b4d 100644 --- a/src/Compiler/Facilities/DiagnosticsLogger.fsi +++ b/src/Compiler/Facilities/DiagnosticsLogger.fsi @@ -87,6 +87,13 @@ exception DiagnosticWithSuggestions of identifier: string * suggestions: Suggestions +type ObsoleteDiagnosticInfo = + | ObsoleteDiagnosticInfo of + isError: bool * + diagnosticId: string option * + message: string option * + urlFormat: string option + exception ObsoleteDiagnostic of isError: bool * diagnosticId: string option * diff --git a/src/Compiler/Symbols/Symbols.fs b/src/Compiler/Symbols/Symbols.fs index d7db3ecd19..82c20374ce 100644 --- a/src/Compiler/Symbols/Symbols.fs +++ b/src/Compiler/Symbols/Symbols.fs @@ -4,6 +4,7 @@ namespace rec FSharp.Compiler.Symbols open System open System.Collections.Generic +open FSharp.Compiler.DiagnosticsLogger open Internal.Utilities.Collections open Internal.Utilities.Library open FSharp.Compiler @@ -219,6 +220,19 @@ type FSharpDisplayContext(denv: TcGlobals -> DisplayEnv) = member x.WithTopLevelPrefixGenericParameters () = FSharpDisplayContext(fun g -> (denv g).UseTopLevelPrefixGenericParameterStyle()) + +[] +type FSharpObsoleteDiagnosticInfo = + { IsError: bool + DiagnosticId: string option + Message: string option + UrlFormat: string option } + + static member FromDiagnosticInfo(info) = + let (ObsoleteDiagnosticInfo(isError, id, msg, urlFormat)) = info + { IsError = isError; DiagnosticId = id; Message = msg; UrlFormat = urlFormat } + + // delay the realization of 'item' in case it is unresolved type FSharpSymbol(cenv: SymbolEnv, item: unit -> Item, access: FSharpSymbol -> CcuThunk -> AccessorDomain -> bool) = @@ -359,6 +373,9 @@ type FSharpSymbol(cenv: SymbolEnv, item: unit -> Item, access: FSharpSymbol -> C member sym.TryGetAttribute<'T>() = sym.Attributes |> Seq.tryFind (fun attr -> attr.IsAttribute<'T>()) + abstract ObsoleteDiagnosticInfo: FSharpObsoleteDiagnosticInfo option + default _.ObsoleteDiagnosticInfo = None + type FSharpEntity(cenv: SymbolEnv, entity: EntityRef, tyargs: TType list) = inherit FSharpSymbol(cenv, (fun () -> @@ -854,6 +871,9 @@ type FSharpEntity(cenv: SymbolEnv, entity: EntityRef, tyargs: TType list) = member x.TryGetMembersFunctionsAndValues() = try x.MembersFunctionsAndValues with _ -> [||] :> _ + override this.ObsoleteDiagnosticInfo = + TryGetEntityObsoleteInfo cenv.g entity |> Option.map FSharpObsoleteDiagnosticInfo.FromDiagnosticInfo + member this.TryGetMetadataText() = match entity.TryDeref with | ValueSome _ -> @@ -1288,7 +1308,16 @@ type FSharpField(cenv: SymbolEnv, d: FSharpFieldData) = | Choice1Of3 r -> r.Accessibility | Choice2Of3 _ -> taccessPublic | Choice3Of3 _ -> taccessPublic - FSharpAccessibility access + FSharpAccessibility access + + override this.ObsoleteDiagnosticInfo = + let infoOption = + match d.TryRecdField with + | Choice1Of3 recdField -> TryGetFSharpObsoleteInfo cenv.g recdField.FieldAttribs + | Choice2Of3 ilFieldInfo -> TryGetILFieldObsoleteInfo cenv.g ilFieldInfo + | Choice3Of3 _ -> None + + infoOption |> Option.map FSharpObsoleteDiagnosticInfo.FromDiagnosticInfo member private x.V = d @@ -2530,6 +2559,17 @@ type FSharpMemberOrFunctionOrValue(cenv, d:FSharpMemberOrValData, item) = Array.append (enclosingEntityFullName.Split '.') [| x.CompiledName |]) else None + override this.ObsoleteDiagnosticInfo = + let infoOption = + match d with + | E einfo -> TryGetEventObsoleteInfo einfo + | P pinfo -> TryGetPropObsoleteInfo pinfo + | M minfo + | C minfo -> TryGetMethodObsoleteInfo minfo + | V vref -> TryGetFSharpObsoleteInfo cenv.g vref.Attribs + + infoOption |> Option.map FSharpObsoleteDiagnosticInfo.FromDiagnosticInfo + type FSharpType(cenv, ty:TType) = let isUnresolved() = diff --git a/src/Compiler/Symbols/Symbols.fsi b/src/Compiler/Symbols/Symbols.fsi index c416016583..a4676a3433 100644 --- a/src/Compiler/Symbols/Symbols.fsi +++ b/src/Compiler/Symbols/Symbols.fsi @@ -79,6 +79,13 @@ type FSharpDisplayContext = /// for example, `int list seq` becomes `seq` member WithTopLevelPrefixGenericParameters: unit -> FSharpDisplayContext +[] +type FSharpObsoleteDiagnosticInfo = + { IsError: bool + DiagnosticId: string option + Message: string option + UrlFormat: string option } + /// Represents a symbol in checked F# source code or a compiled .NET component. /// /// The subtype of the symbol may reveal further information and can be one of FSharpEntity, FSharpUnionCase @@ -148,6 +155,8 @@ type FSharpSymbol = /// Indicates if this symbol has an attribute matching the full name of the given type parameter member HasAttribute<'T> : unit -> bool + abstract ObsoleteDiagnosticInfo: FSharpObsoleteDiagnosticInfo option + /// Represents an assembly as seen by the F# language type FSharpAssembly = diff --git a/tests/FSharp.Compiler.Service.Tests/Checker.fs b/tests/FSharp.Compiler.Service.Tests/Checker.fs index 5d16f87783..e2b6f822c8 100644 --- a/tests/FSharp.Compiler.Service.Tests/Checker.fs +++ b/tests/FSharp.Compiler.Service.Tests/Checker.fs @@ -128,6 +128,9 @@ module CheckResultsExtensions = member this.GetCodeCompletionSuggestions(context: CodeCompletionContext, parseResults: FSharpParseFileResults, options: FSharpCodeCompletionOptions) = this.GetDeclarationListInfo(Some parseResults, context.Pos.Line, context.LineText, context.PartialIdentifier, options = options) + member this.GetMethodOverloads(context: ResolveContext, names: string list) = + this.GetMethodsAsSymbols(context.Pos.Line, context.Pos.Column, context.LineText, names) + [] module Checker = let getResolveContext (markedSource: string) = @@ -180,3 +183,7 @@ module Checker = let getTooltip (markedSource: string) = getTooltipWithOptions [||] markedSource + + let getMethodOverloads names (markedSource: string) = + let context, checkResults = getCheckedResolveContext markedSource + checkResults.GetMethodOverloads(context, names) diff --git a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.bsl b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.bsl index feecc4eecd..c1eeb877a2 100644 --- a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.bsl +++ b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.bsl @@ -5279,6 +5279,8 @@ FSharp.Compiler.Symbols.FSharpEntity: Microsoft.FSharp.Core.FSharpOption`1[FShar FSharp.Compiler.Symbols.FSharpEntity: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Symbols.FSharpEntity] get_DeclaringEntity() FSharp.Compiler.Symbols.FSharpEntity: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Symbols.FSharpType] BaseType FSharp.Compiler.Symbols.FSharpEntity: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Symbols.FSharpType] get_BaseType() +FSharp.Compiler.Symbols.FSharpEntity: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Symbols.ObsoleteDiagnosticInfo] ObsoleteDiagnosticInfo +FSharp.Compiler.Symbols.FSharpEntity: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Symbols.ObsoleteDiagnosticInfo] get_ObsoleteDiagnosticInfo() FSharp.Compiler.Symbols.FSharpEntity: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Text.ISourceText] TryGetMetadataText() FSharp.Compiler.Symbols.FSharpEntity: Microsoft.FSharp.Core.FSharpOption`1[System.String] BasicQualifiedName FSharp.Compiler.Symbols.FSharpEntity: Microsoft.FSharp.Core.FSharpOption`1[System.String] Namespace @@ -5415,6 +5417,8 @@ FSharp.Compiler.Symbols.FSharpField: Microsoft.FSharp.Core.FSharpOption`1[FSharp FSharp.Compiler.Symbols.FSharpField: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Symbols.FSharpEntity] get_DeclaringEntity() FSharp.Compiler.Symbols.FSharpField: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Symbols.FSharpUnionCase] DeclaringUnionCase FSharp.Compiler.Symbols.FSharpField: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Symbols.FSharpUnionCase] get_DeclaringUnionCase() +FSharp.Compiler.Symbols.FSharpField: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Symbols.ObsoleteDiagnosticInfo] ObsoleteDiagnosticInfo +FSharp.Compiler.Symbols.FSharpField: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Symbols.ObsoleteDiagnosticInfo] get_ObsoleteDiagnosticInfo() FSharp.Compiler.Symbols.FSharpField: Microsoft.FSharp.Core.FSharpOption`1[System.Object] LiteralValue FSharp.Compiler.Symbols.FSharpField: Microsoft.FSharp.Core.FSharpOption`1[System.Object] get_LiteralValue() FSharp.Compiler.Symbols.FSharpField: System.Collections.Generic.IList`1[FSharp.Compiler.Symbols.FSharpAttribute] FieldAttributes @@ -5696,6 +5700,8 @@ FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Microsoft.FSharp.Core.FSh FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue] get_EventForFSharpProperty() FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Symbols.FSharpType] FullTypeSafe FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Symbols.FSharpType] get_FullTypeSafe() +FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Symbols.ObsoleteDiagnosticInfo] ObsoleteDiagnosticInfo +FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Symbols.ObsoleteDiagnosticInfo] get_ObsoleteDiagnosticInfo() FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Text.TaggedText[]] GetReturnTypeLayout(FSharp.Compiler.Symbols.FSharpDisplayContext) FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Microsoft.FSharp.Core.FSharpOption`1[System.Collections.Generic.IList`1[FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue]] GetOverloads(Boolean) FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Microsoft.FSharp.Core.FSharpOption`1[System.Object] LiteralValue @@ -5792,6 +5798,8 @@ FSharp.Compiler.Symbols.FSharpSymbol: FSharp.Compiler.Symbols.FSharpAssembly get FSharp.Compiler.Symbols.FSharpSymbol: Int32 GetEffectivelySameAsHash() FSharp.Compiler.Symbols.FSharpSymbol: Int32 GetHashCode() FSharp.Compiler.Symbols.FSharpSymbol: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Symbols.FSharpAttribute] TryGetAttribute[T]() +FSharp.Compiler.Symbols.FSharpSymbol: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Symbols.ObsoleteDiagnosticInfo] ObsoleteDiagnosticInfo +FSharp.Compiler.Symbols.FSharpSymbol: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Symbols.ObsoleteDiagnosticInfo] get_ObsoleteDiagnosticInfo() FSharp.Compiler.Symbols.FSharpSymbol: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Text.Range] DeclarationLocation FSharp.Compiler.Symbols.FSharpSymbol: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Text.Range] ImplementationLocation FSharp.Compiler.Symbols.FSharpSymbol: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Text.Range] SignatureLocation @@ -5955,6 +5963,25 @@ FSharp.Compiler.Symbols.FSharpXmlDoc: Int32 GetHashCode(System.Collections.IEqua FSharp.Compiler.Symbols.FSharpXmlDoc: Int32 Tag FSharp.Compiler.Symbols.FSharpXmlDoc: Int32 get_Tag() FSharp.Compiler.Symbols.FSharpXmlDoc: System.String ToString() +FSharp.Compiler.Symbols.ObsoleteDiagnosticInfo: Boolean Equals(FSharp.Compiler.Symbols.ObsoleteDiagnosticInfo) +FSharp.Compiler.Symbols.ObsoleteDiagnosticInfo: Boolean Equals(FSharp.Compiler.Symbols.ObsoleteDiagnosticInfo, System.Collections.IEqualityComparer) +FSharp.Compiler.Symbols.ObsoleteDiagnosticInfo: Boolean Equals(System.Object) +FSharp.Compiler.Symbols.ObsoleteDiagnosticInfo: Boolean Equals(System.Object, System.Collections.IEqualityComparer) +FSharp.Compiler.Symbols.ObsoleteDiagnosticInfo: Boolean IsError +FSharp.Compiler.Symbols.ObsoleteDiagnosticInfo: Boolean get_IsError() +FSharp.Compiler.Symbols.ObsoleteDiagnosticInfo: Int32 CompareTo(FSharp.Compiler.Symbols.ObsoleteDiagnosticInfo) +FSharp.Compiler.Symbols.ObsoleteDiagnosticInfo: Int32 CompareTo(System.Object) +FSharp.Compiler.Symbols.ObsoleteDiagnosticInfo: Int32 CompareTo(System.Object, System.Collections.IComparer) +FSharp.Compiler.Symbols.ObsoleteDiagnosticInfo: Int32 GetHashCode() +FSharp.Compiler.Symbols.ObsoleteDiagnosticInfo: Int32 GetHashCode(System.Collections.IEqualityComparer) +FSharp.Compiler.Symbols.ObsoleteDiagnosticInfo: Microsoft.FSharp.Core.FSharpOption`1[System.String] DiagnosticId +FSharp.Compiler.Symbols.ObsoleteDiagnosticInfo: Microsoft.FSharp.Core.FSharpOption`1[System.String] Message +FSharp.Compiler.Symbols.ObsoleteDiagnosticInfo: Microsoft.FSharp.Core.FSharpOption`1[System.String] UrlFormat +FSharp.Compiler.Symbols.ObsoleteDiagnosticInfo: Microsoft.FSharp.Core.FSharpOption`1[System.String] get_DiagnosticId() +FSharp.Compiler.Symbols.ObsoleteDiagnosticInfo: Microsoft.FSharp.Core.FSharpOption`1[System.String] get_Message() +FSharp.Compiler.Symbols.ObsoleteDiagnosticInfo: Microsoft.FSharp.Core.FSharpOption`1[System.String] get_UrlFormat() +FSharp.Compiler.Symbols.ObsoleteDiagnosticInfo: System.String ToString() +FSharp.Compiler.Symbols.ObsoleteDiagnosticInfo: Void .ctor(Boolean, Microsoft.FSharp.Core.FSharpOption`1[System.String], Microsoft.FSharp.Core.FSharpOption`1[System.String], Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.Syntax.DebugPointAtBinding+Tags: Int32 NoneAtDo FSharp.Compiler.Syntax.DebugPointAtBinding+Tags: Int32 NoneAtInvisible FSharp.Compiler.Syntax.DebugPointAtBinding+Tags: Int32 NoneAtLet diff --git a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.Tests.fsproj b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.Tests.fsproj index b8f6544af1..1cbea95b31 100644 --- a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.Tests.fsproj +++ b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.Tests.fsproj @@ -27,6 +27,7 @@ + diff --git a/tests/FSharp.Compiler.Service.Tests/TypeChecker/Obsolete.fs b/tests/FSharp.Compiler.Service.Tests/TypeChecker/Obsolete.fs new file mode 100644 index 0000000000..22393f9860 --- /dev/null +++ b/tests/FSharp.Compiler.Service.Tests/TypeChecker/Obsolete.fs @@ -0,0 +1,95 @@ +module FSharp.Compiler.Service.Tests.TypeChecker.Obsolete + +open FSharp.Compiler.Service.Tests +open FSharp.Compiler.Symbols +open FSharp.Test.Assert +open Xunit + +let checkObsoleteMessages message source = + let symbolUse = Checker.getSymbolUse source + symbolUse.Symbol.ObsoleteDiagnosticInfo.Value.Message.Value |> shouldEqual message + +let checkNotObsolete source = + let symbolUse = Checker.getSymbolUse source + symbolUse.Symbol.ObsoleteDiagnosticInfo |> shouldEqual None + +[] +let ``Method 01`` () = + checkObsoleteMessages "Message" """ +type Class() = + [] + static member Method{caret}() = () +""" + +[] +let ``Method 02`` () = + checkObsoleteMessages "Message" """ +type Class() = + [] + static member Method() = () + +Class.Method{caret}() +""" + +[] +let ``Method 03`` () = + checkObsoleteMessages "Message" """ +type Class() = + static member Method(i: int) = () + + [] + static member Method(s: string) = () + +Class.Method{caret}("") +""" + +[] +let ``Method 04`` () = + checkNotObsolete """ +type Class() = + static member Method(i: int) = () + + [] + static member Method(s: string) = () + +Class.Method{caret}(1) +""" + +[] +let ``Method 05`` () = + let methodOverloads = + Checker.getMethodOverloads ["Class"; "Method"] """ +type Class() = + static member Method(i: int) = () + + [] + static member Method(s: string) = () + +Class.Method({caret}1) +""" + methodOverloads.Value + |> List.sortBy _.Symbol.DeclarationLocation.Value.StartLine + |> List.map (_.Symbol.ObsoleteDiagnosticInfo >> (Option.bind _.Message)) + |> shouldEqual [ None; Some "Message"] + +[] +let ``Type 01 - Union`` () = + checkObsoleteMessages "Message" """ +[] +type U{caret} = | A +""" + +[] +let ``Value 01`` () = + checkObsoleteMessages "Message" """ +[] +let s{caret} = "" +""" + +[] +let ``Value 02`` () = + checkObsoleteMessages "Message" """ +[] +let s = "" +let s1 = s{caret} +"""