@@ -24,8 +24,12 @@ open FSharp.Compiler.TypeProviders
2424open FSharp.Core .CompilerServices
2525#endif
2626
27- exception ObsoleteWarning of string * range
28- exception ObsoleteError of string * range
27+ exception ObsoleteDiagnostic of
28+ isError: bool *
29+ diagnosticId: string *
30+ message: string *
31+ urlFormat: string *
32+ range: range
2933
3034let fail () = failwith " This custom attribute has an argument that cannot yet be converted using this API"
3135
@@ -234,7 +238,6 @@ let MethInfoHasAttribute g m attribSpec minfo =
234238 ( fun _ -> Some ())
235239 |> Option.isSome
236240
237-
238241let private CheckCompilerFeatureRequiredAttribute ( g : TcGlobals ) cattrs msg m =
239242 // In some cases C# will generate both ObsoleteAttribute and CompilerFeatureRequiredAttribute.
240243 // Specifically, when default constructor is generated for class with any required members in them.
@@ -244,104 +247,177 @@ let private CheckCompilerFeatureRequiredAttribute (g: TcGlobals) cattrs msg m =
244247 | Some([ ILAttribElem.String ( Some featureName) ], _) when featureName = " RequiredMembers" ->
245248 CompleteD
246249 | _ ->
247- ErrorD ( ObsoleteError( msg, m))
250+ ErrorD ( ObsoleteDiagnostic( true , " " , msg, " " , m))
251+
252+ let private extractILObsoleteAttributeInfo namedArgs =
253+ let extractILAttribValueFrom name namedArgs =
254+ match namedArgs with
255+ | ExtractILAttributeNamedArg name ( AttribElemStringArg v) -> v
256+ | _ -> " "
257+ let diagnosticId = extractILAttribValueFrom " DiagnosticId" namedArgs
258+ let urlFormat = extractILAttribValueFrom " UrlFormat" namedArgs
259+ ( diagnosticId, urlFormat)
260+
261+ let private CheckILObsoleteAttributes ( g : TcGlobals ) isByrefLikeTyconRef cattrs m =
262+ if isByrefLikeTyconRef then
263+ CompleteD
264+ else
265+ let ( AttribInfo ( tref , _ )) = g.attrib_ SystemObsolete
266+ match TryDecodeILAttribute tref cattrs with
267+ // [<Obsolete>]
268+ // [<Obsolete("Message")>]
269+ // [<Obsolete("Message", true)>]
270+ // [<Obsolete("Message", DiagnosticId = "DiagnosticId")>]
271+ // [<Obsolete("Message", DiagnosticId = "DiagnosticId", UrlFormat = "UrlFormat")>]
272+ // [<Obsolete(DiagnosticId = "DiagnosticId")>]
273+ // [<Obsolete(DiagnosticId = "DiagnosticId", UrlFormat = "UrlFormat")>]
274+ // [<Obsolete("Message", true, DiagnosticId = "DiagnosticId")>]
275+ // [<Obsolete("Message", true, DiagnosticId = "DiagnosticId", UrlFormat = "UrlFormat")>]
276+ // Constructors deciding on IsError and Message properties.
277+ | Some ([ attribElement ], namedArgs) ->
278+ let diagnosticId , urlFormat = extractILObsoleteAttributeInfo namedArgs
279+ let msg =
280+ match attribElement with
281+ | ILAttribElem.String ( Some msg) -> msg
282+ | ILAttribElem.String None
283+ | _ -> " "
284+
285+ WarnD ( ObsoleteDiagnostic( false , diagnosticId, msg, urlFormat, m))
286+ | Some ([ ILAttribElem.String ( Some msg); ILAttribElem.Bool isError ], namedArgs) ->
287+ let diagnosticId , urlFormat = extractILObsoleteAttributeInfo namedArgs
288+ if isError then
289+ if g.langVersion.SupportsFeature( LanguageFeature.RequiredPropertiesSupport) then
290+ CheckCompilerFeatureRequiredAttribute g cattrs msg m
291+ else
292+ ErrorD ( ObsoleteDiagnostic( true , diagnosticId, msg, urlFormat, m))
293+ else
294+ WarnD ( ObsoleteDiagnostic( false , diagnosticId, msg, urlFormat, m))
295+ // Only DiagnosticId, UrlFormat
296+ | Some (_, namedArgs) ->
297+ let diagnosticId , urlFormat = extractILObsoleteAttributeInfo namedArgs
298+ WarnD( ObsoleteDiagnostic( false , diagnosticId, " " , urlFormat, m))
299+ // No arguments
300+ | None -> CompleteD
248301
249302/// Check IL attributes for 'ObsoleteAttribute', returning errors and warnings as data
250303let private CheckILAttributes ( g : TcGlobals ) isByrefLikeTyconRef cattrs m =
251- let ( AttribInfo ( tref , _ )) = g.attrib_ SystemObsolete
252- match TryDecodeILAttribute tref cattrs with
253- | Some ([ ILAttribElem.String ( Some msg) ], _) when not isByrefLikeTyconRef ->
254- WarnD( ObsoleteWarning( msg, m))
255- | Some ([ ILAttribElem.String ( Some msg); ILAttribElem.Bool isError ], _) when not isByrefLikeTyconRef ->
256- if isError then
257- if g.langVersion.SupportsFeature( LanguageFeature.RequiredPropertiesSupport) then
258- CheckCompilerFeatureRequiredAttribute g cattrs msg m
259- else
260- ErrorD ( ObsoleteError( msg, m))
261- else
262- WarnD ( ObsoleteWarning( msg, m))
263- | Some ([ ILAttribElem.String None ], _) when not isByrefLikeTyconRef ->
264- WarnD( ObsoleteWarning( " " , m))
265- | Some _ when not isByrefLikeTyconRef ->
266- WarnD( ObsoleteWarning( " " , m))
267- | _ ->
268- CompleteD
304+ trackErrors {
305+ do ! CheckILObsoleteAttributes g isByrefLikeTyconRef cattrs m
306+ }
269307
270308let langVersionPrefix = " --langversion:preview"
271309
310+ let private extractObsoleteAttributeInfo namedArgs =
311+ let extractILAttribValueFrom name namedArgs =
312+ match namedArgs with
313+ | ExtractAttribNamedArg name ( AttribStringArg v) -> v
314+ | _ -> " "
315+ let diagnosticId = extractILAttribValueFrom " DiagnosticId" namedArgs
316+ let urlFormat = extractILAttribValueFrom " UrlFormat" namedArgs
317+ ( diagnosticId, urlFormat)
318+
319+ let private CheckObsoleteAttributes g attribs m =
320+ trackErrors {
321+ match TryFindFSharpAttribute g g.attrib_ SystemObsolete attribs with
322+ // [<Obsolete>]
323+ // [<Obsolete("Message")>]
324+ // [<Obsolete("Message", true)>]
325+ // [<Obsolete("Message", DiagnosticId = "DiagnosticId")>]
326+ // [<Obsolete("Message", DiagnosticId = "DiagnosticId", UrlFormat = "UrlFormat")>]
327+ // [<Obsolete(DiagnosticId = "DiagnosticId")>]
328+ // [<Obsolete(DiagnosticId = "DiagnosticId", UrlFormat = "UrlFormat")>]
329+ // [<Obsolete("Message", true, DiagnosticId = "DiagnosticId")>]
330+ // [<Obsolete("Message", true, DiagnosticId = "DiagnosticId", UrlFormat = "UrlFormat")>]
331+ // Constructors deciding on IsError and Message properties.
332+ | Some( Attrib( unnamedArgs= [ AttribStringArg s ]; propVal= namedArgs)) ->
333+ let diagnosticId , urlFormat = extractObsoleteAttributeInfo namedArgs
334+ do ! WarnD( ObsoleteDiagnostic( false , diagnosticId, s, urlFormat, m))
335+ | Some( Attrib( unnamedArgs= [ AttribStringArg s; AttribBoolArg( isError) ]; propVal= namedArgs)) ->
336+ let diagnosticId , urlFormat = extractObsoleteAttributeInfo namedArgs
337+ if isError then
338+ do ! ErrorD ( ObsoleteDiagnostic( true , diagnosticId, s, urlFormat, m))
339+ else
340+ do ! WarnD ( ObsoleteDiagnostic( false , diagnosticId, s, urlFormat, m))
341+ // Only DiagnosticId, UrlFormat
342+ | Some( Attrib( propVal= namedArgs)) ->
343+ let diagnosticId , urlFormat = extractObsoleteAttributeInfo namedArgs
344+ do ! WarnD( ObsoleteDiagnostic( false , diagnosticId, " " , urlFormat, m))
345+ | None -> ()
346+ }
347+
348+ let private CheckCompilerMessageAttribute g attribs m =
349+ trackErrors {
350+ match TryFindFSharpAttribute g g.attrib_ CompilerMessageAttribute attribs with
351+ | Some( Attrib( unnamedArgs= [ AttribStringArg s ; AttribInt32Arg n ]; propVal= namedArgs)) ->
352+ let msg = UserCompilerMessage( s, n, m)
353+ let isError =
354+ match namedArgs with
355+ | ExtractAttribNamedArg " IsError" ( AttribBoolArg v) -> v
356+ | _ -> false
357+ // If we are using a compiler that supports nameof then error 3501 is always suppressed.
358+ // See attribute on FSharp.Core 'nameof'
359+ if n = 3501 then
360+ ()
361+ elif isError && ( not g.compilingFSharpCore || n <> 1204 ) then
362+ do ! ErrorD msg
363+ else
364+ do ! WarnD msg
365+ | _ ->
366+ ()
367+ }
368+
369+ let private CheckExperimentalAttribute g attribs m =
370+ trackErrors {
371+ match TryFindFSharpAttribute g g.attrib_ ExperimentalAttribute attribs with
372+ | Some( Attrib( unnamedArgs= [ AttribStringArg( s) ])) ->
373+ let isExperimentalAttributeDisabled ( s : string ) =
374+ if g.compilingFSharpCore then
375+ true
376+ else
377+ g.langVersion.IsPreviewEnabled && ( s.IndexOf( langVersionPrefix, StringComparison.OrdinalIgnoreCase) >= 0 )
378+ if not ( isExperimentalAttributeDisabled s) then
379+ do ! WarnD( Experimental( s, m))
380+ | Some _ ->
381+ do ! WarnD( Experimental( FSComp.SR.experimentalConstruct (), m))
382+ | _ ->
383+ ()
384+ }
385+
386+ let private CheckUnverifiableAttribute g attribs m =
387+ trackErrors {
388+ match TryFindFSharpAttribute g g.attrib_ UnverifiableAttribute attribs with
389+ | Some _ ->
390+ do ! WarnD( PossibleUnverifiableCode( m))
391+ | _ -> ()
392+ }
393+
272394/// Check F# attributes for 'ObsoleteAttribute', 'CompilerMessageAttribute' and 'ExperimentalAttribute',
273395/// returning errors and warnings as data
274396let CheckFSharpAttributes ( g : TcGlobals ) attribs m =
275397 if isNil attribs then CompleteD
276398 else
277399 trackErrors {
278- match TryFindFSharpAttribute g g.attrib_ SystemObsolete attribs with
279- | Some( Attrib(_, _, [ AttribStringArg s ], _, _, _, _)) ->
280- do ! WarnD( ObsoleteWarning( s, m))
281- | Some( Attrib(_, _, [ AttribStringArg s; AttribBoolArg( isError) ], _, _, _, _)) ->
282- if isError then
283- do ! ErrorD ( ObsoleteError( s, m))
284- else
285- do ! WarnD ( ObsoleteWarning( s, m))
286- | Some _ ->
287- do ! WarnD( ObsoleteWarning( " " , m))
288- | None ->
289- ()
290-
291- match TryFindFSharpAttribute g g.attrib_ CompilerMessageAttribute attribs with
292- | Some( Attrib(_, _, [ AttribStringArg s ; AttribInt32Arg n ], namedArgs, _, _, _)) ->
293- let msg = UserCompilerMessage( s, n, m)
294- let isError =
295- match namedArgs with
296- | ExtractAttribNamedArg " IsError" ( AttribBoolArg v) -> v
297- | _ -> false
298- // If we are using a compiler that supports nameof then error 3501 is always suppressed.
299- // See attribute on FSharp.Core 'nameof'
300- if n = 3501 then
301- ()
302- elif isError && ( not g.compilingFSharpCore || n <> 1204 ) then
303- do ! ErrorD msg
304- else
305- do ! WarnD msg
306- | _ ->
307- ()
308-
309- match TryFindFSharpAttribute g g.attrib_ ExperimentalAttribute attribs with
310- | Some( Attrib(_, _, [ AttribStringArg( s) ], _, _, _, _)) ->
311- let isExperimentalAttributeDisabled ( s : string ) =
312- if g.compilingFSharpCore then
313- true
314- else
315- g.langVersion.IsPreviewEnabled && ( s.IndexOf( langVersionPrefix, StringComparison.OrdinalIgnoreCase) >= 0 )
316- if not ( isExperimentalAttributeDisabled s) then
317- do ! WarnD( Experimental( s, m))
318- | Some _ ->
319- do ! WarnD( Experimental( FSComp.SR.experimentalConstruct (), m))
320- | _ ->
321- ()
322-
323- match TryFindFSharpAttribute g g.attrib_ UnverifiableAttribute attribs with
324- | Some _ ->
325- do ! WarnD( PossibleUnverifiableCode( m))
326- | _ ->
327- ()
400+ do ! CheckObsoleteAttributes g attribs m
401+ do ! CheckCompilerMessageAttribute g attribs m
402+ do ! CheckExperimentalAttribute g attribs m
403+ do ! CheckUnverifiableAttribute g attribs m
328404 }
329405
330406#if ! NO_ TYPEPROVIDERS
331407/// Check a list of provided attributes for 'ObsoleteAttribute', returning errors and warnings as data
332408let private CheckProvidedAttributes ( g : TcGlobals ) m ( provAttribs : Tainted < IProvidedCustomAttributeProvider >) =
333409 let ( AttribInfo ( tref , _ )) = g.attrib_ SystemObsolete
334410 match provAttribs.PUntaint(( fun a -> a.GetAttributeConstructorArgs( provAttribs.TypeProvider.PUntaintNoFailure( id), tref.FullName)), m) with
335- | Some ([ Some (:? string as msg) ], _) -> WarnD( ObsoleteWarning ( msg, m))
411+ | Some ([ Some (:? string as msg) ], _) -> WarnD( ObsoleteDiagnostic ( false , " " , msg, " " , m))
336412 | Some ([ Some (:? string as msg); Some (:? bool as isError) ], _) ->
337413 if isError then
338- ErrorD ( ObsoleteError ( msg, m))
414+ ErrorD ( ObsoleteDiagnostic ( true , " " , msg, " " , m))
339415 else
340- WarnD ( ObsoleteWarning ( msg, m))
416+ WarnD ( ObsoleteDiagnostic ( false , " " , msg, " " , m))
341417 | Some ([ None ], _) ->
342- WarnD( ObsoleteWarning ( " " , m))
418+ WarnD( ObsoleteDiagnostic ( false , " " , " " , " " , m))
343419 | Some _ ->
344- WarnD( ObsoleteWarning ( " " , m))
420+ WarnD( ObsoleteDiagnostic ( false , " " , " " , " " , m))
345421 | None ->
346422 CompleteD
347423#endif
0 commit comments