@@ -6,11 +6,11 @@ module internal FSharp.Compiler.AttributeChecking
66
77open System
88open System.Collections .Generic
9+ open FSharp.Compiler .Text .Range
910open Internal.Utilities .Library
1011open FSharp.Compiler .AbstractIL .IL
1112open FSharp.Compiler
1213open FSharp.Compiler .DiagnosticsLogger
13- open FSharp.Compiler .Features
1414open FSharp.Compiler .Import
1515open FSharp.Compiler .Infos
1616open FSharp.Compiler .TcGlobals
@@ -255,25 +255,31 @@ let rec MethInfoHasWellKnownAttribute g (m: range) (ilFlag: WellKnownILAttribute
255255let 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+
268274let 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
278284let 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
342340let 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+
357382let 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
376390let 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.
525557let 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+
531569let 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+
534577let 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.
585638let MethInfoIsUnseen g ( m : range ) ( ty : TType ) minfo allowObsolete =
0 commit comments