Symbols: add ObsoleteDiagnosticInfo (#19359) · dotnet/fsharp@0d2b018 · GitHub
Skip to content

Commit 0d2b018

Browse files
authored
Symbols: add ObsoleteDiagnosticInfo (#19359)
* Symbols: add ObsoleteDiagnosticInfo * Extracts fast checks for Obsolete attribute * Fixes RequiredMembers check * Adds Checker.getMethodOverloads test helper * Release notes * Rename * Fix VisualFSharp build
1 parent d5364f2 commit 0d2b018

12 files changed

Lines changed: 343 additions & 89 deletions

File tree

‎docs/release-notes/.FSharp.Compiler.Service/11.0.100.md‎

Lines changed: 1 addition & 0 deletions

‎src/Compiler/Checking/AttributeChecking.fs‎

Lines changed: 138 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@ module internal FSharp.Compiler.AttributeChecking
66

77
open System
88
open System.Collections.Generic
9+
open FSharp.Compiler.Text.Range
910
open Internal.Utilities.Library
1011
open FSharp.Compiler.AbstractIL.IL
1112
open FSharp.Compiler
1213
open FSharp.Compiler.DiagnosticsLogger
13-
open FSharp.Compiler.Features
1414
open FSharp.Compiler.Import
1515
open FSharp.Compiler.Infos
1616
open FSharp.Compiler.TcGlobals
@@ -255,25 +255,31 @@ let rec MethInfoHasWellKnownAttribute g (m: range) (ilFlag: WellKnownILAttribute
255255
let MethInfoHasWellKnownAttributeSpec (g: TcGlobals) (m: range) (spec: WellKnownMethAttribute) (minfo: MethInfo) =
256256
MethInfoHasWellKnownAttribute g m spec.ILFlag spec.ValFlag spec.AttribInfo minfo
257257

258-
let private CheckCompilerFeatureRequiredAttribute cattrs msg m =
259-
// In some cases C# will generate both ObsoleteAttribute and CompilerFeatureRequiredAttribute.
260-
// Specifically, when default constructor is generated for class with any required members in them.
261-
// ObsoleteAttribute should be ignored if CompilerFeatureRequiredAttribute is present, and its name is "RequiredMembers".
258+
let private reportObsoleteDiagnostic m diagnostic =
259+
match diagnostic with
260+
| Some(ObsoleteDiagnosticInfo(isError, id, msg, urlFormat)) ->
261+
let obsoleteDiagnostic = ObsoleteDiagnostic(isError, id, msg, urlFormat, m)
262+
if isError then
263+
ErrorD(obsoleteDiagnostic)
264+
else
265+
WarnD(obsoleteDiagnostic)
266+
267+
| _ -> CompleteD
268+
269+
let private HasCompilerFeatureRequiredAttribute (cattrs: ILAttributes) =
262270
match cattrs with
263-
| ILAttribDecoded WellKnownILAttributes.CompilerFeatureRequiredAttribute ([ILAttribElem.String (Some featureName) ], _) when featureName = "RequiredMembers" ->
264-
CompleteD
265-
| _ ->
266-
ErrorD (ObsoleteDiagnostic(true, None, msg, None, m))
267-
271+
| ILAttribDecoded WellKnownILAttributes.CompilerFeatureRequiredAttribute ([ ILAttribElem.String(Some featureName) ], _) -> featureName = "RequiredMembers"
272+
| _ -> false
273+
268274
let private extractILAttribValueFrom name namedArgs =
269275
match namedArgs with
270276
| ExtractILAttributeNamedArg name (AttribElemStringArg v) -> Some v
271277
| _ -> None
272278

273-
let private extractILAttributeInfo namedArgs =
279+
let private extractILObsoleteAttributeInfo namedArgs =
274280
let diagnosticId = extractILAttribValueFrom "DiagnosticId" namedArgs
275281
let urlFormat = extractILAttribValueFrom "UrlFormat" namedArgs
276-
(diagnosticId, urlFormat)
282+
diagnosticId, urlFormat
277283

278284
let private CheckILExperimentalAttributes cattrs m =
279285
match cattrs with
@@ -296,47 +302,39 @@ let private CheckILExperimentalAttributes cattrs m =
296302
// Empty constructor or only UrlFormat property are not allowed.
297303
| _ -> CompleteD
298304

299-
let private CheckILObsoleteAttributes (g: TcGlobals) isByrefLikeTyconRef cattrs m =
305+
let TryGetILObsoleteInfo (g: TcGlobals) isByrefLikeTyconRef cattrs : ObsoleteDiagnosticInfo option =
300306
if isByrefLikeTyconRef then
307+
None
308+
else
309+
match TryDecodeILAttribute g.attrib_SystemObsolete.TypeRef cattrs with
310+
| Some([ attribElement ], namedArgs) ->
311+
let diagnosticId, urlFormat = extractILObsoleteAttributeInfo namedArgs
312+
let msg =
313+
match attribElement with
314+
| ILAttribElem.String(Some msg) -> Some msg
315+
| ILAttribElem.String None
316+
| _ -> None
317+
318+
Some(ObsoleteDiagnosticInfo(false, diagnosticId, msg, urlFormat))
319+
320+
| Some([ILAttribElem.String msg; ILAttribElem.Bool isError ], namedArgs) ->
321+
let diagnosticId, urlFormat = extractILObsoleteAttributeInfo namedArgs
322+
Some(ObsoleteDiagnosticInfo(isError, diagnosticId, msg, urlFormat))
323+
324+
| Some(_, namedArgs) ->
325+
let diagnosticId, urlFormat = extractILObsoleteAttributeInfo namedArgs
326+
Some(ObsoleteDiagnosticInfo(false, diagnosticId, None, urlFormat))
327+
328+
| None -> None
329+
330+
let private CheckILObsoleteAttributes (g: TcGlobals) isByrefLikeTyconRef cattrs m =
331+
// In some cases C# will generate both ObsoleteAttribute and CompilerFeatureRequiredAttribute.
332+
// Specifically, when default constructor is generated for class with any required members in them.
333+
// ObsoleteAttribute should be ignored if CompilerFeatureRequiredAttribute is present, and its name is "RequiredMembers".
334+
if isByrefLikeTyconRef || HasCompilerFeatureRequiredAttribute cattrs then
301335
CompleteD
302336
else
303-
match cattrs with
304-
// [Obsolete]
305-
// [Obsolete("Message")]
306-
// [Obsolete("Message", true)]
307-
// [Obsolete("Message", DiagnosticId = "DiagnosticId")]
308-
// [Obsolete("Message", DiagnosticId = "DiagnosticId", UrlFormat = "UrlFormat")]
309-
// [Obsolete(DiagnosticId = "DiagnosticId")]
310-
// [Obsolete(DiagnosticId = "DiagnosticId", UrlFormat = "UrlFormat")]
311-
// [Obsolete("Message", true, DiagnosticId = "DiagnosticId")]
312-
// [Obsolete("Message", true, DiagnosticId = "DiagnosticId", UrlFormat = "UrlFormat")]
313-
// Constructors deciding on IsError and Message properties.
314-
| ILAttribDecoded WellKnownILAttributes.ObsoleteAttribute decoded ->
315-
match decoded with
316-
| ([ attribElement ], namedArgs) ->
317-
let diagnosticId, urlFormat = extractILAttributeInfo namedArgs
318-
let msg =
319-
match attribElement with
320-
| ILAttribElem.String (Some msg) -> Some msg
321-
| ILAttribElem.String None
322-
| _ -> None
323-
324-
WarnD (ObsoleteDiagnostic(false, diagnosticId, msg, urlFormat, m))
325-
| ([ILAttribElem.String msg; ILAttribElem.Bool isError ], namedArgs) ->
326-
let diagnosticId, urlFormat = extractILAttributeInfo namedArgs
327-
if isError then
328-
if g.langVersion.SupportsFeature(LanguageFeature.RequiredPropertiesSupport) then
329-
CheckCompilerFeatureRequiredAttribute cattrs msg m
330-
else
331-
ErrorD (ObsoleteDiagnostic(true, diagnosticId, msg, urlFormat, m))
332-
else
333-
WarnD (ObsoleteDiagnostic(false, diagnosticId, msg, urlFormat, m))
334-
// Only DiagnosticId, UrlFormat
335-
| (_, namedArgs) ->
336-
let diagnosticId, urlFormat = extractILAttributeInfo namedArgs
337-
WarnD(ObsoleteDiagnostic(false, diagnosticId, None, urlFormat, m))
338-
// No arguments
339-
| _ -> CompleteD
337+
TryGetILObsoleteInfo g isByrefLikeTyconRef cattrs |> reportObsoleteDiagnostic m
340338

341339
/// Check IL attributes for Experimental, warnings as data
342340
let private CheckILAttributes (g: TcGlobals) isByrefLikeTyconRef cattrs m =
@@ -354,23 +352,39 @@ let private extractObsoleteAttributeInfo namedArgs =
354352
let urlFormat = extractILAttribValueFrom "UrlFormat" namedArgs
355353
(diagnosticId, urlFormat)
356354

355+
let TryGetFSharpObsoleteInfo g attribs : ObsoleteDiagnosticInfo option =
356+
// [<Obsolete>]
357+
// [<Obsolete("Message")>]
358+
// [<Obsolete("Message", true)>]
359+
// [<Obsolete("Message", DiagnosticId = "DiagnosticId")>]
360+
// [<Obsolete("Message", DiagnosticId = "DiagnosticId", UrlFormat = "UrlFormat")>]
361+
// [<Obsolete(DiagnosticId = "DiagnosticId")>]
362+
// [<Obsolete(DiagnosticId = "DiagnosticId", UrlFormat = "UrlFormat")>]
363+
// [<Obsolete("Message", true, DiagnosticId = "DiagnosticId")>]
364+
// [<Obsolete("Message", true, DiagnosticId = "DiagnosticId", UrlFormat = "UrlFormat")>]
365+
// Constructors deciding on IsError and Message properties.
366+
match TryFindFSharpAttribute g g.attrib_SystemObsolete attribs with
367+
| Some(Attrib(unnamedArgs = [ AttribStringArg s ]; propVal = namedArgs)) ->
368+
let diagnosticId, urlFormat = extractObsoleteAttributeInfo namedArgs
369+
Some(ObsoleteDiagnosticInfo(false, diagnosticId, Some s, urlFormat))
370+
371+
| Some(Attrib(unnamedArgs = [ AttribStringArg s; AttribBoolArg(isError) ]; propVal = namedArgs)) ->
372+
let diagnosticId, urlFormat = extractObsoleteAttributeInfo namedArgs
373+
Some(ObsoleteDiagnosticInfo(isError, diagnosticId, Some s, urlFormat))
374+
375+
// Only DiagnosticId, UrlFormat
376+
| Some(Attrib(propVal = namedArgs)) ->
377+
let diagnosticId, urlFormat = extractObsoleteAttributeInfo namedArgs
378+
Some(ObsoleteDiagnosticInfo(false, diagnosticId, None, urlFormat))
379+
380+
| None -> None
381+
357382
let private CheckObsoleteAttributes g attribs m =
358383
trackErrors {
359-
match attribs with
360-
| EntityAttrib g WellKnownEntityAttributes.ObsoleteAttribute (Attrib(unnamedArgs= [ AttribStringArg s ]; propVal= namedArgs)) ->
361-
let diagnosticId, urlFormat = extractObsoleteAttributeInfo namedArgs
362-
do! WarnD(ObsoleteDiagnostic(false, diagnosticId, Some s, urlFormat, m))
363-
| EntityAttrib g WellKnownEntityAttributes.ObsoleteAttribute (Attrib(unnamedArgs= [ AttribStringArg s; AttribBoolArg(isError) ]; propVal= namedArgs)) ->
364-
let diagnosticId, urlFormat = extractObsoleteAttributeInfo namedArgs
365-
if isError then
366-
do! ErrorD (ObsoleteDiagnostic(true, diagnosticId, Some s, urlFormat, m))
367-
else
368-
do! WarnD (ObsoleteDiagnostic(false, diagnosticId, Some s, urlFormat, m))
369-
// Only DiagnosticId, UrlFormat
370-
| EntityAttrib g WellKnownEntityAttributes.ObsoleteAttribute (Attrib(propVal= namedArgs)) ->
371-
let diagnosticId, urlFormat = extractObsoleteAttributeInfo namedArgs
372-
do! WarnD(ObsoleteDiagnostic(false, diagnosticId, None, urlFormat, m))
373-
| _ -> ()
384+
match TryGetFSharpObsoleteInfo g attribs with
385+
| Some _ as diag ->
386+
do! reportObsoleteDiagnostic m diag
387+
| _ -> ()
374388
}
375389

376390
let private CheckCompilerMessageAttribute g attribs m =
@@ -431,22 +445,23 @@ let CheckFSharpAttributes (g:TcGlobals) attribs m =
431445
}
432446

433447
#if !NO_TYPEPROVIDERS
434-
/// Check a list of provided attributes for 'ObsoleteAttribute', returning errors and warnings as data
435-
let private CheckProvidedAttributes (g: TcGlobals) m (provAttribs: Tainted<IProvidedCustomAttributeProvider>) =
448+
let TryGetProvidedObsoleteInfo (g: TcGlobals) m (provAttribs: Tainted<IProvidedCustomAttributeProvider>) : ObsoleteDiagnosticInfo option =
436449
let (AttribInfo(tref, _)) = g.attrib_SystemObsolete
437-
match provAttribs.PUntaint((fun a -> a.GetAttributeConstructorArgs(provAttribs.TypeProvider.PUntaintNoFailure(id), tref.FullName)), m) with
438-
| Some ([ Some (:? string as msg) ], _) -> WarnD(ObsoleteDiagnostic(false, None, Some msg, None, m))
439-
| Some ([ Some (:? string as msg); Some (:?bool as isError) ], _) ->
440-
if isError then
441-
ErrorD (ObsoleteDiagnostic(true, None, Some msg, None, m))
442-
else
443-
WarnD (ObsoleteDiagnostic(false, None, Some msg, None, m))
444-
| Some ([ None ], _) ->
445-
WarnD(ObsoleteDiagnostic(false, None, None, None, m))
446-
| Some _ ->
447-
WarnD(ObsoleteDiagnostic(false, None, None, None, m))
448-
| None ->
449-
CompleteD
450+
match provAttribs.PUntaint(_.GetAttributeConstructorArgs(provAttribs.TypeProvider.PUntaintNoFailure(id), tref.FullName), m) with
451+
| Some([ Some (:? string as msg) ], _) ->
452+
Some(ObsoleteDiagnosticInfo(false, None, Some msg, None))
453+
454+
| Some([ Some (:? string as msg); Some (:? bool as isError) ], _) ->
455+
Some(ObsoleteDiagnosticInfo(isError, None, Some msg, None))
456+
457+
| Some _ ->
458+
Some(ObsoleteDiagnosticInfo(false, None, None, None))
459+
460+
| _ -> None
461+
462+
/// Check a list of provided attributes for 'ObsoleteAttribute', returning errors and warnings as data
463+
let private CheckProvidedAttributes (g: TcGlobals) m (provAttribs: Tainted<IProvidedCustomAttributeProvider>) =
464+
TryGetProvidedObsoleteInfo g m provAttribs |> reportObsoleteDiagnostic m
450465
#endif
451466

452467
/// Indicate if IL attributes contain 'ObsoleteAttribute'. Used to suppress the item in intellisense.
@@ -507,12 +522,21 @@ let CheckPropInfoAttributes pinfo m =
507522
#if !NO_TYPEPROVIDERS
508523
| ProvidedProp (amap, pi, m) ->
509524
CheckProvidedAttributes amap.g m (pi.PApply((fun st -> (st :> IProvidedCustomAttributeProvider)), m))
510-
511525
#endif
512526

527+
let TryGetPropObsoleteInfo pinfo =
528+
match pinfo with
529+
| ILProp(ILPropInfo(_, pdef)) -> TryGetILObsoleteInfo pinfo.TcGlobals false pdef.CustomAttrs
530+
| FSProp(g, _, Some vref, _)
531+
| FSProp(g, _, _, Some vref) -> TryGetFSharpObsoleteInfo g vref.Attribs
532+
| FSProp _ -> failwith "CheckPropInfoAttributes: unreachable"
533+
#if !NO_TYPEPROVIDERS
534+
| ProvidedProp (amap, pi, m) ->
535+
TryGetProvidedObsoleteInfo amap.g m (pi.PApply((fun st -> (st :> IProvidedCustomAttributeProvider)), m))
536+
#endif
513537

514538
/// Check the attributes associated with a IL field, returning warnings and errors as data.
515-
let CheckILFieldAttributes g (finfo:ILFieldInfo) m =
539+
let CheckILFieldAttributes g (finfo: ILFieldInfo) m =
516540
match finfo with
517541
| ILFieldInfo(_, pd) ->
518542
CheckILAttributes g false pd.CustomAttrs m |> CommitOperationResult
@@ -521,16 +545,35 @@ let CheckILFieldAttributes g (finfo:ILFieldInfo) m =
521545
CheckProvidedAttributes amap.g m (fi.PApply((fun st -> (st :> IProvidedCustomAttributeProvider)), m)) |> CommitOperationResult
522546
#endif
523547

548+
let TryGetILFieldObsoleteInfo g (finfo : ILFieldInfo) =
549+
match finfo with
550+
| ILFieldInfo(_, pd) -> TryGetILObsoleteInfo g false pd.CustomAttrs
551+
#if !NO_TYPEPROVIDERS
552+
| ProvidedField (amap, fi, m) ->
553+
TryGetProvidedObsoleteInfo amap.g m (fi.PApply((fun st -> (st :> IProvidedCustomAttributeProvider)), m))
554+
#endif
555+
524556
/// Check the attributes on an entity, returning errors and warnings as data.
525557
let CheckEntityAttributes g (tcref: TyconRef) m =
526-
if tcref.IsILTycon then
558+
if tcref.IsILTycon then
527559
CheckILAttributes g (isByrefLikeTyconRef g m tcref) tcref.ILTyconRawMetadata.CustomAttrs m
528-
else
560+
else
529561
CheckFSharpAttributes g tcref.Attribs m
530-
562+
563+
let TryGetEntityObsoleteInfo g (tcref: TyconRef) =
564+
if tcref.IsILTycon then
565+
TryGetILObsoleteInfo g (isByrefLikeTyconRef g range0 tcref) tcref.ILTyconRawMetadata.CustomAttrs
566+
else
567+
TryGetFSharpObsoleteInfo g tcref.Attribs
568+
531569
let CheckILEventAttributes g (tcref: TyconRef) cattrs m =
532570
CheckILAttributes g (isByrefLikeTyconRef g m tcref) cattrs m
533571

572+
let TryGetEventObsoleteInfo (einfo: EventInfo) =
573+
match einfo with
574+
| ILEvent(ILEventInfo(_, ilEventDef)) -> TryGetILObsoleteInfo einfo.TcGlobals false ilEventDef.CustomAttrs
575+
| _ -> None
576+
534577
let CheckUnitOfMeasureAttributes g (measure: Measure) =
535578
let checkAttribs tm m =
536579
let attribs =
@@ -580,6 +623,16 @@ let CheckMethInfoAttributes g m tyargsOpt (minfo: MethInfo) =
580623
| None -> () // no attribute = no errors
581624
}
582625

626+
let TryGetMethodObsoleteInfo minfo =
627+
BindMethInfoAttributes range0 minfo
628+
(TryGetILObsoleteInfo minfo.TcGlobals false)
629+
(TryGetFSharpObsoleteInfo minfo.TcGlobals)
630+
#if !NO_TYPEPROVIDERS
631+
(TryGetProvidedObsoleteInfo minfo.TcGlobals range0)
632+
#else
633+
(fun _provAttribs -> None)
634+
#endif
635+
583636
/// Indicate if a method has 'Obsolete', 'CompilerMessageAttribute' or 'TypeProviderEditorHideMethodsAttribute'.
584637
/// Used to suppress the item in intellisense.
585638
let MethInfoIsUnseen g (m: range) (ty: TType) minfo allowObsolete =

‎src/Compiler/Checking/AttributeChecking.fsi‎

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ val TryBindMethInfoAttribute:
5555
'a option
5656
#endif
5757

58+
val TryGetMethodObsoleteInfo: minfo: MethInfo -> ObsoleteDiagnosticInfo option
59+
5860
val TryFindMethInfoStringAttribute:
5961
g: TcGlobals -> m: range -> attribSpec: BuiltinAttribInfo -> minfo: MethInfo -> string option
6062

@@ -86,14 +88,20 @@ val CheckILAttributesForUnseenStored: g: TcGlobals -> cattrsStored: ILAttributes
8688

8789
val CheckFSharpAttributesForHidden: g: TcGlobals -> attribs: Attrib list -> bool
8890

89-
val CheckFSharpAttributesForObsolete: g: TcGlobals -> attribs: Attrib list -> bool
91+
val TryGetFSharpObsoleteInfo: g: TcGlobals -> attribs: Attrib list -> ObsoleteDiagnosticInfo option
92+
93+
val CheckFSharpAttributesForObsolete: g: TcGlobals -> attribs: Attribs -> bool
9094

9195
val CheckFSharpAttributesForUnseen: g: TcGlobals -> attribs: Attrib list -> allowObsolete: bool -> bool
9296

9397
val CheckPropInfoAttributes: pinfo: PropInfo -> m: range -> OperationResult<unit>
9498

99+
val TryGetPropObsoleteInfo: pinfo: PropInfo -> ObsoleteDiagnosticInfo option
100+
95101
val CheckILFieldAttributes: g: TcGlobals -> finfo: ILFieldInfo -> m: range -> unit
96102

103+
val TryGetILFieldObsoleteInfo: g: TcGlobals -> finfo: ILFieldInfo -> ObsoleteDiagnosticInfo option
104+
97105
val CheckMethInfoAttributes:
98106
g: TcGlobals -> m: range -> tyargsOpt: 'a option -> minfo: MethInfo -> OperationResult<unit>
99107

@@ -107,6 +115,8 @@ val EventInfoIsUnseen: allowObsolete: bool -> einfo: EventInfo -> bool
107115

108116
val CheckEntityAttributes: g: TcGlobals -> tcref: TyconRef -> m: range -> OperationResult<unit>
109117

118+
val TryGetEntityObsoleteInfo: g: TcGlobals -> tcref: TyconRef -> ObsoleteDiagnosticInfo option
119+
110120
val CheckUnionCaseAttributes: g: TcGlobals -> x: UnionCaseRef -> m: range -> OperationResult<unit>
111121

112122
val CheckUnitOfMeasureAttributes: g: TcGlobals -> measure: Measure -> unit
@@ -125,3 +135,5 @@ val IsSecurityCriticalAttribute: g: TcGlobals -> Attrib -> bool
125135
val IsAssemblyVersionAttribute: g: TcGlobals -> Attrib -> bool
126136

127137
val CheckILEventAttributes: g: TcGlobals -> tcref: TyconRef -> cattrs: ILAttributes -> m: range -> OperationResult<unit>
138+
139+
val TryGetEventObsoleteInfo: einfo: EventInfo -> ObsoleteDiagnosticInfo option

‎src/Compiler/Facilities/DiagnosticsLogger.fs‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,9 @@ exception DiagnosticWithSuggestions of number: int * message: string * range: ra
132132
/// A diagnostic that is raised when enabled manually, or by default with a language feature
133133
exception DiagnosticEnabledWithLanguageFeature of number: int * message: string * range: range * enabledByLangFeature: bool
134134

135-
/// A diagnostic that is raised when a diagnostic is obsolete
135+
type ObsoleteDiagnosticInfo =
136+
| ObsoleteDiagnosticInfo of isError: bool * diagnosticId: string option * message: string option * urlFormat: string option
137+
136138
exception ObsoleteDiagnostic of
137139
isError: bool *
138140
diagnosticId: string option *

‎src/Compiler/Facilities/DiagnosticsLogger.fsi‎

Lines changed: 7 additions & 0 deletions

0 commit comments

Comments
 (0)