Skip to content

Commit bd106f8

Browse files
auduchinokbaronfel
authored andcommitted
Type check: recover in Set expressions when left side value is not mutable (#7682)
* Recover ValNoMutable and PropertyCannotBeSet errors * Recover FieldNotMutable error * Refactor tests * Refactor tests * Update baseline
1 parent 4ee6fec commit bd106f8

File tree

4 files changed

+82
-15
lines changed

4 files changed

+82
-15
lines changed

src/fsharp/MethodCalls.fs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1638,5 +1638,6 @@ let MethInfoChecks g amap isInstance tyargsOpt objArgs ad m (minfo: MethInfo) =
16381638
exception FieldNotMutable of DisplayEnv * Tast.RecdFieldRef * range
16391639

16401640
let CheckRecdFieldMutation m denv (rfinfo: RecdFieldInfo) =
1641-
if not rfinfo.RecdField.IsMutable then error (FieldNotMutable(denv, rfinfo.RecdFieldRef, m))
1641+
if not rfinfo.RecdField.IsMutable then
1642+
errorR (FieldNotMutable (denv, rfinfo.RecdFieldRef, m))
16421643

src/fsharp/TypeChecker.fs

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9362,7 +9362,8 @@ and TcItemThen cenv overallTy env tpenv (item, mItem, rest, afterResolution) del
93629362
if isByrefTy g vty then
93639363
destByrefTy g vty
93649364
else
9365-
if not vref.IsMutable then error (ValNotMutable(env.DisplayEnv, vref, mStmt))
9365+
if not vref.IsMutable then
9366+
errorR (ValNotMutable (env.DisplayEnv, vref, mStmt))
93669367
vty
93679368
// Always allow subsumption on assignment to fields
93689369
let e2', tpenv = TcExprFlex cenv true false vty2 env tpenv e2
@@ -9415,15 +9416,15 @@ and TcItemThen cenv overallTy env tpenv (item, mItem, rest, afterResolution) del
94159416
if meths.IsEmpty then
94169417
let meths = pinfos |> GettersOfPropInfos
94179418
let isByrefMethReturnSetter = meths |> List.exists (function (_,Some pinfo) -> isByrefTy g (pinfo.GetPropertyType(cenv.amap,mItem)) | _ -> false)
9418-
if isByrefMethReturnSetter then
9419-
// x.P <- ... byref setter
9420-
if isNil meths then error (Error (FSComp.SR.tcPropertyIsNotReadable nm, mItem))
9421-
TcMethodApplicationThen cenv env overallTy None tpenv tyargsOpt [] mItem mItem nm ad NeverMutates true meths afterResolution NormalValUse args ExprAtomicFlag.Atomic delayed
9422-
else
9423-
error (Error (FSComp.SR.tcPropertyCannotBeSet1 nm, mItem))
9419+
if not isByrefMethReturnSetter then
9420+
errorR (Error (FSComp.SR.tcPropertyCannotBeSet1 nm, mItem))
9421+
// x.P <- ... byref setter
9422+
if isNil meths then error (Error (FSComp.SR.tcPropertyIsNotReadable nm, mItem))
9423+
TcMethodApplicationThen cenv env overallTy None tpenv tyargsOpt [] mItem mItem nm ad NeverMutates true meths afterResolution NormalValUse args ExprAtomicFlag.Atomic delayed
94249424
else
94259425
let args = if pinfo.IsIndexer then args else []
9426-
if isNil meths then error (Error (FSComp.SR.tcPropertyCannotBeSet1 nm, mItem))
9426+
if isNil meths then
9427+
errorR (Error (FSComp.SR.tcPropertyCannotBeSet1 nm, mItem))
94279428
// Note: static calls never mutate a struct object argument
94289429
TcMethodApplicationThen cenv env overallTy None tpenv tyargsOpt [] mStmt mItem nm ad NeverMutates true meths afterResolution NormalValUse (args@[e2]) ExprAtomicFlag.NonAtomic otherDelayed
94299430
| _ ->
@@ -9600,12 +9601,11 @@ and TcLookupThen cenv overallTy env tpenv mObjExpr objExpr objExprTy longId dela
96009601
if meths.IsEmpty then
96019602
let meths = pinfos |> GettersOfPropInfos
96029603
let isByrefMethReturnSetter = meths |> List.exists (function (_,Some pinfo) -> isByrefTy cenv.g (pinfo.GetPropertyType(cenv.amap,mItem)) | _ -> false)
9603-
if isByrefMethReturnSetter then
9604-
// x.P <- ... byref setter
9605-
if isNil meths then error (Error (FSComp.SR.tcPropertyIsNotReadable nm, mItem))
9606-
TcMethodApplicationThen cenv env overallTy None tpenv tyargsOpt objArgs mExprAndItem mItem nm ad PossiblyMutates true meths afterResolution NormalValUse args atomicFlag delayed
9607-
else
9608-
error (Error (FSComp.SR.tcPropertyCannotBeSet1 nm, mItem))
9604+
if not isByrefMethReturnSetter then
9605+
errorR (Error (FSComp.SR.tcPropertyCannotBeSet1 nm, mItem))
9606+
// x.P <- ... byref setter
9607+
if isNil meths then error (Error (FSComp.SR.tcPropertyIsNotReadable nm, mItem))
9608+
TcMethodApplicationThen cenv env overallTy None tpenv tyargsOpt objArgs mExprAndItem mItem nm ad PossiblyMutates true meths afterResolution NormalValUse args atomicFlag delayed
96099609
else
96109610
let args = if pinfo.IsIndexer then args else []
96119611
let mut = (if isStructTy cenv.g (tyOfExpr cenv.g objExpr) then DefinitelyMutates else PossiblyMutates)

tests/service/Common.fs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ open System.IO
55
open System.Collections.Generic
66
open FSharp.Compiler
77
open FSharp.Compiler.SourceCodeServices
8+
open FsUnit
89

910
#if NETCOREAPP2_0
1011
let readRefs (folder : string) (projectFile: string) =
@@ -304,6 +305,34 @@ let rec allSymbolsInEntities compGen (entities: IList<FSharpEntity>) =
304305
yield! allSymbolsInEntities compGen e.NestedEntities ]
305306

306307

308+
let getSymbolUses (source: string) =
309+
let _, typeCheckResults = parseAndCheckScript("/home/user/Test.fsx", source)
310+
typeCheckResults.GetAllUsesOfAllSymbolsInFile() |> Async.RunSynchronously
311+
312+
let getSymbols (source: string) =
313+
getSymbolUses source
314+
|> Array.map (fun symbolUse -> symbolUse.Symbol)
315+
316+
317+
let getSymbolName (symbol: FSharpSymbol) =
318+
match symbol with
319+
| :? FSharpMemberOrFunctionOrValue as mfv -> Some mfv.LogicalName
320+
| :? FSharpEntity as entity -> Some entity.LogicalName
321+
| :? FSharpGenericParameter as parameter -> Some parameter.Name
322+
| :? FSharpParameter as parameter -> parameter.Name
323+
| :? FSharpStaticParameter as parameter -> Some parameter.Name
324+
| :? FSharpActivePatternCase as case -> Some case.Name
325+
| :? FSharpUnionCase as case -> Some case.Name
326+
| _ -> None
327+
328+
329+
let assertContainsSymbolWithName name source =
330+
getSymbols source
331+
|> Array.choose getSymbolName
332+
|> Array.contains name
333+
|> shouldEqual true
334+
335+
307336
let coreLibAssemblyName =
308337
#if NETCOREAPP2_0
309338
"System.Runtime"

tests/service/EditorTests.fs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1319,3 +1319,40 @@ let ``FSharpField.IsNameGenerated`` () =
13191319
"type U = Case of string * Item2: string * string * Name: string",
13201320
["Item1", true; "Item2", false; "Item3", true; "Name", false]]
13211321
|> List.iter (fun (source, expected) -> checkFields source |> shouldEqual expected)
1322+
1323+
1324+
[<Test>]
1325+
let ``ValNoMutable recovery`` () =
1326+
let source = """
1327+
let x = 1
1328+
x <-
1329+
let y = 1
1330+
y
1331+
"""
1332+
assertContainsSymbolWithName "y" source
1333+
1334+
1335+
[<Test>]
1336+
let ``PropertyCannotBeSet recovery`` () =
1337+
let source = """
1338+
type T =
1339+
static member P = 1
1340+
1341+
T.P <-
1342+
let y = 1
1343+
y
1344+
"""
1345+
assertContainsSymbolWithName "y" source
1346+
1347+
1348+
[<Test>]
1349+
let ``FieldNotMutable recovery`` () =
1350+
let source = """
1351+
type R =
1352+
{ F: int }
1353+
1354+
{ F = 1 }.F <-
1355+
let y = 1
1356+
y
1357+
"""
1358+
assertContainsSymbolWithName "y" source

0 commit comments

Comments
 (0)