From 18830f41f2d0b3ea012a8aeb71d824bf77bbd722 Mon Sep 17 00:00:00 2001 From: Edgar Gonzalez Date: Fri, 10 Jan 2025 09:18:03 +0000 Subject: [PATCH 1/8] Update `Obsolete` attribute checking to account for `diagnosticId` and `urlFormat` properties --- src/Compiler/Checking/AttributeChecking.fs | 188 ++++++++++++------ src/Compiler/Checking/AttributeChecking.fsi | 9 +- src/Compiler/Checking/PostInferenceChecks.fs | 2 +- src/Compiler/Driver/CompilerDiagnostics.fs | 11 +- src/Compiler/Symbols/FSharpDiagnostic.fs | 15 +- src/Compiler/Symbols/FSharpDiagnostic.fsi | 11 + .../ExtendedDiagnosticDataTests.fs | 130 ++++++++++++ 7 files changed, 290 insertions(+), 76 deletions(-) diff --git a/src/Compiler/Checking/AttributeChecking.fs b/src/Compiler/Checking/AttributeChecking.fs index 563af942829..10787536721 100644 --- a/src/Compiler/Checking/AttributeChecking.fs +++ b/src/Compiler/Checking/AttributeChecking.fs @@ -24,8 +24,12 @@ open FSharp.Compiler.TypeProviders open FSharp.Core.CompilerServices #endif -exception ObsoleteWarning of string * range -exception ObsoleteError of string * range +exception ObsoleteDiagnostic of + isError: bool * + diagnosticId: string option * + message: string * + urlFormat: string option * + range: range let fail() = failwith "This custom attribute has an argument that cannot yet be converted using this API" @@ -234,7 +238,6 @@ 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. @@ -244,87 +247,144 @@ let private CheckCompilerFeatureRequiredAttribute (g: TcGlobals) cattrs msg m = | Some([ILAttribElem.String (Some featureName) ], _) when featureName = "RequiredMembers" -> CompleteD | _ -> - ErrorD (ObsoleteError(msg, m)) + ErrorD (ObsoleteDiagnostic(true, None, msg, None, m)) /// Check IL attributes for 'ObsoleteAttribute', returning errors and warnings as data let private CheckILAttributes (g: TcGlobals) isByrefLikeTyconRef cattrs m = let (AttribInfo(tref,_)) = g.attrib_SystemObsolete match TryDecodeILAttribute tref cattrs with | Some ([ILAttribElem.String (Some msg) ], _) when not isByrefLikeTyconRef -> - WarnD(ObsoleteWarning(msg, m)) + WarnD(ObsoleteDiagnostic(false, None, msg, None, m)) | Some ([ILAttribElem.String (Some msg); ILAttribElem.Bool isError ], _) when not isByrefLikeTyconRef -> if isError then if g.langVersion.SupportsFeature(LanguageFeature.RequiredPropertiesSupport) then CheckCompilerFeatureRequiredAttribute g cattrs msg m else - ErrorD (ObsoleteError(msg, m)) + ErrorD (ObsoleteDiagnostic(true, None, msg, None, m)) else - WarnD (ObsoleteWarning(msg, m)) + WarnD (ObsoleteDiagnostic(false, None, msg, None, m)) + | Some ([ILAttribElem.String None ], _) when not isByrefLikeTyconRef -> - WarnD(ObsoleteWarning("", m)) + WarnD(ObsoleteDiagnostic(false, None, "", None, m)) | Some _ when not isByrefLikeTyconRef -> - WarnD(ObsoleteWarning("", m)) + WarnD(ObsoleteDiagnostic(false, None, "",None, m)) | _ -> CompleteD let langVersionPrefix = "--langversion:preview" +let private CheckObsoleteAttributes g attribs m = + trackErrors { + match TryFindFSharpAttribute g g.attrib_SystemObsolete attribs with + | Some(Attrib(unnamedArgs= [ AttribStringArg s ]; propVal= namedArgs)) -> + let diagnosticId = + match namedArgs with + | ExtractAttribNamedArg "DiagnosticId" (AttribStringArg v) -> Some v + | _ -> None + + let urlFormat = + match namedArgs with + | ExtractAttribNamedArg "UrlFormat" (AttribStringArg v) -> Some v + | _ -> None + do! WarnD(ObsoleteDiagnostic(false, diagnosticId, s, urlFormat, m)) + + | Some(Attrib(unnamedArgs= [ AttribStringArg s; AttribBoolArg(isError) ]; propVal= namedArgs)) -> + let diagnosticId = + match namedArgs with + | ExtractAttribNamedArg "DiagnosticId" (AttribStringArg v) -> Some v + | _ -> None + + let urlFormat = + match namedArgs with + | ExtractAttribNamedArg "UrlFormat" (AttribStringArg v) -> Some v + | _ -> None + + if isError then + do! ErrorD (ObsoleteDiagnostic(true, diagnosticId, s, urlFormat, m)) + else + do! WarnD (ObsoleteDiagnostic(false, diagnosticId, s, urlFormat, m)) + | Some(Attrib(unnamedArgs= [ AttribStringArg s ]; propVal= namedArgs)) -> + let diagnosticId = + match namedArgs with + | ExtractAttribNamedArg "DiagnosticId" (AttribStringArg v) -> Some v + | _ -> None + + let urlFormat = + match namedArgs with + | ExtractAttribNamedArg "UrlFormat" (AttribStringArg v) -> Some v + | _ -> None + do! WarnD(ObsoleteDiagnostic(false, diagnosticId, s, urlFormat, m)) + | Some(Attrib(propVal= namedArgs)) -> + let diagnosticId = + match namedArgs with + | ExtractAttribNamedArg "DiagnosticId" (AttribStringArg v) -> Some v + | _ -> None + + let urlFormat = + match namedArgs with + | ExtractAttribNamedArg "UrlFormat" (AttribStringArg v) -> Some v + | _ -> None + do! WarnD(ObsoleteDiagnostic(false, diagnosticId, "", urlFormat, m)) + | None -> + () + } + +let private CheckCompilerMessageAttribute g attribs m = + trackErrors { + match TryFindFSharpAttribute g g.attrib_CompilerMessageAttribute attribs with + | Some(Attrib(unnamedArgs= [ AttribStringArg s ; AttribInt32Arg n ]; propVal= namedArgs)) -> + let msg = UserCompilerMessage(s, n, m) + let isError = + match namedArgs with + | ExtractAttribNamedArg "IsError" (AttribBoolArg v) -> v + | _ -> false + // If we are using a compiler that supports nameof then error 3501 is always suppressed. + // See attribute on FSharp.Core 'nameof' + if n = 3501 then + () + elif isError && (not g.compilingFSharpCore || n <> 1204) then + do! ErrorD msg + else + do! WarnD msg + | _ -> + () + } + +let private CheckExperimentalAttribute g attribs m = + trackErrors { + match TryFindFSharpAttribute g g.attrib_ExperimentalAttribute attribs with + | Some(Attrib(unnamedArgs= [ AttribStringArg(s) ])) -> + let isExperimentalAttributeDisabled (s:string) = + if g.compilingFSharpCore then + true + else + g.langVersion.IsPreviewEnabled && (s.IndexOf(langVersionPrefix, StringComparison.OrdinalIgnoreCase) >= 0) + if not (isExperimentalAttributeDisabled s) then + do! WarnD(Experimental(s, m)) + | Some _ -> + do! WarnD(Experimental(FSComp.SR.experimentalConstruct (), m)) + | _ -> + () + } + +let private CheckUnverifiableAttribute g attribs m = + trackErrors { + match TryFindFSharpAttribute g g.attrib_UnverifiableAttribute attribs with + | Some _ -> + do! WarnD(PossibleUnverifiableCode(m)) + | _ -> () + } + /// Check F# attributes for 'ObsoleteAttribute', 'CompilerMessageAttribute' and 'ExperimentalAttribute', /// returning errors and warnings as data let CheckFSharpAttributes (g:TcGlobals) attribs m = if isNil attribs then CompleteD else trackErrors { - match TryFindFSharpAttribute g g.attrib_SystemObsolete attribs with - | Some(Attrib(_, _, [ AttribStringArg s ], _, _, _, _)) -> - do! WarnD(ObsoleteWarning(s, m)) - | Some(Attrib(_, _, [ AttribStringArg s; AttribBoolArg(isError) ], _, _, _, _)) -> - if isError then - do! ErrorD (ObsoleteError(s, m)) - else - do! WarnD (ObsoleteWarning(s, m)) - | Some _ -> - do! WarnD(ObsoleteWarning("", m)) - | None -> - () - - match TryFindFSharpAttribute g g.attrib_CompilerMessageAttribute attribs with - | Some(Attrib(_, _, [ AttribStringArg s ; AttribInt32Arg n ], namedArgs, _, _, _)) -> - let msg = UserCompilerMessage(s, n, m) - let isError = - match namedArgs with - | ExtractAttribNamedArg "IsError" (AttribBoolArg v) -> v - | _ -> false - // If we are using a compiler that supports nameof then error 3501 is always suppressed. - // See attribute on FSharp.Core 'nameof' - if n = 3501 then - () - elif isError && (not g.compilingFSharpCore || n <> 1204) then - do! ErrorD msg - else - do! WarnD msg - | _ -> - () - - match TryFindFSharpAttribute g g.attrib_ExperimentalAttribute attribs with - | Some(Attrib(_, _, [ AttribStringArg(s) ], _, _, _, _)) -> - let isExperimentalAttributeDisabled (s:string) = - if g.compilingFSharpCore then - true - else - g.langVersion.IsPreviewEnabled && (s.IndexOf(langVersionPrefix, StringComparison.OrdinalIgnoreCase) >= 0) - if not (isExperimentalAttributeDisabled s) then - do! WarnD(Experimental(s, m)) - | Some _ -> - do! WarnD(Experimental(FSComp.SR.experimentalConstruct (), m)) - | _ -> - () - - match TryFindFSharpAttribute g g.attrib_UnverifiableAttribute attribs with - | Some _ -> - do! WarnD(PossibleUnverifiableCode(m)) - | _ -> - () + do! CheckObsoleteAttributes g attribs m + do! CheckCompilerMessageAttribute g attribs m + do! CheckExperimentalAttribute g attribs m + do! CheckUnverifiableAttribute g attribs m } #if !NO_TYPEPROVIDERS @@ -332,16 +392,16 @@ let CheckFSharpAttributes (g:TcGlobals) attribs m = let private CheckProvidedAttributes (g: TcGlobals) m (provAttribs: Tainted) = 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(ObsoleteWarning(msg, m)) + | Some ([ Some (:? string as msg) ], _) -> WarnD(ObsoleteDiagnostic(false, None, msg, None, m)) | Some ([ Some (:? string as msg); Some (:?bool as isError) ], _) -> if isError then - ErrorD (ObsoleteError(msg, m)) + ErrorD (ObsoleteDiagnostic(true, None, msg, None, m)) else - WarnD (ObsoleteWarning(msg, m)) + WarnD (ObsoleteDiagnostic(false, None, msg, None, m)) | Some ([ None ], _) -> - WarnD(ObsoleteWarning("", m)) + WarnD(ObsoleteDiagnostic(false, None, "", None, m)) | Some _ -> - WarnD(ObsoleteWarning("", m)) + WarnD(ObsoleteDiagnostic(false, None, "", None, m)) | None -> CompleteD #endif diff --git a/src/Compiler/Checking/AttributeChecking.fsi b/src/Compiler/Checking/AttributeChecking.fsi index 663198f9247..2ee029c7621 100644 --- a/src/Compiler/Checking/AttributeChecking.fsi +++ b/src/Compiler/Checking/AttributeChecking.fsi @@ -13,9 +13,12 @@ open FSharp.Compiler.TcGlobals open FSharp.Compiler.Text open FSharp.Compiler.TypedTree -exception ObsoleteWarning of string * range - -exception ObsoleteError of string * range +exception ObsoleteDiagnostic of + isError: bool * + diagnosticId: string option * + message: string * + urlFormat: string option * + range: range type AttribInfo = | FSAttribInfo of TcGlobals * Attrib diff --git a/src/Compiler/Checking/PostInferenceChecks.fs b/src/Compiler/Checking/PostInferenceChecks.fs index 645f43fe3cb..7b532521868 100644 --- a/src/Compiler/Checking/PostInferenceChecks.fs +++ b/src/Compiler/Checking/PostInferenceChecks.fs @@ -551,7 +551,7 @@ let WarnOnWrongTypeForAccess (cenv: cenv) env objName valAcc m ty = if isLessAccessible tyconAcc valAcc then let errorText = FSComp.SR.chkTypeLessAccessibleThanType(tcref.DisplayName, (objName())) |> snd let warningText = errorText + Environment.NewLine + FSComp.SR.tcTypeAbbreviationsCheckedAtCompileTime() - warning(AttributeChecking.ObsoleteWarning(warningText, m)) + warning(AttributeChecking.ObsoleteDiagnostic(false, None, warningText, None, m)) CheckTypeDeep cenv (visitType, None, None, None, None) cenv.g env NoInfo ty diff --git a/src/Compiler/Driver/CompilerDiagnostics.fs b/src/Compiler/Driver/CompilerDiagnostics.fs index 83554e74834..e4cdf2a47a8 100644 --- a/src/Compiler/Driver/CompilerDiagnostics.fs +++ b/src/Compiler/Driver/CompilerDiagnostics.fs @@ -148,8 +148,7 @@ type Exception with | IntfImplInExtrinsicAugmentation m | ValueRestriction(_, _, _, _, m) | LetRecUnsound(_, _, m) - | ObsoleteError(_, m) - | ObsoleteWarning(_, m) + | ObsoleteDiagnostic(_, _, _, _, m) | Experimental(_, m) | PossibleUnverifiableCode m | UserCompilerMessage(_, _, m) @@ -266,7 +265,7 @@ type Exception with | UnresolvedOverloading _ -> 41 | LibraryUseOnly _ -> 42 | ErrorFromAddingConstraint _ -> 43 - | ObsoleteWarning _ -> 44 + | ObsoleteDiagnostic(isError = false) -> 44 | ReservedKeyword _ -> 46 | SelfRefObjCtor _ -> 47 | VirtualAugmentationOnNullValuedType _ -> 48 @@ -327,7 +326,7 @@ type Exception with | UnresolvedConversionOperator _ -> 93 // avoid 94-100 for safety - | ObsoleteError _ -> 101 + | ObsoleteDiagnostic(isError = true) -> 101 #if !NO_TYPEPROVIDERS | TypeProviders.ProvidedTypeResolutionNoRange _ | TypeProviders.ProvidedTypeResolution _ -> 103 @@ -1791,9 +1790,7 @@ type Exception with | ValNotLocal _ -> os.AppendString(ValNotLocalE().Format) - | ObsoleteError(s, _) - - | ObsoleteWarning(s, _) -> + | ObsoleteDiagnostic(message = s) -> os.AppendString(Obsolete1E().Format) if s <> "" then diff --git a/src/Compiler/Symbols/FSharpDiagnostic.fs b/src/Compiler/Symbols/FSharpDiagnostic.fs index 31ec0536c3e..a63c96d6419 100644 --- a/src/Compiler/Symbols/FSharpDiagnostic.fs +++ b/src/Compiler/Symbols/FSharpDiagnostic.fs @@ -9,6 +9,7 @@ namespace FSharp.Compiler.Diagnostics open System +open FSharp.Compiler.AttributeChecking open FSharp.Compiler.CheckExpressions open FSharp.Compiler.ConstraintSolver open FSharp.Compiler.SignatureConformance @@ -18,7 +19,6 @@ open FSharp.Compiler.TypedTree open FSharp.Compiler.TypedTreeBasics open FSharp.Compiler.TypedTreeOps open Internal.Utilities.Library -open Internal.Utilities.Library.Extras open FSharp.Core.Printf open FSharp.Compiler @@ -67,6 +67,17 @@ module ExtendedData = [] type IFSharpDiagnosticExtendedData = interface end + /// Additional data for diagnostics about obsolete attributes. + [] + type ObsoleteDiagnosticExtendedData + internal (diagnosticId: string option, urlFormat: string option) = + interface IFSharpDiagnosticExtendedData + /// Represents the DiagnosticId of the diagnostic + member this.DiagnosticId: string option = diagnosticId + + /// Represents the URL format of the diagnostic + member this.UrlFormat: string option = urlFormat + [] type TypeMismatchDiagnosticExtendedData internal (symbolEnv: SymbolEnv, dispEnv: DisplayEnv, expectedType: TType, actualType: TType, context: DiagnosticContextInfo) = @@ -201,6 +212,8 @@ type FSharpDiagnostic(m: range, severity: FSharpDiagnosticSeverity, message: str | DefinitionsInSigAndImplNotCompatibleAbbreviationsDiffer(implTycon = implTycon; sigTycon = sigTycon) -> Some(DefinitionsInSigAndImplNotCompatibleAbbreviationsDifferExtendedData(sigTycon, implTycon)) + | ObsoleteDiagnostic(diagnosticId= diagnosticId; urlFormat= urlFormat) -> + Some(ObsoleteDiagnosticExtendedData(diagnosticId, urlFormat)) | _ -> None let msg = diff --git a/src/Compiler/Symbols/FSharpDiagnostic.fsi b/src/Compiler/Symbols/FSharpDiagnostic.fsi index 6c941bdd84d..d4c802beaee 100644 --- a/src/Compiler/Symbols/FSharpDiagnostic.fsi +++ b/src/Compiler/Symbols/FSharpDiagnostic.fsi @@ -50,6 +50,17 @@ module public ExtendedData = interface end + /// Additional data for diagnostics about obsolete attributes. + [] + type public ObsoleteDiagnosticExtendedData = + interface IFSharpDiagnosticExtendedData + + /// Represents the DiagnosticId of the diagnostic + member DiagnosticId: string option + + /// Represents the URL format of the diagnostic + member UrlFormat: string option + /// Additional data for type-mismatch-like (usually with ErrorNumber = 1) diagnostics [] type public TypeMismatchDiagnosticExtendedData = diff --git a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ExtendedDiagnosticDataTests.fs b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ExtendedDiagnosticDataTests.fs index 65a1cceb43d..931ab976314 100644 --- a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ExtendedDiagnosticDataTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ExtendedDiagnosticDataTests.fs @@ -241,3 +241,133 @@ type Foo = {| bar: int; x: int |} (fun (fieldsData: DefinitionsInSigAndImplNotCompatibleAbbreviationsDifferExtendedData) -> assertRange (4,5) (4,8) fieldsData.SignatureRange assertRange (4,6) (4,9) fieldsData.ImplementationRange) + + +[] +let ``Warning - ObsoleteDiagnosticExtendedData 01`` () = + FSharp """ +open System +[] +type MyClass() = class end + +let x = MyClass() +""" + |> typecheckResults + |> checkDiagnostic + (44, "This construct is deprecated. Message") + (fun (obsoleteDiagnostic: ObsoleteDiagnosticExtendedData) -> + Assert.Equal("FS222", obsoleteDiagnostic.DiagnosticId.Value) + Assert.Equal("https://example.com", obsoleteDiagnostic.UrlFormat.Value)) + +[] +let ``Warning - ObsoleteDiagnosticExtendedData 02`` () = + FSharp """ +open System +[] +type MyClass() = class end + +let x = MyClass() +""" + |> typecheckResults + |> checkDiagnostic + (44, "This construct is deprecated. Message") + (fun (obsoleteDiagnostic: ObsoleteDiagnosticExtendedData) -> + Assert.Equal("FS222", obsoleteDiagnostic.DiagnosticId.Value) + Assert.Equal(None, obsoleteDiagnostic.UrlFormat)) + +[] +let ``Warning - ObsoleteDiagnosticExtendedData 03`` () = + FSharp """ +open System +[] +type MyClass() = class end + +let x = MyClass() +""" + |> typecheckResults + |> checkDiagnostic + (44, "This construct is deprecated. Message") + (fun (obsoleteDiagnostic: ObsoleteDiagnosticExtendedData) -> + Assert.Equal(None, obsoleteDiagnostic.DiagnosticId) + Assert.Equal(None, obsoleteDiagnostic.UrlFormat)) + +[] +let ``Warning - ObsoleteDiagnosticExtendedData 04`` () = + FSharp """ +open System +[] +type MyClass() = class end + +let x = MyClass() +""" + |> typecheckResults + |> checkDiagnostic + (44, "This construct is deprecated") + (fun (obsoleteDiagnostic: ObsoleteDiagnosticExtendedData) -> + Assert.Equal("FS222", obsoleteDiagnostic.DiagnosticId.Value) + Assert.Equal("https://example.com", obsoleteDiagnostic.UrlFormat.Value)) + + +[] +let ``Error - ObsoleteDiagnosticExtendedData 01`` () = + FSharp """ +open System +[] +type MyClass() = class end + +let x = MyClass() +""" + |> typecheckResults + |> checkDiagnostic + (101, "This construct is deprecated. Message") + (fun (obsoleteDiagnostic: ObsoleteDiagnosticExtendedData) -> + Assert.Equal("FS222", obsoleteDiagnostic.DiagnosticId.Value) + Assert.Equal("https://example.com", obsoleteDiagnostic.UrlFormat.Value)) + +[] +let ``Error - ObsoleteDiagnosticExtendedData 02`` () = + FSharp """ +open System +[] +type MyClass() = class end + +let x = MyClass() +""" + |> typecheckResults + |> checkDiagnostic + (101, "This construct is deprecated. Message") + (fun (obsoleteDiagnostic: ObsoleteDiagnosticExtendedData) -> + Assert.Equal("FS222", obsoleteDiagnostic.DiagnosticId.Value) + Assert.Equal(None, obsoleteDiagnostic.UrlFormat)) + +[] +let ``Error - ObsoleteDiagnosticExtendedData 03`` () = + FSharp """ +open System +[] +type MyClass() = class end + +let x = MyClass() +""" + |> typecheckResults + |> checkDiagnostic + (101, "This construct is deprecated. Message") + (fun (obsoleteDiagnostic: ObsoleteDiagnosticExtendedData) -> + Assert.Equal(None, obsoleteDiagnostic.DiagnosticId) + Assert.Equal(None, obsoleteDiagnostic.UrlFormat)) + +[] +let ``Error - ObsoleteDiagnosticExtendedData 04`` () = + FSharp """ +open System +[] +type MyClass() = class end + +let x = MyClass() +""" + |> typecheckResults + |> checkDiagnostic + (101, "This construct is deprecated") + (fun (obsoleteDiagnostic: ObsoleteDiagnosticExtendedData) -> + Assert.Equal("FS222", obsoleteDiagnostic.DiagnosticId.Value) + Assert.Equal("https://example.com", obsoleteDiagnostic.UrlFormat.Value)) \ No newline at end of file From 9d68b7596767904535c9f7e1020bfe0b36a0fe45 Mon Sep 17 00:00:00 2001 From: Edgar Gonzalez Date: Fri, 10 Jan 2025 10:54:17 +0000 Subject: [PATCH 2/8] Simplify logic --- src/Compiler/Checking/AttributeChecking.fs | 77 +++++++------------ src/Compiler/Checking/AttributeChecking.fsi | 4 +- src/Compiler/Checking/PostInferenceChecks.fs | 2 +- src/Compiler/Symbols/FSharpDiagnostic.fs | 6 +- src/Compiler/Symbols/FSharpDiagnostic.fsi | 4 +- .../ExtendedDiagnosticDataTests.fs | 32 ++++---- 6 files changed, 51 insertions(+), 74 deletions(-) diff --git a/src/Compiler/Checking/AttributeChecking.fs b/src/Compiler/Checking/AttributeChecking.fs index 10787536721..7532a9eef56 100644 --- a/src/Compiler/Checking/AttributeChecking.fs +++ b/src/Compiler/Checking/AttributeChecking.fs @@ -26,9 +26,9 @@ open FSharp.Core.CompilerServices exception ObsoleteDiagnostic of isError: bool * - diagnosticId: string option * + diagnosticId: string * message: string * - urlFormat: string option * + urlFormat: string * range: range let fail() = failwith "This custom attribute has an argument that cannot yet be converted using this API" @@ -247,83 +247,60 @@ let private CheckCompilerFeatureRequiredAttribute (g: TcGlobals) cattrs msg m = | Some([ILAttribElem.String (Some featureName) ], _) when featureName = "RequiredMembers" -> CompleteD | _ -> - ErrorD (ObsoleteDiagnostic(true, None, msg, None, m)) + ErrorD (ObsoleteDiagnostic(true, "", msg, "", m)) /// Check IL attributes for 'ObsoleteAttribute', returning errors and warnings as data let private CheckILAttributes (g: TcGlobals) isByrefLikeTyconRef cattrs m = let (AttribInfo(tref,_)) = g.attrib_SystemObsolete match TryDecodeILAttribute tref cattrs with | Some ([ILAttribElem.String (Some msg) ], _) when not isByrefLikeTyconRef -> - WarnD(ObsoleteDiagnostic(false, None, msg, None, m)) + WarnD(ObsoleteDiagnostic(false, "", msg, "", m)) | Some ([ILAttribElem.String (Some msg); ILAttribElem.Bool isError ], _) when not isByrefLikeTyconRef -> if isError then if g.langVersion.SupportsFeature(LanguageFeature.RequiredPropertiesSupport) then CheckCompilerFeatureRequiredAttribute g cattrs msg m else - ErrorD (ObsoleteDiagnostic(true, None, msg, None, m)) + ErrorD (ObsoleteDiagnostic(true, "", msg, "", m)) else - WarnD (ObsoleteDiagnostic(false, None, msg, None, m)) + WarnD (ObsoleteDiagnostic(false, "", msg, "", m)) | Some ([ILAttribElem.String None ], _) when not isByrefLikeTyconRef -> - WarnD(ObsoleteDiagnostic(false, None, "", None, m)) + WarnD(ObsoleteDiagnostic(false, "", "", "", m)) | Some _ when not isByrefLikeTyconRef -> - WarnD(ObsoleteDiagnostic(false, None, "",None, m)) + WarnD(ObsoleteDiagnostic(false, "", "", "", m)) | _ -> CompleteD let langVersionPrefix = "--langversion:preview" let private CheckObsoleteAttributes g attribs m = + let extractAttribValueFrom name namedArgs = + match namedArgs with + | ExtractAttribNamedArg name (AttribStringArg v) -> v + | _ -> "" + trackErrors { match TryFindFSharpAttribute g g.attrib_SystemObsolete attribs with | Some(Attrib(unnamedArgs= [ AttribStringArg s ]; propVal= namedArgs)) -> - let diagnosticId = - match namedArgs with - | ExtractAttribNamedArg "DiagnosticId" (AttribStringArg v) -> Some v - | _ -> None - - let urlFormat = - match namedArgs with - | ExtractAttribNamedArg "UrlFormat" (AttribStringArg v) -> Some v - | _ -> None + let diagnosticId = extractAttribValueFrom "DiagnosticId" namedArgs + let urlFormat = extractAttribValueFrom "UrlFormat" namedArgs do! WarnD(ObsoleteDiagnostic(false, diagnosticId, s, urlFormat, m)) | Some(Attrib(unnamedArgs= [ AttribStringArg s; AttribBoolArg(isError) ]; propVal= namedArgs)) -> - let diagnosticId = - match namedArgs with - | ExtractAttribNamedArg "DiagnosticId" (AttribStringArg v) -> Some v - | _ -> None - - let urlFormat = - match namedArgs with - | ExtractAttribNamedArg "UrlFormat" (AttribStringArg v) -> Some v - | _ -> None - + let diagnosticId = extractAttribValueFrom "DiagnosticId" namedArgs + let urlFormat = extractAttribValueFrom "UrlFormat" namedArgs if isError then do! ErrorD (ObsoleteDiagnostic(true, diagnosticId, s, urlFormat, m)) else do! WarnD (ObsoleteDiagnostic(false, diagnosticId, s, urlFormat, m)) | Some(Attrib(unnamedArgs= [ AttribStringArg s ]; propVal= namedArgs)) -> - let diagnosticId = - match namedArgs with - | ExtractAttribNamedArg "DiagnosticId" (AttribStringArg v) -> Some v - | _ -> None - - let urlFormat = - match namedArgs with - | ExtractAttribNamedArg "UrlFormat" (AttribStringArg v) -> Some v - | _ -> None + let diagnosticId = extractAttribValueFrom "DiagnosticId" namedArgs + let urlFormat = extractAttribValueFrom "UrlFormat" namedArgs + do! WarnD(ObsoleteDiagnostic(false, diagnosticId, s, urlFormat, m)) | Some(Attrib(propVal= namedArgs)) -> - let diagnosticId = - match namedArgs with - | ExtractAttribNamedArg "DiagnosticId" (AttribStringArg v) -> Some v - | _ -> None - - let urlFormat = - match namedArgs with - | ExtractAttribNamedArg "UrlFormat" (AttribStringArg v) -> Some v - | _ -> None + let diagnosticId = extractAttribValueFrom "DiagnosticId" namedArgs + let urlFormat = extractAttribValueFrom "UrlFormat" namedArgs do! WarnD(ObsoleteDiagnostic(false, diagnosticId, "", urlFormat, m)) | None -> () @@ -392,16 +369,16 @@ let CheckFSharpAttributes (g:TcGlobals) attribs m = let private CheckProvidedAttributes (g: TcGlobals) m (provAttribs: Tainted) = 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, msg, None, m)) + | Some ([ Some (:? string as msg) ], _) -> WarnD(ObsoleteDiagnostic(false, "", msg, "", m)) | Some ([ Some (:? string as msg); Some (:?bool as isError) ], _) -> if isError then - ErrorD (ObsoleteDiagnostic(true, None, msg, None, m)) + ErrorD (ObsoleteDiagnostic(true, "", msg, "", m)) else - WarnD (ObsoleteDiagnostic(false, None, msg, None, m)) + WarnD (ObsoleteDiagnostic(false, "", msg, "", m)) | Some ([ None ], _) -> - WarnD(ObsoleteDiagnostic(false, None, "", None, m)) + WarnD(ObsoleteDiagnostic(false, "", "", "", m)) | Some _ -> - WarnD(ObsoleteDiagnostic(false, None, "", None, m)) + WarnD(ObsoleteDiagnostic(false, "", "", "", m)) | None -> CompleteD #endif diff --git a/src/Compiler/Checking/AttributeChecking.fsi b/src/Compiler/Checking/AttributeChecking.fsi index 2ee029c7621..5885c1b39e9 100644 --- a/src/Compiler/Checking/AttributeChecking.fsi +++ b/src/Compiler/Checking/AttributeChecking.fsi @@ -15,9 +15,9 @@ open FSharp.Compiler.TypedTree exception ObsoleteDiagnostic of isError: bool * - diagnosticId: string option * + diagnosticId: string * message: string * - urlFormat: string option * + urlFormat: string * range: range type AttribInfo = diff --git a/src/Compiler/Checking/PostInferenceChecks.fs b/src/Compiler/Checking/PostInferenceChecks.fs index 7b532521868..e8d6f5a5af0 100644 --- a/src/Compiler/Checking/PostInferenceChecks.fs +++ b/src/Compiler/Checking/PostInferenceChecks.fs @@ -551,7 +551,7 @@ let WarnOnWrongTypeForAccess (cenv: cenv) env objName valAcc m ty = if isLessAccessible tyconAcc valAcc then let errorText = FSComp.SR.chkTypeLessAccessibleThanType(tcref.DisplayName, (objName())) |> snd let warningText = errorText + Environment.NewLine + FSComp.SR.tcTypeAbbreviationsCheckedAtCompileTime() - warning(AttributeChecking.ObsoleteDiagnostic(false, None, warningText, None, m)) + warning(AttributeChecking.ObsoleteDiagnostic(false, "", warningText, "", m)) CheckTypeDeep cenv (visitType, None, None, None, None) cenv.g env NoInfo ty diff --git a/src/Compiler/Symbols/FSharpDiagnostic.fs b/src/Compiler/Symbols/FSharpDiagnostic.fs index a63c96d6419..581b51ab8c7 100644 --- a/src/Compiler/Symbols/FSharpDiagnostic.fs +++ b/src/Compiler/Symbols/FSharpDiagnostic.fs @@ -70,13 +70,13 @@ module ExtendedData = /// Additional data for diagnostics about obsolete attributes. [] type ObsoleteDiagnosticExtendedData - internal (diagnosticId: string option, urlFormat: string option) = + internal (diagnosticId: string, urlFormat: string) = interface IFSharpDiagnosticExtendedData /// Represents the DiagnosticId of the diagnostic - member this.DiagnosticId: string option = diagnosticId + member this.DiagnosticId: string = diagnosticId /// Represents the URL format of the diagnostic - member this.UrlFormat: string option = urlFormat + member this.UrlFormat: string = urlFormat [] type TypeMismatchDiagnosticExtendedData diff --git a/src/Compiler/Symbols/FSharpDiagnostic.fsi b/src/Compiler/Symbols/FSharpDiagnostic.fsi index d4c802beaee..ecee4c0540b 100644 --- a/src/Compiler/Symbols/FSharpDiagnostic.fsi +++ b/src/Compiler/Symbols/FSharpDiagnostic.fsi @@ -56,10 +56,10 @@ module public ExtendedData = interface IFSharpDiagnosticExtendedData /// Represents the DiagnosticId of the diagnostic - member DiagnosticId: string option + member DiagnosticId: string /// Represents the URL format of the diagnostic - member UrlFormat: string option + member UrlFormat: string /// Additional data for type-mismatch-like (usually with ErrorNumber = 1) diagnostics [] diff --git a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ExtendedDiagnosticDataTests.fs b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ExtendedDiagnosticDataTests.fs index 931ab976314..0f0f4feb142 100644 --- a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ExtendedDiagnosticDataTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ExtendedDiagnosticDataTests.fs @@ -256,8 +256,8 @@ let x = MyClass() |> checkDiagnostic (44, "This construct is deprecated. Message") (fun (obsoleteDiagnostic: ObsoleteDiagnosticExtendedData) -> - Assert.Equal("FS222", obsoleteDiagnostic.DiagnosticId.Value) - Assert.Equal("https://example.com", obsoleteDiagnostic.UrlFormat.Value)) + Assert.Equal("FS222", obsoleteDiagnostic.DiagnosticId) + Assert.Equal("https://example.com", obsoleteDiagnostic.UrlFormat)) [] let ``Warning - ObsoleteDiagnosticExtendedData 02`` () = @@ -272,8 +272,8 @@ let x = MyClass() |> checkDiagnostic (44, "This construct is deprecated. Message") (fun (obsoleteDiagnostic: ObsoleteDiagnosticExtendedData) -> - Assert.Equal("FS222", obsoleteDiagnostic.DiagnosticId.Value) - Assert.Equal(None, obsoleteDiagnostic.UrlFormat)) + Assert.Equal("FS222", obsoleteDiagnostic.DiagnosticId) + Assert.Equal("", obsoleteDiagnostic.UrlFormat)) [] let ``Warning - ObsoleteDiagnosticExtendedData 03`` () = @@ -288,8 +288,8 @@ let x = MyClass() |> checkDiagnostic (44, "This construct is deprecated. Message") (fun (obsoleteDiagnostic: ObsoleteDiagnosticExtendedData) -> - Assert.Equal(None, obsoleteDiagnostic.DiagnosticId) - Assert.Equal(None, obsoleteDiagnostic.UrlFormat)) + Assert.Equal("", obsoleteDiagnostic.DiagnosticId) + Assert.Equal("", obsoleteDiagnostic.UrlFormat)) [] let ``Warning - ObsoleteDiagnosticExtendedData 04`` () = @@ -304,8 +304,8 @@ let x = MyClass() |> checkDiagnostic (44, "This construct is deprecated") (fun (obsoleteDiagnostic: ObsoleteDiagnosticExtendedData) -> - Assert.Equal("FS222", obsoleteDiagnostic.DiagnosticId.Value) - Assert.Equal("https://example.com", obsoleteDiagnostic.UrlFormat.Value)) + Assert.Equal("FS222", obsoleteDiagnostic.DiagnosticId) + Assert.Equal("https://example.com", obsoleteDiagnostic.UrlFormat)) [] @@ -321,8 +321,8 @@ let x = MyClass() |> checkDiagnostic (101, "This construct is deprecated. Message") (fun (obsoleteDiagnostic: ObsoleteDiagnosticExtendedData) -> - Assert.Equal("FS222", obsoleteDiagnostic.DiagnosticId.Value) - Assert.Equal("https://example.com", obsoleteDiagnostic.UrlFormat.Value)) + Assert.Equal("FS222", obsoleteDiagnostic.DiagnosticId) + Assert.Equal("https://example.com", obsoleteDiagnostic.UrlFormat)) [] let ``Error - ObsoleteDiagnosticExtendedData 02`` () = @@ -337,8 +337,8 @@ let x = MyClass() |> checkDiagnostic (101, "This construct is deprecated. Message") (fun (obsoleteDiagnostic: ObsoleteDiagnosticExtendedData) -> - Assert.Equal("FS222", obsoleteDiagnostic.DiagnosticId.Value) - Assert.Equal(None, obsoleteDiagnostic.UrlFormat)) + Assert.Equal("FS222", obsoleteDiagnostic.DiagnosticId) + Assert.Equal("", obsoleteDiagnostic.UrlFormat)) [] let ``Error - ObsoleteDiagnosticExtendedData 03`` () = @@ -353,8 +353,8 @@ let x = MyClass() |> checkDiagnostic (101, "This construct is deprecated. Message") (fun (obsoleteDiagnostic: ObsoleteDiagnosticExtendedData) -> - Assert.Equal(None, obsoleteDiagnostic.DiagnosticId) - Assert.Equal(None, obsoleteDiagnostic.UrlFormat)) + Assert.Equal("", obsoleteDiagnostic.DiagnosticId) + Assert.Equal("", obsoleteDiagnostic.UrlFormat)) [] let ``Error - ObsoleteDiagnosticExtendedData 04`` () = @@ -369,5 +369,5 @@ let x = MyClass() |> checkDiagnostic (101, "This construct is deprecated") (fun (obsoleteDiagnostic: ObsoleteDiagnosticExtendedData) -> - Assert.Equal("FS222", obsoleteDiagnostic.DiagnosticId.Value) - Assert.Equal("https://example.com", obsoleteDiagnostic.UrlFormat.Value)) \ No newline at end of file + Assert.Equal("FS222", obsoleteDiagnostic.DiagnosticId) + Assert.Equal("https://example.com", obsoleteDiagnostic.UrlFormat)) \ No newline at end of file From 320bbb1d0a487044da3f8860bfd068f2beb4ef2b Mon Sep 17 00:00:00 2001 From: Edgar Gonzalez Date: Fri, 10 Jan 2025 10:54:33 +0000 Subject: [PATCH 3/8] Update baselines --- ...harp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl | 5 +++++ ...rp.Compiler.Service.SurfaceArea.netstandard20.release.bsl | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl index 6004f3fb62d..79ad3ebe2e6 100644 --- a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl +++ b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl @@ -2856,6 +2856,11 @@ FSharp.Compiler.Diagnostics.ExtendedData: FSharp.Compiler.Diagnostics.ExtendedDa FSharp.Compiler.Diagnostics.ExtendedData: FSharp.Compiler.Diagnostics.ExtendedData+IFSharpDiagnosticExtendedData FSharp.Compiler.Diagnostics.ExtendedData: FSharp.Compiler.Diagnostics.ExtendedData+TypeMismatchDiagnosticExtendedData FSharp.Compiler.Diagnostics.ExtendedData: FSharp.Compiler.Diagnostics.ExtendedData+ValueNotContainedDiagnosticExtendedData +FSharp.Compiler.Diagnostics.ExtendedData+ObsoleteDiagnosticExtendedData: System.String DiagnosticId +FSharp.Compiler.Diagnostics.ExtendedData+ObsoleteDiagnosticExtendedData: System.String UrlFormat +FSharp.Compiler.Diagnostics.ExtendedData+ObsoleteDiagnosticExtendedData: System.String get_DiagnosticId() +FSharp.Compiler.Diagnostics.ExtendedData+ObsoleteDiagnosticExtendedData: System.String get_UrlFormat() +FSharp.Compiler.Diagnostics.ExtendedData: FSharp.Compiler.Diagnostics.ExtendedData+ObsoleteDiagnosticExtendedData FSharp.Compiler.Diagnostics.FSharpDiagnostic: FSharp.Compiler.Diagnostics.FSharpDiagnostic Create(FSharp.Compiler.Diagnostics.FSharpDiagnosticSeverity, System.String, Int32, FSharp.Compiler.Text.Range, Microsoft.FSharp.Core.FSharpOption`1[System.String], Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.Diagnostics.FSharpDiagnostic: FSharp.Compiler.Diagnostics.FSharpDiagnosticSeverity Severity FSharp.Compiler.Diagnostics.FSharpDiagnostic: FSharp.Compiler.Diagnostics.FSharpDiagnosticSeverity get_Severity() diff --git a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl index 6004f3fb62d..79ad3ebe2e6 100644 --- a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl +++ b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl @@ -2856,6 +2856,11 @@ FSharp.Compiler.Diagnostics.ExtendedData: FSharp.Compiler.Diagnostics.ExtendedDa FSharp.Compiler.Diagnostics.ExtendedData: FSharp.Compiler.Diagnostics.ExtendedData+IFSharpDiagnosticExtendedData FSharp.Compiler.Diagnostics.ExtendedData: FSharp.Compiler.Diagnostics.ExtendedData+TypeMismatchDiagnosticExtendedData FSharp.Compiler.Diagnostics.ExtendedData: FSharp.Compiler.Diagnostics.ExtendedData+ValueNotContainedDiagnosticExtendedData +FSharp.Compiler.Diagnostics.ExtendedData+ObsoleteDiagnosticExtendedData: System.String DiagnosticId +FSharp.Compiler.Diagnostics.ExtendedData+ObsoleteDiagnosticExtendedData: System.String UrlFormat +FSharp.Compiler.Diagnostics.ExtendedData+ObsoleteDiagnosticExtendedData: System.String get_DiagnosticId() +FSharp.Compiler.Diagnostics.ExtendedData+ObsoleteDiagnosticExtendedData: System.String get_UrlFormat() +FSharp.Compiler.Diagnostics.ExtendedData: FSharp.Compiler.Diagnostics.ExtendedData+ObsoleteDiagnosticExtendedData FSharp.Compiler.Diagnostics.FSharpDiagnostic: FSharp.Compiler.Diagnostics.FSharpDiagnostic Create(FSharp.Compiler.Diagnostics.FSharpDiagnosticSeverity, System.String, Int32, FSharp.Compiler.Text.Range, Microsoft.FSharp.Core.FSharpOption`1[System.String], Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.Diagnostics.FSharpDiagnostic: FSharp.Compiler.Diagnostics.FSharpDiagnosticSeverity Severity FSharp.Compiler.Diagnostics.FSharpDiagnostic: FSharp.Compiler.Diagnostics.FSharpDiagnosticSeverity get_Severity() From 3d0241cd546fc38952bf58604d28bed95fa925fb Mon Sep 17 00:00:00 2001 From: Edgar Gonzalez Date: Fri, 10 Jan 2025 11:12:11 +0000 Subject: [PATCH 4/8] Update release notes --- docs/release-notes/.FSharp.Compiler.Service/9.0.300.md | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 docs/release-notes/.FSharp.Compiler.Service/9.0.300.md diff --git a/docs/release-notes/.FSharp.Compiler.Service/9.0.300.md b/docs/release-notes/.FSharp.Compiler.Service/9.0.300.md new file mode 100644 index 00000000000..4f2def94ae3 --- /dev/null +++ b/docs/release-notes/.FSharp.Compiler.Service/9.0.300.md @@ -0,0 +1,8 @@ +### Fixed + +### Added + +### Changed +* Update `Obsolete` attribute checking to account for `DiagnosticId` and `UrlFormat` properties. ([PR #18224](https://github.com/dotnet/fsharp/pull/18224)) + +### Breaking Changes \ No newline at end of file From ef436adfa24cb1d07f2a6b57033b19d1c3a63966 Mon Sep 17 00:00:00 2001 From: Edgar Gonzalez Date: Fri, 10 Jan 2025 13:39:14 +0000 Subject: [PATCH 5/8] CheckILObsoleteAttributes --- src/Compiler/Checking/AttributeChecking.fs | 40 ++-- src/Compiler/TypedTree/TypedTreeOps.fs | 6 + src/Compiler/TypedTree/TypedTreeOps.fsi | 5 + .../ExtendedDiagnosticDataTests.fs | 172 +++++++++++++++++- 4 files changed, 210 insertions(+), 13 deletions(-) diff --git a/src/Compiler/Checking/AttributeChecking.fs b/src/Compiler/Checking/AttributeChecking.fs index 7532a9eef56..1e59bca212b 100644 --- a/src/Compiler/Checking/AttributeChecking.fs +++ b/src/Compiler/Checking/AttributeChecking.fs @@ -249,28 +249,44 @@ let private CheckCompilerFeatureRequiredAttribute (g: TcGlobals) cattrs msg m = | _ -> ErrorD (ObsoleteDiagnostic(true, "", msg, "", m)) -/// Check IL attributes for 'ObsoleteAttribute', returning errors and warnings as data -let private CheckILAttributes (g: TcGlobals) isByrefLikeTyconRef cattrs m = +let private CheckILObsoleteAttributes (g: TcGlobals) isByrefLikeTyconRef cattrs m = + let extractILAttribValueFrom name namedArgs = + match namedArgs with + | ExtractILAttributeNamedArg name (AttribElemStringArg v) -> v + | _ -> "" let (AttribInfo(tref,_)) = g.attrib_SystemObsolete match TryDecodeILAttribute tref cattrs with - | Some ([ILAttribElem.String (Some msg) ], _) when not isByrefLikeTyconRef -> - WarnD(ObsoleteDiagnostic(false, "", msg, "", m)) - | Some ([ILAttribElem.String (Some msg); ILAttribElem.Bool isError ], _) when not isByrefLikeTyconRef -> + | Some ([ILAttribElem.String (Some msg) ], namedArgs) when not isByrefLikeTyconRef -> + let diagnosticId = extractILAttribValueFrom "DiagnosticId" namedArgs + let urlFormat = extractILAttribValueFrom "UrlFormat" namedArgs + WarnD(ObsoleteDiagnostic(false, diagnosticId, msg, urlFormat, m)) + | Some ([ILAttribElem.String (Some msg); ILAttribElem.Bool isError ], namedArgs) when not isByrefLikeTyconRef -> + let diagnosticId = extractILAttribValueFrom "DiagnosticId" namedArgs + let urlFormat = extractILAttribValueFrom "UrlFormat" namedArgs if isError then if g.langVersion.SupportsFeature(LanguageFeature.RequiredPropertiesSupport) then CheckCompilerFeatureRequiredAttribute g cattrs msg m else - ErrorD (ObsoleteDiagnostic(true, "", msg, "", m)) + ErrorD (ObsoleteDiagnostic(true, diagnosticId, msg, urlFormat, m)) else - WarnD (ObsoleteDiagnostic(false, "", msg, "", m)) - - | Some ([ILAttribElem.String None ], _) when not isByrefLikeTyconRef -> - WarnD(ObsoleteDiagnostic(false, "", "", "", m)) - | Some _ when not isByrefLikeTyconRef -> - WarnD(ObsoleteDiagnostic(false, "", "", "", m)) + WarnD (ObsoleteDiagnostic(false, diagnosticId, msg, urlFormat, m)) + + | Some ([ILAttribElem.String None ], namedArgs) when not isByrefLikeTyconRef -> + let diagnosticId = extractILAttribValueFrom "DiagnosticId" namedArgs + let urlFormat = extractILAttribValueFrom "UrlFormat" namedArgs + WarnD(ObsoleteDiagnostic(false, diagnosticId, "", urlFormat, m)) + | Some (_, namedArgs) when not isByrefLikeTyconRef -> + let diagnosticId = extractILAttribValueFrom "DiagnosticId" namedArgs + let urlFormat = extractILAttribValueFrom "UrlFormat" namedArgs + WarnD(ObsoleteDiagnostic(false, diagnosticId, "", urlFormat, m)) | _ -> CompleteD +/// Check IL attributes for 'ObsoleteAttribute', returning errors and warnings as data +let private CheckILAttributes (g: TcGlobals) isByrefLikeTyconRef cattrs m = + trackErrors { + do! CheckILObsoleteAttributes g isByrefLikeTyconRef cattrs m + } let langVersionPrefix = "--langversion:preview" let private CheckObsoleteAttributes g attribs m = diff --git a/src/Compiler/TypedTree/TypedTreeOps.fs b/src/Compiler/TypedTree/TypedTreeOps.fs index e2bc362c784..d2964bb8dfc 100644 --- a/src/Compiler/TypedTree/TypedTreeOps.fs +++ b/src/Compiler/TypedTree/TypedTreeOps.fs @@ -3524,6 +3524,10 @@ let IsMatchingFSharpAttributeOpt g attrOpt (Attrib(tcref2, _, _, _, _, _, _)) = [] let (|ExtractAttribNamedArg|_|) nm args = args |> List.tryPick (function AttribNamedArg(nm2, _, _, v) when nm = nm2 -> Some v | _ -> None) |> ValueOptionInternal.ofOption + +[] +let (|ExtractILAttributeNamedArg|_|) nm (args: ILAttributeNamedArg list) = + args |> List.tryPick (function nm2, _, _, v when nm = nm2 -> Some v | _ -> None) |> ValueOptionInternal.ofOption [] let (|StringExpr|_|) = function Expr.Const (Const.String n, _, _) -> ValueSome n | _ -> ValueNone @@ -3540,6 +3544,8 @@ let (|AttribBoolArg|_|) = function AttribExpr(_, Expr.Const (Const.Bool n, _, _) [] let (|AttribStringArg|_|) = function AttribExpr(_, Expr.Const (Const.String n, _, _)) -> ValueSome n | _ -> ValueNone +let (|AttribElemStringArg|_|) = function ILAttribElem.String(n) -> n | _ -> None + let TryFindFSharpBoolAttributeWithDefault dflt g nm attrs = match TryFindFSharpAttribute g nm attrs with | Some(Attrib(_, _, [ ], _, _, _, _)) -> Some dflt diff --git a/src/Compiler/TypedTree/TypedTreeOps.fsi b/src/Compiler/TypedTree/TypedTreeOps.fsi index 6433cf16d3e..d0e2205e8b2 100755 --- a/src/Compiler/TypedTree/TypedTreeOps.fsi +++ b/src/Compiler/TypedTree/TypedTreeOps.fsi @@ -2598,6 +2598,9 @@ val (|ConstToILFieldInit|_|): Const -> ILFieldInit voption [] val (|ExtractAttribNamedArg|_|): string -> AttribNamedArg list -> AttribExpr voption +[] +val (|ExtractILAttributeNamedArg|_|): string -> ILAttributeNamedArg list -> ILAttribElem voption + [] val (|AttribInt32Arg|_|): (AttribExpr -> int32 voption) @@ -2610,6 +2613,8 @@ val (|AttribBoolArg|_|): (AttribExpr -> bool voption) [] val (|AttribStringArg|_|): (AttribExpr -> string voption) +val (|AttribElemStringArg|_|): (ILAttribElem -> string option) + [] val (|Int32Expr|_|): Expr -> int32 voption diff --git a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ExtendedDiagnosticDataTests.fs b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ExtendedDiagnosticDataTests.fs index 0f0f4feb142..74db95451ae 100644 --- a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ExtendedDiagnosticDataTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ExtendedDiagnosticDataTests.fs @@ -258,7 +258,7 @@ let x = MyClass() (fun (obsoleteDiagnostic: ObsoleteDiagnosticExtendedData) -> Assert.Equal("FS222", obsoleteDiagnostic.DiagnosticId) Assert.Equal("https://example.com", obsoleteDiagnostic.UrlFormat)) - + [] let ``Warning - ObsoleteDiagnosticExtendedData 02`` () = FSharp """ @@ -307,6 +307,176 @@ let x = MyClass() Assert.Equal("FS222", obsoleteDiagnostic.DiagnosticId) Assert.Equal("https://example.com", obsoleteDiagnostic.UrlFormat)) + +[] +let ``Warning - ObsoleteDiagnosticExtendedData 05`` () = + let CSLib = + CSharp """ +using System; +[Obsolete("Use something else", false, DiagnosticId = "FS222")] +public static class Class1 +{ + public static string Test() + { + return "Hello"; + } +} + """ + |> withName "CSLib" + + let app = + FSharp """ +open MyLib + +let text = Class1.Test(); + """ |> withReferences [CSLib] + + app + |> typecheckResults + |> checkDiagnostic + (44, "This construct is deprecated. Use something else") + (fun (obsoleteDiagnostic: ObsoleteDiagnosticExtendedData) -> + Assert.Equal("FS222", obsoleteDiagnostic.DiagnosticId) + Assert.Equal("", obsoleteDiagnostic.UrlFormat)) + +[] +let ``Warning - ObsoleteDiagnosticExtendedData 06`` () = + let CSLib = + CSharp """ +using System; +[Obsolete("Use something else", false, DiagnosticId = "FS222", UrlFormat = "https://example.com")] +public static class Class1 +{ + public static string Test() + { + return "Hello"; + } +} + """ + |> withName "CSLib" + + let app = + FSharp """ +open MyLib + +let text = Class1.Test(); + """ |> withReferences [CSLib] + + app + |> typecheckResults + |> checkDiagnostic + (44, "This construct is deprecated. Use something else") + (fun (obsoleteDiagnostic: ObsoleteDiagnosticExtendedData) -> + Assert.Equal("FS222", obsoleteDiagnostic.DiagnosticId) + Assert.Equal("https://example.com", obsoleteDiagnostic.UrlFormat)) + +[] +let ``Warning - ObsoleteDiagnosticExtendedData 07`` () = + let CSLib = + CSharp """ +using System; +[Obsolete("Use something else", false)] +public static class Class1 +{ + public static string Test() + { + return "Hello"; + } +} + """ + |> withName "CSLib" + + let app = + FSharp """ +open MyLib + +let text = Class1.Test(); + """ |> withReferences [CSLib] + + app + |> typecheckResults + |> checkDiagnostic + (44, "This construct is deprecated. Use something else") + (fun (obsoleteDiagnostic: ObsoleteDiagnosticExtendedData) -> + Assert.Equal("", obsoleteDiagnostic.DiagnosticId) + Assert.Equal("", obsoleteDiagnostic.UrlFormat)) + +[] +let ``Warning - ObsoleteDiagnosticExtendedData 08`` () = + let CSLib = + CSharp """ +using System; +[Obsolete(DiagnosticId = "FS222", UrlFormat = "https://example.com")] +public static class Class1 +{ + public static string Test() + { + return "Hello"; + } +} + """ + |> withName "CSLib" + + let app = + FSharp """ +open MyLib + +let text = Class1.Test(); + """ |> withReferences [CSLib] + + app + |> typecheckResults + |> checkDiagnostic + (44, "This construct is deprecated") + (fun (obsoleteDiagnostic: ObsoleteDiagnosticExtendedData) -> + Assert.Equal("FS222", obsoleteDiagnostic.DiagnosticId) + Assert.Equal("https://example.com", obsoleteDiagnostic.UrlFormat)) + +[] +let ``Warning - ObsoleteDiagnosticExtendedData 09`` () = + FSharp """ +open System +[] +type MyClass() = class end + +let x = MyClass() +""" + |> typecheckResults + |> checkDiagnostic + (44, "This construct is deprecated") + (fun (obsoleteDiagnostic: ObsoleteDiagnosticExtendedData) -> + Assert.Equal("", obsoleteDiagnostic.DiagnosticId) + Assert.Equal("", obsoleteDiagnostic.UrlFormat)) + +let ``Warning - ObsoleteDiagnosticExtendedData 10`` () = + let CSLib = + CSharp """ +using System; +[Obsolete] +public static class Class1 +{ + public static string Test() + { + return "Hello"; + } +} + """ + |> withName "CSLib" + + let app = + FSharp """ +open MyLib + +let text = Class1.Test(); + """ |> withReferences [CSLib] + + app + |> typecheckResults + |> checkDiagnostic + (44, "This construct is deprecated") + (fun (obsoleteDiagnostic: ObsoleteDiagnosticExtendedData) -> + Assert.Equal("", obsoleteDiagnostic.DiagnosticId) + Assert.Equal("", obsoleteDiagnostic.UrlFormat)) [] let ``Error - ObsoleteDiagnosticExtendedData 01`` () = From 4ea3741ef7434c4718712ca0dc92e7537d133c14 Mon Sep 17 00:00:00 2001 From: Edgar Gonzalez Date: Fri, 10 Jan 2025 19:30:45 +0000 Subject: [PATCH 6/8] FactForNETCOREAPP --- .../ExtendedDiagnosticDataTests.fs | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ExtendedDiagnosticDataTests.fs b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ExtendedDiagnosticDataTests.fs index 74db95451ae..23e4a9b9440 100644 --- a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ExtendedDiagnosticDataTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ExtendedDiagnosticDataTests.fs @@ -243,7 +243,7 @@ type Foo = {| bar: int; x: int |} assertRange (4,6) (4,9) fieldsData.ImplementationRange) -[] +[] let ``Warning - ObsoleteDiagnosticExtendedData 01`` () = FSharp """ open System @@ -259,7 +259,7 @@ let x = MyClass() Assert.Equal("FS222", obsoleteDiagnostic.DiagnosticId) Assert.Equal("https://example.com", obsoleteDiagnostic.UrlFormat)) -[] +[] let ``Warning - ObsoleteDiagnosticExtendedData 02`` () = FSharp """ open System @@ -275,7 +275,7 @@ let x = MyClass() Assert.Equal("FS222", obsoleteDiagnostic.DiagnosticId) Assert.Equal("", obsoleteDiagnostic.UrlFormat)) -[] +[] let ``Warning - ObsoleteDiagnosticExtendedData 03`` () = FSharp """ open System @@ -291,7 +291,7 @@ let x = MyClass() Assert.Equal("", obsoleteDiagnostic.DiagnosticId) Assert.Equal("", obsoleteDiagnostic.UrlFormat)) -[] +[] let ``Warning - ObsoleteDiagnosticExtendedData 04`` () = FSharp """ open System @@ -308,7 +308,7 @@ let x = MyClass() Assert.Equal("https://example.com", obsoleteDiagnostic.UrlFormat)) -[] +[] let ``Warning - ObsoleteDiagnosticExtendedData 05`` () = let CSLib = CSharp """ @@ -339,7 +339,7 @@ let text = Class1.Test(); Assert.Equal("FS222", obsoleteDiagnostic.DiagnosticId) Assert.Equal("", obsoleteDiagnostic.UrlFormat)) -[] +[] let ``Warning - ObsoleteDiagnosticExtendedData 06`` () = let CSLib = CSharp """ @@ -370,7 +370,7 @@ let text = Class1.Test(); Assert.Equal("FS222", obsoleteDiagnostic.DiagnosticId) Assert.Equal("https://example.com", obsoleteDiagnostic.UrlFormat)) -[] +[] let ``Warning - ObsoleteDiagnosticExtendedData 07`` () = let CSLib = CSharp """ @@ -401,7 +401,7 @@ let text = Class1.Test(); Assert.Equal("", obsoleteDiagnostic.DiagnosticId) Assert.Equal("", obsoleteDiagnostic.UrlFormat)) -[] +[] let ``Warning - ObsoleteDiagnosticExtendedData 08`` () = let CSLib = CSharp """ @@ -432,7 +432,7 @@ let text = Class1.Test(); Assert.Equal("FS222", obsoleteDiagnostic.DiagnosticId) Assert.Equal("https://example.com", obsoleteDiagnostic.UrlFormat)) -[] +[] let ``Warning - ObsoleteDiagnosticExtendedData 09`` () = FSharp """ open System @@ -448,6 +448,7 @@ let x = MyClass() Assert.Equal("", obsoleteDiagnostic.DiagnosticId) Assert.Equal("", obsoleteDiagnostic.UrlFormat)) +[] let ``Warning - ObsoleteDiagnosticExtendedData 10`` () = let CSLib = CSharp """ @@ -478,7 +479,7 @@ let text = Class1.Test(); Assert.Equal("", obsoleteDiagnostic.DiagnosticId) Assert.Equal("", obsoleteDiagnostic.UrlFormat)) -[] +[] let ``Error - ObsoleteDiagnosticExtendedData 01`` () = FSharp """ open System @@ -494,7 +495,7 @@ let x = MyClass() Assert.Equal("FS222", obsoleteDiagnostic.DiagnosticId) Assert.Equal("https://example.com", obsoleteDiagnostic.UrlFormat)) -[] +[] let ``Error - ObsoleteDiagnosticExtendedData 02`` () = FSharp """ open System @@ -510,7 +511,7 @@ let x = MyClass() Assert.Equal("FS222", obsoleteDiagnostic.DiagnosticId) Assert.Equal("", obsoleteDiagnostic.UrlFormat)) -[] +[] let ``Error - ObsoleteDiagnosticExtendedData 03`` () = FSharp """ open System @@ -526,7 +527,7 @@ let x = MyClass() Assert.Equal("", obsoleteDiagnostic.DiagnosticId) Assert.Equal("", obsoleteDiagnostic.UrlFormat)) -[] +[] let ``Error - ObsoleteDiagnosticExtendedData 04`` () = FSharp """ open System From 2e294176e799b6be495ff7c645b93f10a9714b41 Mon Sep 17 00:00:00 2001 From: Edgar Gonzalez Date: Tue, 14 Jan 2025 13:11:32 +0000 Subject: [PATCH 7/8] Fix release notes --- docs/release-notes/.FSharp.Compiler.Service/9.0.300.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/release-notes/.FSharp.Compiler.Service/9.0.300.md b/docs/release-notes/.FSharp.Compiler.Service/9.0.300.md index b7f82512e38..650022ea05e 100644 --- a/docs/release-notes/.FSharp.Compiler.Service/9.0.300.md +++ b/docs/release-notes/.FSharp.Compiler.Service/9.0.300.md @@ -1,4 +1,5 @@ ### Fixed +* Fix Realsig+ generates nested closures with incorrect Generic ([Issue #17797](https://github.com/dotnet/fsharp/issues/17797), [PR #17877](https://github.com/dotnet/fsharp/pull/17877)) ### Added @@ -6,4 +7,3 @@ * Update `Obsolete` attribute checking to account for `DiagnosticId` and `UrlFormat` properties. ([PR #18224](https://github.com/dotnet/fsharp/pull/18224)) ### Breaking Changes -* Fix Realsig+ generates nested closures with incorrect Generic ([Issue #17797](https://github.com/dotnet/fsharp/issues/17797), [PR #17877](https://github.com/dotnet/fsharp/pull/17877)) \ No newline at end of file From f5e49021f0d472ed5c49b68820a4ba90f173cc7a Mon Sep 17 00:00:00 2001 From: Edgar Gonzalez Date: Sat, 18 Jan 2025 14:49:08 +0000 Subject: [PATCH 8/8] Unify logic and comments --- src/Compiler/Checking/AttributeChecking.fs | 107 +++++++++++++-------- 1 file changed, 65 insertions(+), 42 deletions(-) mode change 100644 => 100755 src/Compiler/Checking/AttributeChecking.fs diff --git a/src/Compiler/Checking/AttributeChecking.fs b/src/Compiler/Checking/AttributeChecking.fs old mode 100644 new mode 100755 index 1e59bca212b..8c317ad5f31 --- a/src/Compiler/Checking/AttributeChecking.fs +++ b/src/Compiler/Checking/AttributeChecking.fs @@ -249,77 +249,100 @@ let private CheckCompilerFeatureRequiredAttribute (g: TcGlobals) cattrs msg m = | _ -> ErrorD (ObsoleteDiagnostic(true, "", msg, "", m)) -let private CheckILObsoleteAttributes (g: TcGlobals) isByrefLikeTyconRef cattrs m = +let private extractILObsoleteAttributeInfo namedArgs = let extractILAttribValueFrom name namedArgs = match namedArgs with | ExtractILAttributeNamedArg name (AttribElemStringArg v) -> v | _ -> "" - let (AttribInfo(tref,_)) = g.attrib_SystemObsolete - match TryDecodeILAttribute tref cattrs with - | Some ([ILAttribElem.String (Some msg) ], namedArgs) when not isByrefLikeTyconRef -> - let diagnosticId = extractILAttribValueFrom "DiagnosticId" namedArgs - let urlFormat = extractILAttribValueFrom "UrlFormat" namedArgs - WarnD(ObsoleteDiagnostic(false, diagnosticId, msg, urlFormat, m)) - | Some ([ILAttribElem.String (Some msg); ILAttribElem.Bool isError ], namedArgs) when not isByrefLikeTyconRef -> - let diagnosticId = extractILAttribValueFrom "DiagnosticId" namedArgs - let urlFormat = extractILAttribValueFrom "UrlFormat" 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)) + let diagnosticId = extractILAttribValueFrom "DiagnosticId" namedArgs + let urlFormat = extractILAttribValueFrom "UrlFormat" namedArgs + (diagnosticId, urlFormat) - | Some ([ILAttribElem.String None ], namedArgs) when not isByrefLikeTyconRef -> - let diagnosticId = extractILAttribValueFrom "DiagnosticId" namedArgs - let urlFormat = extractILAttribValueFrom "UrlFormat" namedArgs - WarnD(ObsoleteDiagnostic(false, diagnosticId, "", urlFormat, m)) - | Some (_, namedArgs) when not isByrefLikeTyconRef -> - let diagnosticId = extractILAttribValueFrom "DiagnosticId" namedArgs - let urlFormat = extractILAttribValueFrom "UrlFormat" namedArgs - WarnD(ObsoleteDiagnostic(false, diagnosticId, "", urlFormat, m)) - | _ -> +let private CheckILObsoleteAttributes (g: TcGlobals) isByrefLikeTyconRef cattrs m = + if isByrefLikeTyconRef then CompleteD + else + let (AttribInfo(tref,_)) = g.attrib_SystemObsolete + match TryDecodeILAttribute tref cattrs with + // [] + // [] + // [] + // [] + // [] + // [] + // [] + // [] + // [] + // Constructors deciding on IsError and Message properties. + | Some ([ attribElement ], namedArgs) -> + let diagnosticId, urlFormat = extractILObsoleteAttributeInfo namedArgs + let msg = + match attribElement with + | ILAttribElem.String (Some msg) -> msg + | ILAttribElem.String None + | _ -> "" + + WarnD (ObsoleteDiagnostic(false, diagnosticId, msg, urlFormat, m)) + | Some ([ILAttribElem.String (Some msg); ILAttribElem.Bool isError ], namedArgs) -> + let diagnosticId, urlFormat = extractILObsoleteAttributeInfo 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 = extractILObsoleteAttributeInfo namedArgs + WarnD(ObsoleteDiagnostic(false, diagnosticId, "", urlFormat, m)) + // No arguments + | None -> CompleteD /// Check IL attributes for 'ObsoleteAttribute', returning errors and warnings as data let private CheckILAttributes (g: TcGlobals) isByrefLikeTyconRef cattrs m = trackErrors { do! CheckILObsoleteAttributes g isByrefLikeTyconRef cattrs m } + let langVersionPrefix = "--langversion:preview" -let private CheckObsoleteAttributes g attribs m = - let extractAttribValueFrom name namedArgs = +let private extractObsoleteAttributeInfo namedArgs = + let extractILAttribValueFrom name namedArgs = match namedArgs with | ExtractAttribNamedArg name (AttribStringArg v) -> v | _ -> "" + let diagnosticId = extractILAttribValueFrom "DiagnosticId" namedArgs + let urlFormat = extractILAttribValueFrom "UrlFormat" namedArgs + (diagnosticId, urlFormat) +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 = extractAttribValueFrom "DiagnosticId" namedArgs - let urlFormat = extractAttribValueFrom "UrlFormat" namedArgs + let diagnosticId, urlFormat = extractObsoleteAttributeInfo namedArgs do! WarnD(ObsoleteDiagnostic(false, diagnosticId, s, urlFormat, m)) - | Some(Attrib(unnamedArgs= [ AttribStringArg s; AttribBoolArg(isError) ]; propVal= namedArgs)) -> - let diagnosticId = extractAttribValueFrom "DiagnosticId" namedArgs - let urlFormat = extractAttribValueFrom "UrlFormat" namedArgs + let diagnosticId, urlFormat = extractObsoleteAttributeInfo namedArgs if isError then do! ErrorD (ObsoleteDiagnostic(true, diagnosticId, s, urlFormat, m)) else do! WarnD (ObsoleteDiagnostic(false, diagnosticId, s, urlFormat, m)) - | Some(Attrib(unnamedArgs= [ AttribStringArg s ]; propVal= namedArgs)) -> - let diagnosticId = extractAttribValueFrom "DiagnosticId" namedArgs - let urlFormat = extractAttribValueFrom "UrlFormat" namedArgs - - do! WarnD(ObsoleteDiagnostic(false, diagnosticId, s, urlFormat, m)) + // Only DiagnosticId, UrlFormat | Some(Attrib(propVal= namedArgs)) -> - let diagnosticId = extractAttribValueFrom "DiagnosticId" namedArgs - let urlFormat = extractAttribValueFrom "UrlFormat" namedArgs + let diagnosticId, urlFormat = extractObsoleteAttributeInfo namedArgs do! WarnD(ObsoleteDiagnostic(false, diagnosticId, "", urlFormat, m)) - | None -> - () + | None -> () } let private CheckCompilerMessageAttribute g attribs m =