Skip to content

Commit

Permalink
Obsolete attribute was ignored in constructor property assignment. (#…
Browse files Browse the repository at this point in the history
…16900)

* Raise an warning when Obsolete attribute is used in constructor property assignment.
  • Loading branch information
edgarfgp authored Apr 2, 2024
1 parent b912eb8 commit 06c85f0
Show file tree
Hide file tree
Showing 3 changed files with 186 additions and 1 deletion.
1 change: 1 addition & 0 deletions docs/release-notes/.FSharp.Compiler.Service/8.0.300.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
* Enforce AttributeTargets on enums ([PR #16887](https://github.com/dotnet/fsharp/pull/16887))
* Completion: fix for unfinished record field decl ([PR #16893](https://github.com/dotnet/fsharp/pull/16893))
* Enforce AttributeTargets on delegates ([PR #16891](https://github.com/dotnet/fsharp/pull/16891))
* Obsolete attribute is ignored in constructor property assignment ([PR #16900](https://github.com/dotnet/fsharp/pull/16900))
* Completion: fix completion in empty dot lambda prefix ([#16829](https://github.com/dotnet/fsharp/pull/16829))
* Fix StackOverflow when checking non-recursive bindings in module or namespace in `fscAnyCpu`/`fsiAnyCpu`. ([PR #16908](https://github.com/dotnet/fsharp/pull/16908))

Expand Down
3 changes: 2 additions & 1 deletion src/Compiler/Checking/CheckExpressions.fs
Original file line number Diff line number Diff line change
Expand Up @@ -10086,7 +10086,6 @@ and TcMethodApplication
// Handle post-hoc property assignments
let setterExprPrebinders, callExpr2b =
let expr = callExpr2

CheckRequiredProperties g env cenv finalCalledMethInfo finalAssignedItemSetters mMethExpr

if isCheckingAttributeCall then
Expand Down Expand Up @@ -10161,6 +10160,8 @@ and TcSetterArgExpr (cenv: cenv) env denv objExpr ad assignedSetter calledFromCo
match setter with
| AssignedPropSetter (propStaticTyOpt, pinfo, pminfo, pminst) ->

CheckPropInfoAttributes pinfo id.idRange |> CommitOperationResult

if g.langVersion.SupportsFeature(LanguageFeature.RequiredPropertiesSupport) && pinfo.IsSetterInitOnly && not calledFromConstructor then
errorR (Error (FSComp.SR.tcInitOnlyPropertyCannotBeSet1 pinfo.PropertyName, m))

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
namespace Language

open FSharp.Test
open Xunit
open FSharp.Test.Compiler

Expand Down Expand Up @@ -1262,3 +1263,185 @@ let f (x: IFirst) = x.F()
(Error 101, Line 13, Col 11, Line 13, Col 17, "This construct is deprecated. Use G instead")
(Error 72, Line 13, Col 21, Line 13, Col 24, "Lookup on object of indeterminate type based on information prior to this program point. A type annotation may be needed prior to this program point to constrain the type of the object. This may allow the lookup to be resolved.")
]

[<Fact>]
let ``Obsolete attribute warning is taken into account in a constructor property assignment`` () =
Fsx """
open System
type JsonSerializerOptions() =
[<Obsolete("This is bad")>]
member val DefaultOptions = false with get, set
member val UseCustomOptions = false with get, set
let options = JsonSerializerOptions(DefaultOptions = true, UseCustomOptions = false)
let options2 = JsonSerializerOptions(DefaultOptions = true, DefaultOptions = false)
"""
|> typecheck
|> shouldFail
|> withDiagnostics [
(Warning 44, Line 9, Col 37, Line 9, Col 51, "This construct is deprecated. This is bad")
(Error 364, Line 10, Col 16, Line 10, Col 84, "The named argument 'DefaultOptions' has been assigned more than one value")
(Warning 44, Line 10, Col 38, Line 10, Col 52, "This construct is deprecated. This is bad")
(Warning 44, Line 10, Col 61, Line 10, Col 75, "This construct is deprecated. This is bad")
]

[<Fact>]
let ``Obsolete attribute warning is not taken into account in prop setters that can be included in methods which are not constructors`` () =
Fsx """
open System
type JsonSerializerOptions() =
[<Obsolete("This is bad")>]
member val DefaultOptions = false with get, set
member val UseCustomOptions = false with get, set
member this.With() = this
let options = JsonSerializerOptions()
let options2 =
options
.With(DefaultOptions = true)
.With(UseCustomOptions = false)
"""
|> typecheck
|> withDiagnostics [
(Warning 44, Line 13, Col 15, Line 13, Col 29, "This construct is deprecated. This is bad")
]

[<Fact>]
let ``Obsolete attribute error is not taken into account in prop setters that can be included in methods which are not constructors`` () =
Fsx """
open System
type JsonSerializerOptions() =
[<Obsolete("This is bad", true)>]
member val DefaultOptions = false with get, set
member val UseCustomOptions = false with get, set
member this.With() = this
let options = JsonSerializerOptions()
let options2 =
options
.With(DefaultOptions = true)
.With(UseCustomOptions = false)
"""
|> typecheck
|> shouldFail
|> withDiagnostics [
(Error 101, Line 13, Col 15, Line 13, Col 29, "This construct is deprecated. This is bad")
]

[<Fact>]
let ``Obsolete attribute error is taken into account in a constructor property assignment`` () =
Fsx """
open System
type JsonSerializerOptions() =
[<Obsolete("This is bad", true)>]
member val DefaultOptions = false with get, set
member val UseCustomOptions = false with get, set
let options = JsonSerializerOptions(DefaultOptions = true, UseCustomOptions = false)
let options2 = JsonSerializerOptions(DefaultOptions = true, DefaultOptions = false)
"""
|> typecheck
|> shouldFail
|> withDiagnostics [
(Error 101, Line 9, Col 37, Line 9, Col 51, "This construct is deprecated. This is bad");
(Error 364, Line 10, Col 16, Line 10, Col 84, "The named argument 'DefaultOptions' has been assigned more than one value");
(Error 101, Line 10, Col 38, Line 10, Col 52, "This construct is deprecated. This is bad")
]

[<Fact>]
let ``Obsolete attribute warning is taken into account in a nested constructor property assignment`` () =
Fsx """
open System
type JsonSerializer1Options() =
[<Obsolete("This is bad")>]
member val DefaultOptions = false with get, set
member val UseCustomOptions = false with get, set
type JsonSerializerOptions() =
member val DefaultOptions = JsonSerializer1Options() with get, set
member val UseCustomOptions = false with get, set
let options = JsonSerializerOptions(DefaultOptions = JsonSerializer1Options(DefaultOptions = true), UseCustomOptions = false)
"""
|> typecheck
|> shouldFail
|> withDiagnostics [
(Warning 44, Line 14, Col 77, Line 14, Col 91, "This construct is deprecated. This is bad")
]

[<Fact>]
let ``Obsolete attribute error is taken into account in a nested constructor property assignment`` () =
Fsx """
open System
type JsonSerializer1Options() =
[<Obsolete("This is bad", true)>]
member val DefaultOptions = false with get, set
member val UseCustomOptions = false with get, set
type JsonSerializerOptions() =
member val DefaultOptions = JsonSerializer1Options() with get, set
member val UseCustomOptions = false with get, set
let options = JsonSerializerOptions(DefaultOptions = JsonSerializer1Options(DefaultOptions = true), UseCustomOptions = false)
"""
|> typecheck
|> shouldFail
|> withDiagnostics [
(Error 101, Line 14, Col 77, Line 14, Col 91, "This construct is deprecated. This is bad")
]

[<Fact>]
let ``Obsolete attribute warning is taken into account in a constructor property assignment from a csharp class`` () =
let CSLib =
CSharp """
using System;
public class JsonProtocolTestData {
[Obsolete("Use Json instead")]
public bool IgnoreNullValues { get; set; }
}
""" |> withName "CSLib"

let app =
FSharp """
module ObsoleteStruct.FS
let res = JsonProtocolTestData(IgnoreNullValues = false)
""" |> withReferences [CSLib]

app
|> compile
|> shouldFail
|> withDiagnostics [
(Warning 44, Line 3, Col 32, Line 3, Col 48, "This construct is deprecated. Use Json instead")
]

[<Fact>]
let ``Obsolete attribute error is taken into account in a constructor property assignment from a csharp class`` () =
let CSLib =
CSharp """
using System;
public class JsonProtocolTestData {
[Obsolete("Use Json instead", true)]
public bool IgnoreNullValues { get; set; }
}
""" |> withName "CSLib"

let app =
FSharp """
module ObsoleteStruct.FS
let res = JsonProtocolTestData(IgnoreNullValues = false)
""" |> withReferences [CSLib]

app
|> compile
|> shouldFail
|> withDiagnostics [
(Error 101, Line 3, Col 32, Line 3, Col 48, "This construct is deprecated. Use Json instead")
]

0 comments on commit 06c85f0

Please sign in to comment.