@@ -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
@@ -231,26 +231,31 @@ let MethInfoHasAttribute g m attribSpec minfo =
231231 ( fun _ -> Some ())
232232 |> Option.isSome
233233
234- let private CheckCompilerFeatureRequiredAttribute ( g : TcGlobals ) cattrs msg m =
235- // In some cases C# will generate both ObsoleteAttribute and CompilerFeatureRequiredAttribute.
236- // Specifically, when default constructor is generated for class with any required members in them.
237- // ObsoleteAttribute should be ignored if CompilerFeatureRequiredAttribute is present, and its name is "RequiredMembers".
238- let ( AttribInfo ( tref , _ )) = g.attrib_ CompilerFeatureRequiredAttribute
239- match TryDecodeILAttribute tref cattrs with
240- | Some([ ILAttribElem.String ( Some featureName) ], _) when featureName = " RequiredMembers" ->
241- CompleteD
242- | _ ->
243- ErrorD ( ObsoleteDiagnostic( true , None, msg, None, m))
244-
234+ let private reportObsoleteDiagnostic m diagnostic =
235+ match diagnostic with
236+ | Some( ObsoleteDiagnosticInfo( isError, id, msg, urlFormat)) ->
237+ let obsoleteDiagnostic = ObsoleteDiagnostic( isError, id, msg, urlFormat, m)
238+ if isError then
239+ ErrorD( obsoleteDiagnostic)
240+ else
241+ WarnD( obsoleteDiagnostic)
242+
243+ | _ -> CompleteD
244+
245+ let private HasCompilerFeatureRequiredAttribute ( g : TcGlobals ) cattrs =
246+ match TryDecodeILAttribute g.attrib_ CompilerFeatureRequiredAttribute.TypeRef cattrs with
247+ | Some([ ILAttribElem.String( Some featureName) ], _) when featureName = " RequiredMembers" -> true
248+ | _ -> false
249+
245250let private extractILAttribValueFrom name namedArgs =
246251 match namedArgs with
247252 | ExtractILAttributeNamedArg name ( AttribElemStringArg v) -> Some v
248253 | _ -> None
249254
250- let private extractILAttributeInfo namedArgs =
255+ let private extractILObsoleteAttributeInfo namedArgs =
251256 let diagnosticId = extractILAttribValueFrom " DiagnosticId" namedArgs
252257 let urlFormat = extractILAttribValueFrom " UrlFormat" namedArgs
253- ( diagnosticId, urlFormat)
258+ diagnosticId, urlFormat
254259
255260let private CheckILExperimentalAttributes ( g : TcGlobals ) cattrs m =
256261 let ( AttribInfo ( tref , _ )) = g.attrib_ IlExperimentalAttribute
@@ -275,46 +280,39 @@ let private CheckILExperimentalAttributes (g: TcGlobals) cattrs m =
275280 | Some _
276281 | None -> CompleteD
277282
278- let private CheckILObsoleteAttributes ( g : TcGlobals ) isByrefLikeTyconRef cattrs m =
283+ let TryGetILObsoleteInfo ( g : TcGlobals ) isByrefLikeTyconRef cattrs : ObsoleteDiagnosticInfo option =
279284 if isByrefLikeTyconRef then
280- CompleteD
285+ None
281286 else
282- let ( AttribInfo ( tref , _ )) = g.attrib_ SystemObsolete
283- match TryDecodeILAttribute tref cattrs with
284- // [Obsolete]
285- // [Obsolete("Message")]
286- // [Obsolete("Message", true)]
287- // [Obsolete("Message", DiagnosticId = "DiagnosticId")]
288- // [Obsolete("Message", DiagnosticId = "DiagnosticId", UrlFormat = "UrlFormat")]
289- // [Obsolete(DiagnosticId = "DiagnosticId")]
290- // [Obsolete(DiagnosticId = "DiagnosticId", UrlFormat = "UrlFormat")]
291- // [Obsolete("Message", true, DiagnosticId = "DiagnosticId")]
292- // [Obsolete("Message", true, DiagnosticId = "DiagnosticId", UrlFormat = "UrlFormat")]
293- // Constructors deciding on IsError and Message properties.
294- | Some ([ attribElement ], namedArgs) ->
295- let diagnosticId , urlFormat = extractILAttributeInfo namedArgs
287+ match TryDecodeILAttribute g.attrib_ SystemObsolete.TypeRef cattrs with
288+ | Some([ attribElement ], namedArgs) ->
289+ let diagnosticId , urlFormat = extractILObsoleteAttributeInfo namedArgs
296290 let msg =
297291 match attribElement with
298- | ILAttribElem.String ( Some msg) -> Some msg
292+ | ILAttribElem.String( Some msg) -> Some msg
299293 | ILAttribElem.String None
300294 | _ -> None
301295
302- WarnD ( ObsoleteDiagnostic( false , diagnosticId, msg, urlFormat, m))
303- | Some ([ ILAttribElem.String msg; ILAttribElem.Bool isError ], namedArgs) ->
304- let diagnosticId , urlFormat = extractILAttributeInfo namedArgs
305- if isError then
306- if g.langVersion.SupportsFeature( LanguageFeature.RequiredPropertiesSupport) then
307- CheckCompilerFeatureRequiredAttribute g cattrs msg m
308- else
309- ErrorD ( ObsoleteDiagnostic( true , diagnosticId, msg, urlFormat, m))
310- else
311- WarnD ( ObsoleteDiagnostic( false , diagnosticId, msg, urlFormat, m))
312- // Only DiagnosticId, UrlFormat
313- | Some (_, namedArgs) ->
314- let diagnosticId , urlFormat = extractILAttributeInfo namedArgs
315- WarnD( ObsoleteDiagnostic( false , diagnosticId, None, urlFormat, m))
316- // No arguments
317- | None -> CompleteD
296+ Some( ObsoleteDiagnosticInfo( false , diagnosticId, msg, urlFormat))
297+
298+ | Some([ ILAttribElem.String msg; ILAttribElem.Bool isError ], namedArgs) ->
299+ let diagnosticId , urlFormat = extractILObsoleteAttributeInfo namedArgs
300+ Some( ObsoleteDiagnosticInfo( isError, diagnosticId, msg, urlFormat))
301+
302+ | Some(_, namedArgs) ->
303+ let diagnosticId , urlFormat = extractILObsoleteAttributeInfo namedArgs
304+ Some( ObsoleteDiagnosticInfo( false , diagnosticId, None, urlFormat))
305+
306+ | None -> None
307+
308+ let private CheckILObsoleteAttributes ( g : TcGlobals ) isByrefLikeTyconRef cattrs m =
309+ // In some cases C# will generate both ObsoleteAttribute and CompilerFeatureRequiredAttribute.
310+ // Specifically, when default constructor is generated for class with any required members in them.
311+ // ObsoleteAttribute should be ignored if CompilerFeatureRequiredAttribute is present, and its name is "RequiredMembers".
312+ if isByrefLikeTyconRef || HasCompilerFeatureRequiredAttribute g cattrs then
313+ CompleteD
314+ else
315+ TryGetILObsoleteInfo g isByrefLikeTyconRef cattrs |> reportObsoleteDiagnostic m
318316
319317/// Check IL attributes for Experimental, warnings as data
320318let private CheckILAttributes ( g : TcGlobals ) isByrefLikeTyconRef cattrs m =
@@ -332,33 +330,39 @@ let private extractObsoleteAttributeInfo namedArgs =
332330 let urlFormat = extractILAttribValueFrom " UrlFormat" namedArgs
333331 ( diagnosticId, urlFormat)
334332
333+ let TryGetFSharpObsoleteInfo g attribs : ObsoleteDiagnosticInfo option =
334+ // [<Obsolete>]
335+ // [<Obsolete("Message")>]
336+ // [<Obsolete("Message", true)>]
337+ // [<Obsolete("Message", DiagnosticId = "DiagnosticId")>]
338+ // [<Obsolete("Message", DiagnosticId = "DiagnosticId", UrlFormat = "UrlFormat")>]
339+ // [<Obsolete(DiagnosticId = "DiagnosticId")>]
340+ // [<Obsolete(DiagnosticId = "DiagnosticId", UrlFormat = "UrlFormat")>]
341+ // [<Obsolete("Message", true, DiagnosticId = "DiagnosticId")>]
342+ // [<Obsolete("Message", true, DiagnosticId = "DiagnosticId", UrlFormat = "UrlFormat")>]
343+ // Constructors deciding on IsError and Message properties.
344+ match TryFindFSharpAttribute g g.attrib_ SystemObsolete attribs with
345+ | Some( Attrib( unnamedArgs = [ AttribStringArg s ]; propVal = namedArgs)) ->
346+ let diagnosticId , urlFormat = extractObsoleteAttributeInfo namedArgs
347+ Some( ObsoleteDiagnosticInfo( false , diagnosticId, Some s, urlFormat))
348+
349+ | Some( Attrib( unnamedArgs = [ AttribStringArg s; AttribBoolArg( isError) ]; propVal = namedArgs)) ->
350+ let diagnosticId , urlFormat = extractObsoleteAttributeInfo namedArgs
351+ Some( ObsoleteDiagnosticInfo( isError, diagnosticId, Some s, urlFormat))
352+
353+ // Only DiagnosticId, UrlFormat
354+ | Some( Attrib( propVal = namedArgs)) ->
355+ let diagnosticId , urlFormat = extractObsoleteAttributeInfo namedArgs
356+ Some( ObsoleteDiagnosticInfo( false , diagnosticId, None, urlFormat))
357+
358+ | None -> None
359+
335360let private CheckObsoleteAttributes g attribs m =
336361 trackErrors {
337- match TryFindFSharpAttribute g g.attrib_ SystemObsolete attribs with
338- // [<Obsolete>]
339- // [<Obsolete("Message")>]
340- // [<Obsolete("Message", true)>]
341- // [<Obsolete("Message", DiagnosticId = "DiagnosticId")>]
342- // [<Obsolete("Message", DiagnosticId = "DiagnosticId", UrlFormat = "UrlFormat")>]
343- // [<Obsolete(DiagnosticId = "DiagnosticId")>]
344- // [<Obsolete(DiagnosticId = "DiagnosticId", UrlFormat = "UrlFormat")>]
345- // [<Obsolete("Message", true, DiagnosticId = "DiagnosticId")>]
346- // [<Obsolete("Message", true, DiagnosticId = "DiagnosticId", UrlFormat = "UrlFormat")>]
347- // Constructors deciding on IsError and Message properties.
348- | Some( Attrib( unnamedArgs= [ AttribStringArg s ]; propVal= namedArgs)) ->
349- let diagnosticId , urlFormat = extractObsoleteAttributeInfo namedArgs
350- do ! WarnD( ObsoleteDiagnostic( false , diagnosticId, Some s, urlFormat, m))
351- | Some( Attrib( unnamedArgs= [ AttribStringArg s; AttribBoolArg( isError) ]; propVal= namedArgs)) ->
352- let diagnosticId , urlFormat = extractObsoleteAttributeInfo namedArgs
353- if isError then
354- do ! ErrorD ( ObsoleteDiagnostic( true , diagnosticId, Some s, urlFormat, m))
355- else
356- do ! WarnD ( ObsoleteDiagnostic( false , diagnosticId, Some s, urlFormat, m))
357- // Only DiagnosticId, UrlFormat
358- | Some( Attrib( propVal= namedArgs)) ->
359- let diagnosticId , urlFormat = extractObsoleteAttributeInfo namedArgs
360- do ! WarnD( ObsoleteDiagnostic( false , diagnosticId, None, urlFormat, m))
361- | None -> ()
362+ match TryGetFSharpObsoleteInfo g attribs with
363+ | Some _ as diag ->
364+ do ! reportObsoleteDiagnostic m diag
365+ | _ -> ()
362366 }
363367
364368let private CheckCompilerMessageAttribute g attribs m =
@@ -420,22 +424,23 @@ let CheckFSharpAttributes (g:TcGlobals) attribs m =
420424 }
421425
422426#if ! NO_ TYPEPROVIDERS
423- /// Check a list of provided attributes for 'ObsoleteAttribute', returning errors and warnings as data
424- let private CheckProvidedAttributes ( g : TcGlobals ) m ( provAttribs : Tainted < IProvidedCustomAttributeProvider >) =
427+ let TryGetProvidedObsoleteInfo ( g : TcGlobals ) m ( provAttribs : Tainted < IProvidedCustomAttributeProvider >) : ObsoleteDiagnosticInfo option =
425428 let ( AttribInfo ( tref , _ )) = g.attrib_ SystemObsolete
426- match provAttribs.PUntaint(( fun a -> a.GetAttributeConstructorArgs( provAttribs.TypeProvider.PUntaintNoFailure( id), tref.FullName)), m) with
427- | Some ([ Some (:? string as msg) ], _) -> WarnD( ObsoleteDiagnostic( false , None, Some msg, None, m))
428- | Some ([ Some (:? string as msg); Some (:? bool as isError) ], _) ->
429- if isError then
430- ErrorD ( ObsoleteDiagnostic( true , None, Some msg, None, m))
431- else
432- WarnD ( ObsoleteDiagnostic( false , None, Some msg, None, m))
433- | Some ([ None ], _) ->
434- WarnD( ObsoleteDiagnostic( false , None, None, None, m))
435- | Some _ ->
436- WarnD( ObsoleteDiagnostic( false , None, None, None, m))
437- | None ->
438- CompleteD
429+ match provAttribs.PUntaint(_. GetAttributeConstructorArgs( provAttribs.TypeProvider.PUntaintNoFailure( id), tref.FullName), m) with
430+ | Some([ Some (:? string as msg) ], _) ->
431+ Some( ObsoleteDiagnosticInfo( false , None, Some msg, None))
432+
433+ | Some([ Some (:? string as msg); Some (:? bool as isError) ], _) ->
434+ Some( ObsoleteDiagnosticInfo( isError, None, Some msg, None))
435+
436+ | Some _ ->
437+ Some( ObsoleteDiagnosticInfo( false , None, None, None))
438+
439+ | _ -> None
440+
441+ /// Check a list of provided attributes for 'ObsoleteAttribute', returning errors and warnings as data
442+ let private CheckProvidedAttributes ( g : TcGlobals ) m ( provAttribs : Tainted < IProvidedCustomAttributeProvider >) =
443+ TryGetProvidedObsoleteInfo g m provAttribs |> reportObsoleteDiagnostic m
439444#endif
440445
441446/// Indicate if a list of IL attributes contains 'ObsoleteAttribute'. Used to suppress the item in intellisense.
@@ -496,12 +501,21 @@ let CheckPropInfoAttributes pinfo m =
496501#if ! NO_ TYPEPROVIDERS
497502 | ProvidedProp ( amap, pi, m) ->
498503 CheckProvidedAttributes amap.g m ( pi.PApply(( fun st -> ( st :> IProvidedCustomAttributeProvider)), m))
499-
500504#endif
501505
506+ let TryGetPropObsoleteInfo pinfo =
507+ match pinfo with
508+ | ILProp( ILPropInfo(_, pdef)) -> TryGetILObsoleteInfo pinfo.TcGlobals false pdef.CustomAttrs
509+ | FSProp( g, _, Some vref, _)
510+ | FSProp( g, _, _, Some vref) -> TryGetFSharpObsoleteInfo g vref.Attribs
511+ | FSProp _ -> failwith " CheckPropInfoAttributes: unreachable"
512+ #if ! NO_ TYPEPROVIDERS
513+ | ProvidedProp ( amap, pi, m) ->
514+ TryGetProvidedObsoleteInfo amap.g m ( pi.PApply(( fun st -> ( st :> IProvidedCustomAttributeProvider)), m))
515+ #endif
502516
503517/// Check the attributes associated with a IL field, returning warnings and errors as data.
504- let CheckILFieldAttributes g ( finfo : ILFieldInfo ) m =
518+ let CheckILFieldAttributes g ( finfo : ILFieldInfo ) m =
505519 match finfo with
506520 | ILFieldInfo(_, pd) ->
507521 CheckILAttributes g false pd.CustomAttrs m |> CommitOperationResult
@@ -510,16 +524,35 @@ let CheckILFieldAttributes g (finfo:ILFieldInfo) m =
510524 CheckProvidedAttributes amap.g m ( fi.PApply(( fun st -> ( st :> IProvidedCustomAttributeProvider)), m)) |> CommitOperationResult
511525#endif
512526
527+ let TryGetILFieldObsoleteInfo g ( finfo : ILFieldInfo ) =
528+ match finfo with
529+ | ILFieldInfo(_, pd) -> TryGetILObsoleteInfo g false pd.CustomAttrs
530+ #if ! NO_ TYPEPROVIDERS
531+ | ProvidedField ( amap, fi, m) ->
532+ TryGetProvidedObsoleteInfo amap.g m ( fi.PApply(( fun st -> ( st :> IProvidedCustomAttributeProvider)), m))
533+ #endif
534+
513535/// Check the attributes on an entity, returning errors and warnings as data.
514536let CheckEntityAttributes g ( tcref : TyconRef ) m =
515- if tcref.IsILTycon then
537+ if tcref.IsILTycon then
516538 CheckILAttributes g ( isByrefLikeTyconRef g m tcref) tcref.ILTyconRawMetadata.CustomAttrs m
517- else
539+ else
518540 CheckFSharpAttributes g tcref.Attribs m
519-
541+
542+ let TryGetEntityObsoleteInfo g ( tcref : TyconRef ) =
543+ if tcref.IsILTycon then
544+ TryGetILObsoleteInfo g ( isByrefLikeTyconRef g range0 tcref) tcref.ILTyconRawMetadata.CustomAttrs
545+ else
546+ TryGetFSharpObsoleteInfo g tcref.Attribs
547+
520548let CheckILEventAttributes g ( tcref : TyconRef ) cattrs m =
521549 CheckILAttributes g ( isByrefLikeTyconRef g m tcref) cattrs m
522550
551+ let TryGetEventObsoleteInfo ( einfo : EventInfo ) =
552+ match einfo with
553+ | ILEvent( ILEventInfo(_, ilEventDef)) -> TryGetILObsoleteInfo einfo.TcGlobals false ilEventDef.CustomAttrs
554+ | _ -> None
555+
523556let CheckUnitOfMeasureAttributes g ( measure : Measure ) =
524557 let checkAttribs tm m =
525558 let attribs =
@@ -569,6 +602,16 @@ let CheckMethInfoAttributes g m tyargsOpt (minfo: MethInfo) =
569602 | None -> () // no attribute = no errors
570603}
571604
605+ let TryGetMethodObsoleteInfo minfo =
606+ BindMethInfoAttributes range0 minfo
607+ ( TryGetILObsoleteInfo minfo.TcGlobals false )
608+ ( TryGetFSharpObsoleteInfo minfo.TcGlobals)
609+ #if ! NO_ TYPEPROVIDERS
610+ ( TryGetProvidedObsoleteInfo minfo.TcGlobals range0)
611+ #else
612+ ( fun _provAttribs -> None)
613+ #endif
614+
572615/// Indicate if a method has 'Obsolete', 'CompilerMessageAttribute' or 'TypeProviderEditorHideMethodsAttribute'.
573616/// Used to suppress the item in intellisense.
574617let MethInfoIsUnseen g ( m : range ) ( ty : TType ) minfo allowObsolete =
0 commit comments