diff --git a/src/Compiler/Checking/CheckExpressions.fs b/src/Compiler/Checking/CheckExpressions.fs index 3ef149a7151..b7b89d4e2a8 100644 --- a/src/Compiler/Checking/CheckExpressions.fs +++ b/src/Compiler/Checking/CheckExpressions.fs @@ -5163,10 +5163,14 @@ and ConvSynPatToSynExpr synPat = /// Check a long identifier 'Case' or 'Case argsR' that has been resolved to an active pattern case and TcPatLongIdentActivePatternCase warnOnUpper (cenv: cenv) (env: TcEnv) vFlags patEnv ty (mLongId, item, apref, args, m) = let g = cenv.g - let (TcPatLinearEnv(tpenv, names, takenNames)) = patEnv let (APElemRef (apinfo, vref, idx, isStructRetTy)) = apref + let cenv = + match g.checkNullness,TryFindLocalizedFSharpStringAttribute g g.attrib_WarnOnWithoutNullArgumentAttribute vref.Attribs with + | true, (Some _ as warnMsg) -> {cenv with css.WarnWhenUsingWithoutNullOnAWithNullTarget = warnMsg} + | _ -> cenv + // Report information about the 'active recognizer' occurrence to IDE CallNameResolutionSink cenv.tcSink (mLongId, env.NameEnv, item, emptyTyparInst, ItemOccurence.Pattern, env.eAccessRights) @@ -8428,11 +8432,11 @@ and TcApplicationThen (cenv: cenv) (overallTy: OverallTy) env tpenv mExprAndArg SynExpr.ComputationExpr (true, comp, m) | _ -> synArg - let arg, tpenv = + let (arg, tpenv), cenv = // treat left and right of '||' and '&&' as control flow, so for example // f expr1 && g expr2 // will have debug points on "f expr1" and "g expr2" - let env = + let env,cenv = match leftExpr with | ApplicableExpr(expr=Expr.Val (vref, _, _)) | ApplicableExpr(expr=Expr.App (Expr.Val (vref, _, _), _, _, [_], _)) @@ -8440,10 +8444,15 @@ and TcApplicationThen (cenv: cenv) (overallTy: OverallTy) env tpenv mExprAndArg || valRefEq g vref g.and2_vref || valRefEq g vref g.or_vref || valRefEq g vref g.or2_vref -> - { env with eIsControlFlow = true } - | _ -> env - - TcExprFlex2 cenv domainTy env false tpenv synArg + { env with eIsControlFlow = true },cenv + | ApplicableExpr(expr=Expr.Val (valRef=vref)) + | ApplicableExpr(expr=Expr.App (funcExpr=Expr.Val (valRef=vref))) -> + match TryFindLocalizedFSharpStringAttribute g g.attrib_WarnOnWithoutNullArgumentAttribute vref.Attribs with + | Some _ as msg -> env,{ cenv with css.WarnWhenUsingWithoutNullOnAWithNullTarget = msg} + | None -> env,cenv + | _ -> env,cenv + + TcExprFlex2 cenv domainTy env false tpenv synArg, cenv let exprAndArg, resultTy = buildApp cenv leftExpr resultTy arg mExprAndArg TcDelayed cenv overallTy env tpenv mExprAndArg exprAndArg resultTy atomicFlag delayed diff --git a/src/Compiler/Checking/ConstraintSolver.fs b/src/Compiler/Checking/ConstraintSolver.fs index cfae752df80..e8c63814bbd 100644 --- a/src/Compiler/Checking/ConstraintSolver.fs +++ b/src/Compiler/Checking/ConstraintSolver.fs @@ -268,6 +268,7 @@ type ConstraintSolverState = /// Checks to run after all inference is complete. PostInferenceChecksFinal: ResizeArray unit> + WarnWhenUsingWithoutNullOnAWithNullTarget: string option } static member New(g, amap, infoReader, tcVal) = @@ -277,7 +278,8 @@ type ConstraintSolverState = InfoReader = infoReader TcVal = tcVal PostInferenceChecksPreDefaults = ResizeArray() - PostInferenceChecksFinal = ResizeArray() } + PostInferenceChecksFinal = ResizeArray() + WarnWhenUsingWithoutNullOnAWithNullTarget = None } member this.PushPostInferenceCheck (preDefaults, check) = if preDefaults then @@ -1041,6 +1043,9 @@ and SolveNullnessEquiv (csenv: ConstraintSolverEnv) m2 (trace: OptionalTrace) ty | _, NullnessInfo.AmbivalentToNull -> CompleteD | NullnessInfo.WithNull, NullnessInfo.WithNull -> CompleteD | NullnessInfo.WithoutNull, NullnessInfo.WithoutNull -> CompleteD + // Warn for 'strict "must pass null"` APIs like Option.ofObj + | NullnessInfo.WithNull, NullnessInfo.WithoutNull when csenv.g.checkNullness && csenv.SolverState.WarnWhenUsingWithoutNullOnAWithNullTarget.IsSome -> + WarnD(Error(FSComp.SR.tcPassingWithoutNullToANullableExpectingFunc (csenv.SolverState.WarnWhenUsingWithoutNullOnAWithNullTarget.Value),m2)) // Allow expected of WithNull and actual of WithoutNull // TODO NULLNESS: this is not sound in contravariant cases etc. It is assuming covariance. | NullnessInfo.WithNull, NullnessInfo.WithoutNull -> CompleteD @@ -1076,8 +1081,12 @@ and SolveNullnessSubsumesNullness (csenv: ConstraintSolverEnv) m2 (trace: Option | _, NullnessInfo.AmbivalentToNull -> CompleteD | NullnessInfo.WithNull, NullnessInfo.WithNull -> CompleteD | NullnessInfo.WithoutNull, NullnessInfo.WithoutNull -> CompleteD + // Warn for 'strict "must pass null"` APIs like Option.ofObj + | NullnessInfo.WithNull, NullnessInfo.WithoutNull when csenv.g.checkNullness && csenv.SolverState.WarnWhenUsingWithoutNullOnAWithNullTarget.IsSome -> + WarnD(Error(FSComp.SR.tcPassingWithoutNullToANullableExpectingFunc (csenv.SolverState.WarnWhenUsingWithoutNullOnAWithNullTarget.Value),m2)) // Allow target of WithNull and actual of WithoutNull - | NullnessInfo.WithNull, NullnessInfo.WithoutNull -> CompleteD + | NullnessInfo.WithNull, NullnessInfo.WithoutNull -> + CompleteD | NullnessInfo.WithoutNull, NullnessInfo.WithNull -> if csenv.g.checkNullness then if not (isObjTy csenv.g ty1) || not (isObjTy csenv.g ty2) then @@ -3968,7 +3977,8 @@ let CreateCodegenState tcVal g amap = ExtraCxs = HashMultiMap(10, HashIdentity.Structural) InfoReader = InfoReader(g, amap) PostInferenceChecksPreDefaults = ResizeArray() - PostInferenceChecksFinal = ResizeArray() } + PostInferenceChecksFinal = ResizeArray() + WarnWhenUsingWithoutNullOnAWithNullTarget = None} /// Determine if a codegen witness for a trait will require witness args to be available, e.g. in generic code let CodegenWitnessExprForTraitConstraintWillRequireWitnessArgs tcVal g amap m (traitInfo:TraitConstraintInfo) = @@ -4063,7 +4073,8 @@ let IsApplicableMethApprox g amap m (minfo: MethInfo) availObjTy = ExtraCxs = HashMultiMap(10, HashIdentity.Structural) InfoReader = InfoReader(g, amap) PostInferenceChecksPreDefaults = ResizeArray() - PostInferenceChecksFinal = ResizeArray() } + PostInferenceChecksFinal = ResizeArray() + WarnWhenUsingWithoutNullOnAWithNullTarget = None} let csenv = MakeConstraintSolverEnv ContextInfo.NoContext css m (DisplayEnv.Empty g) let minst = FreshenMethInfo m minfo match minfo.GetObjArgTypes(amap, m, minst) with diff --git a/src/Compiler/Checking/ConstraintSolver.fsi b/src/Compiler/Checking/ConstraintSolver.fsi index 371fa31e598..13b484356e9 100644 --- a/src/Compiler/Checking/ConstraintSolver.fsi +++ b/src/Compiler/Checking/ConstraintSolver.fsi @@ -188,8 +188,32 @@ exception ArgDoesNotMatchError of /// A function that denotes captured tcVal, Used in constraint solver and elsewhere to get appropriate expressions for a ValRef. type TcValF = ValRef -> ValUseFlag -> TType list -> range -> Expr * TType -[] type ConstraintSolverState = + { + g: TcGlobals + + amap: ImportMap + + InfoReader: InfoReader + + /// The function used to freshen values we encounter during trait constraint solving + TcVal: TcValF + + /// This table stores all unsolved, ungeneralized trait constraints, indexed by free type variable. + /// That is, there will be one entry in this table for each free type variable in + /// each outstanding, unsolved, ungeneralized trait constraint. Constraints are removed from the table and resolved + /// each time a solution to an index variable is found. + mutable ExtraCxs: Internal.Utilities.Collections.HashMultiMap + + /// Checks to run after all inference is complete, but before defaults are applied and internal unknowns solved + PostInferenceChecksPreDefaults: ResizeArray unit> + + /// Checks to run after all inference is complete. + PostInferenceChecksFinal: ResizeArray unit> + + WarnWhenUsingWithoutNullOnAWithNullTarget: string option + } + static member New: TcGlobals * ImportMap * InfoReader * TcValF -> ConstraintSolverState /// Add a post-inference check to run at the end of inference diff --git a/src/Compiler/FSComp.txt b/src/Compiler/FSComp.txt index 59cbd32aba5..e70c4a3e2a6 100644 --- a/src/Compiler/FSComp.txt +++ b/src/Compiler/FSComp.txt @@ -1526,6 +1526,12 @@ notAFunctionButMaybeDeclaration,"This value is not a function and cannot be appl #3261 reserved for ConstraintSolverNullnessWarningWithTypes #3261 reserved for ConstraintSolverNullnessWarningWithType #3261 reserved for ConstraintSolverNullnessWarning +3262,tcPassingWithoutNullToANullableExpectingFunc,"Value known to be without null passed to a function meant for nullables: %s" +tcPassingWithoutNullToOptionOfObj,"You can create 'Some value' directly instead of 'ofObj', or consider not using an option for this value." +tcPassingWithoutNullToValueOptionOfObj,"You can create 'ValueSome value' directly instead of 'ofObj', or consider not using a voption for this value." +tcPassingWithoutNullToNonNullAP,"You can remove this |Null|NonNull| pattern usage." +tcPassingWithoutNullToNonNullQuickAP,"You can remove this |NonNullQuick| pattern usage." +tcPassingWithoutNullTononNullFunction,"You can remove this `nonNull` assertion." 3268,csNullNotNullConstraintInconsistent,"The constraints 'null' and 'not null' are inconsistent" 3271,tcNullnessCheckingNotEnabled,"The 'nullness checking' language feature is not enabled. This use of a nullness checking construct will be ignored." csTypeHasNullAsTrueValue,"The type '%s' uses 'null' as a representation value but a non-null type is expected" diff --git a/src/Compiler/TypedTree/TcGlobals.fs b/src/Compiler/TypedTree/TcGlobals.fs index 895a821fbb6..12b1f5e361d 100644 --- a/src/Compiler/TypedTree/TcGlobals.fs +++ b/src/Compiler/TypedTree/TcGlobals.fs @@ -1573,6 +1573,7 @@ type TcGlobals( member val attrib_MeasureableAttribute = mk_MFCore_attrib "MeasureAnnotatedAbbreviationAttribute" member val attrib_NoDynamicInvocationAttribute = mk_MFCore_attrib "NoDynamicInvocationAttribute" member val attrib_NoCompilerInliningAttribute = mk_MFCore_attrib "NoCompilerInliningAttribute" + member val attrib_WarnOnWithoutNullArgumentAttribute = mk_MFCore_attrib "WarnOnWithoutNullArgumentAttribute" member val attrib_SecurityAttribute = tryFindSysAttrib "System.Security.Permissions.SecurityAttribute" member val attrib_SecurityCriticalAttribute = findSysAttrib "System.Security.SecurityCriticalAttribute" member val attrib_SecuritySafeCriticalAttribute = findSysAttrib "System.Security.SecuritySafeCriticalAttribute" diff --git a/src/Compiler/TypedTree/TypedTreeBasics.fs b/src/Compiler/TypedTree/TypedTreeBasics.fs index a4d670ccb0b..f57c443f4d5 100644 --- a/src/Compiler/TypedTree/TypedTreeBasics.fs +++ b/src/Compiler/TypedTree/TypedTreeBasics.fs @@ -315,7 +315,7 @@ let rec stripTyparEqnsAux nullness0 canShortcut ty = addNullnessToTy nullness0 ty | TType_measure unt -> TType_measure (stripUnitEqnsAux canShortcut unt) - | _ -> ty + | _ -> addNullnessToTy nullness0 ty let stripTyparEqns ty = stripTyparEqnsAux KnownWithoutNull false ty diff --git a/src/Compiler/TypedTree/TypedTreeOps.fs b/src/Compiler/TypedTree/TypedTreeOps.fs index f57e00d0158..1c2ad533bb5 100644 --- a/src/Compiler/TypedTree/TypedTreeOps.fs +++ b/src/Compiler/TypedTree/TypedTreeOps.fs @@ -3496,6 +3496,19 @@ let TryFindFSharpStringAttribute g nm attrs = match TryFindFSharpAttribute g nm attrs with | Some(Attrib(_, _, [ AttribStringArg b ], _, _, _, _)) -> Some b | _ -> None + +let TryFindLocalizedFSharpStringAttribute g nm attrs = + match TryFindFSharpAttribute g nm attrs with + | Some(Attrib(_, _, [ AttribStringArg b ], namedArgs, _, _, _)) -> + match namedArgs with + | ExtractAttribNamedArg "Localize" (AttribBoolArg true) -> + #if PROTO + Some b + #else + FSComp.SR.GetTextOpt(b) + #endif + | _ -> Some b + | _ -> None let TryFindILAttribute (AttribInfo (atref, _)) attrs = HasILAttribute atref attrs diff --git a/src/Compiler/TypedTree/TypedTreeOps.fsi b/src/Compiler/TypedTree/TypedTreeOps.fsi index 4b617dc1965..66fae0692fd 100755 --- a/src/Compiler/TypedTree/TypedTreeOps.fsi +++ b/src/Compiler/TypedTree/TypedTreeOps.fsi @@ -2286,6 +2286,8 @@ val TryFindFSharpBoolAttributeAssumeFalse: TcGlobals -> BuiltinAttribInfo -> Att val TryFindFSharpStringAttribute: TcGlobals -> BuiltinAttribInfo -> Attribs -> string option +val TryFindLocalizedFSharpStringAttribute: TcGlobals -> BuiltinAttribInfo -> Attribs -> string option + val TryFindFSharpInt32Attribute: TcGlobals -> BuiltinAttribInfo -> Attribs -> int32 option /// Try to find a specific attribute on a type definition, where the attribute accepts a string argument. diff --git a/src/Compiler/Utilities/Activity.fs b/src/Compiler/Utilities/Activity.fs index ed2ac738ed2..3e004d3a6d2 100644 --- a/src/Compiler/Utilities/Activity.fs +++ b/src/Compiler/Utilities/Activity.fs @@ -60,16 +60,16 @@ module internal Activity = member this.RootId = let rec rootID (act: Activity) = match act.Parent with - | NonNull parent -> rootID parent - | Null -> act.Id + | null -> act.Id + | parent -> rootID parent rootID this member this.Depth = let rec depth (act: Activity) acc = match act.Parent with - | NonNull parent -> depth parent (acc + 1) - | Null -> acc + | null -> acc + | parent -> depth parent (acc + 1) depth this 0 @@ -79,8 +79,8 @@ module internal Activity = let activity = activitySource.CreateActivity(name, ActivityKind.Internal) match activity with - | Null -> activity - | NonNull activity -> + | null -> activity + | activity -> for key, value in tags do activity.AddTag(key, value) |> ignore @@ -90,7 +90,8 @@ module internal Activity = let addEvent name = match Activity.Current with - | NonNull activity when activity.Source = activitySource -> activity.AddEvent(ActivityEvent name) |> ignore + | null -> () + | activity when activity.Source = activitySource -> activity.AddEvent(ActivityEvent name) |> ignore | _ -> () module Profiling = diff --git a/src/Compiler/xlf/FSComp.txt.cs.xlf b/src/Compiler/xlf/FSComp.txt.cs.xlf index 5ba918d6c5a..638ea5564df 100644 --- a/src/Compiler/xlf/FSComp.txt.cs.xlf +++ b/src/Compiler/xlf/FSComp.txt.cs.xlf @@ -1422,6 +1422,36 @@ Syntaxe (expr1)[expr2] je teď pro indexování vyhrazená a je při použití jako argument nejednoznačná. Více informací: https://aka.ms/fsharp-index-notation. Pokud voláte funkci s vícenásobnými curryfikovanými argumenty, přidejte mezi ně mezeru, třeba someFunction (expr1) [expr2]. + + Value known to be without null passed to a function meant for nullables: {0} + Value known to be without null passed to a function meant for nullables: {0} + + + + You can remove this |Null|NonNull| pattern usage. + You can remove this |Null|NonNull| pattern usage. + + + + You can remove this |NonNullQuick| pattern usage. + You can remove this |NonNullQuick| pattern usage. + + + + You can create 'Some value' directly instead of 'ofObj', or consider not using an option for this value. + You can create 'Some value' directly instead of 'ofObj', or consider not using an option for this value. + + + + You can create 'ValueSome value' directly instead of 'ofObj', or consider not using a voption for this value. + You can create 'ValueSome value' directly instead of 'ofObj', or consider not using a voption for this value. + + + + You can remove this `nonNull` assertion. + You can remove this `nonNull` assertion. + + The 'let! ... and! ...' construct may only be used if the computation expression builder defines either a '{0}' method or appropriate 'MergeSources' and 'Bind' methods Konstrukt „let! ... and! ...“ se dá použít jen v případě, že tvůrce výpočetních výrazů definuje buď metodu „{0}“, nebo vhodné metody „MergeSource“ a „Bind“. diff --git a/src/Compiler/xlf/FSComp.txt.de.xlf b/src/Compiler/xlf/FSComp.txt.de.xlf index b3a71bfca4f..0a3000388aa 100644 --- a/src/Compiler/xlf/FSComp.txt.de.xlf +++ b/src/Compiler/xlf/FSComp.txt.de.xlf @@ -1422,6 +1422,36 @@ Die Syntax "(expr1)[expr2]" ist jetzt für die Indizierung reserviert und mehrdeutig, wenn sie als Argument verwendet wird. Siehe https://aka.ms/fsharp-index-notation. Wenn Sie eine Funktion mit mehreren geschweiften Argumenten aufrufen, fügen Sie ein Leerzeichen dazwischen hinzu, z. B. "someFunction (expr1) [expr2]". + + Value known to be without null passed to a function meant for nullables: {0} + Value known to be without null passed to a function meant for nullables: {0} + + + + You can remove this |Null|NonNull| pattern usage. + You can remove this |Null|NonNull| pattern usage. + + + + You can remove this |NonNullQuick| pattern usage. + You can remove this |NonNullQuick| pattern usage. + + + + You can create 'Some value' directly instead of 'ofObj', or consider not using an option for this value. + You can create 'Some value' directly instead of 'ofObj', or consider not using an option for this value. + + + + You can create 'ValueSome value' directly instead of 'ofObj', or consider not using a voption for this value. + You can create 'ValueSome value' directly instead of 'ofObj', or consider not using a voption for this value. + + + + You can remove this `nonNull` assertion. + You can remove this `nonNull` assertion. + + The 'let! ... and! ...' construct may only be used if the computation expression builder defines either a '{0}' method or appropriate 'MergeSources' and 'Bind' methods Das Konstrukt "let! ... and! ..." kann nur verwendet werden, wenn der Berechnungsausdrucks-Generator entweder eine {0}-Methode oder geeignete MergeSources- und Bind-Methoden definiert. diff --git a/src/Compiler/xlf/FSComp.txt.es.xlf b/src/Compiler/xlf/FSComp.txt.es.xlf index fd7d985bd82..faad1a8a55e 100644 --- a/src/Compiler/xlf/FSComp.txt.es.xlf +++ b/src/Compiler/xlf/FSComp.txt.es.xlf @@ -1422,6 +1422,36 @@ La sintaxis "(expr1)[expr2]" está reservada ahora para la indexación y es ambigua cuando se usa como argumento. Vea https://aka.ms/fsharp-index-notation. Si se llama a una función con varios argumentos currificados, agregue un espacio entre ellos, por ejemplo, "unaFunción (expr1) [expr2]". + + Value known to be without null passed to a function meant for nullables: {0} + Value known to be without null passed to a function meant for nullables: {0} + + + + You can remove this |Null|NonNull| pattern usage. + You can remove this |Null|NonNull| pattern usage. + + + + You can remove this |NonNullQuick| pattern usage. + You can remove this |NonNullQuick| pattern usage. + + + + You can create 'Some value' directly instead of 'ofObj', or consider not using an option for this value. + You can create 'Some value' directly instead of 'ofObj', or consider not using an option for this value. + + + + You can create 'ValueSome value' directly instead of 'ofObj', or consider not using a voption for this value. + You can create 'ValueSome value' directly instead of 'ofObj', or consider not using a voption for this value. + + + + You can remove this `nonNull` assertion. + You can remove this `nonNull` assertion. + + The 'let! ... and! ...' construct may only be used if the computation expression builder defines either a '{0}' method or appropriate 'MergeSources' and 'Bind' methods La construcción "let! ... and! ..." solo se puede usar si el generador de expresiones de cálculo define un método "{0}" o bien los métodos "MergeSources" y "Bind" adecuados. diff --git a/src/Compiler/xlf/FSComp.txt.fr.xlf b/src/Compiler/xlf/FSComp.txt.fr.xlf index eb85aafcd23..7339b4ab8e3 100644 --- a/src/Compiler/xlf/FSComp.txt.fr.xlf +++ b/src/Compiler/xlf/FSComp.txt.fr.xlf @@ -1422,6 +1422,36 @@ La syntaxe « (expr1)[expr2] » est désormais réservée à l’indexation et est ambiguë lorsqu’elle est utilisée comme argument. Voir https://aka.ms/fsharp-index-notation. Si vous appelez une fonction avec plusieurs arguments codés, ajoutez un espace entre eux, par exemple « someFunction (expr1) [expr2] ». + + Value known to be without null passed to a function meant for nullables: {0} + Value known to be without null passed to a function meant for nullables: {0} + + + + You can remove this |Null|NonNull| pattern usage. + You can remove this |Null|NonNull| pattern usage. + + + + You can remove this |NonNullQuick| pattern usage. + You can remove this |NonNullQuick| pattern usage. + + + + You can create 'Some value' directly instead of 'ofObj', or consider not using an option for this value. + You can create 'Some value' directly instead of 'ofObj', or consider not using an option for this value. + + + + You can create 'ValueSome value' directly instead of 'ofObj', or consider not using a voption for this value. + You can create 'ValueSome value' directly instead of 'ofObj', or consider not using a voption for this value. + + + + You can remove this `nonNull` assertion. + You can remove this `nonNull` assertion. + + The 'let! ... and! ...' construct may only be used if the computation expression builder defines either a '{0}' method or appropriate 'MergeSources' and 'Bind' methods Le « laissez ! » ... et! ...' ne peut être utilisée que si le générateur d'expression de calcul définit soit une méthode '{0}', soit des méthodes 'MergeSources' et 'Bind' appropriées. diff --git a/src/Compiler/xlf/FSComp.txt.it.xlf b/src/Compiler/xlf/FSComp.txt.it.xlf index e0fd005fe89..de520f346bf 100644 --- a/src/Compiler/xlf/FSComp.txt.it.xlf +++ b/src/Compiler/xlf/FSComp.txt.it.xlf @@ -1422,6 +1422,36 @@ La sintassi '(expr1)[expr2]' è ora riservata per l'indicizzazione ed è ambigua quando usata come argomento. Vedere https://aka.ms/fsharp-index-notation. Se si chiama una funzione con più argomenti sottoposti a corsi, aggiungere uno spazio tra di essi, ad esempio 'someFunction (expr1) [expr2]'. + + Value known to be without null passed to a function meant for nullables: {0} + Value known to be without null passed to a function meant for nullables: {0} + + + + You can remove this |Null|NonNull| pattern usage. + You can remove this |Null|NonNull| pattern usage. + + + + You can remove this |NonNullQuick| pattern usage. + You can remove this |NonNullQuick| pattern usage. + + + + You can create 'Some value' directly instead of 'ofObj', or consider not using an option for this value. + You can create 'Some value' directly instead of 'ofObj', or consider not using an option for this value. + + + + You can create 'ValueSome value' directly instead of 'ofObj', or consider not using a voption for this value. + You can create 'ValueSome value' directly instead of 'ofObj', or consider not using a voption for this value. + + + + You can remove this `nonNull` assertion. + You can remove this `nonNull` assertion. + + The 'let! ... and! ...' construct may only be used if the computation expression builder defines either a '{0}' method or appropriate 'MergeSources' and 'Bind' methods È possibile usare il costrutto "let! ... and! ..." solo se il generatore di espressioni di calcolo definisce un metodo "{0}" o metodi "MergeSource" e "Bind" appropriati diff --git a/src/Compiler/xlf/FSComp.txt.ja.xlf b/src/Compiler/xlf/FSComp.txt.ja.xlf index 69d0d2841b4..afd62484b26 100644 --- a/src/Compiler/xlf/FSComp.txt.ja.xlf +++ b/src/Compiler/xlf/FSComp.txt.ja.xlf @@ -1422,6 +1422,36 @@ 構文 '(expr1)[expr2]' はインデックス作成に予約されているので、引数として使うとあいまいです。https://aka.ms/fsharp-index-notation を参照してください。複数のカリー化された引数を持つ関数を呼び出す場合には、'someFunction (expr1) [expr2]' のように間にスペースを追加します。 + + Value known to be without null passed to a function meant for nullables: {0} + Value known to be without null passed to a function meant for nullables: {0} + + + + You can remove this |Null|NonNull| pattern usage. + You can remove this |Null|NonNull| pattern usage. + + + + You can remove this |NonNullQuick| pattern usage. + You can remove this |NonNullQuick| pattern usage. + + + + You can create 'Some value' directly instead of 'ofObj', or consider not using an option for this value. + You can create 'Some value' directly instead of 'ofObj', or consider not using an option for this value. + + + + You can create 'ValueSome value' directly instead of 'ofObj', or consider not using a voption for this value. + You can create 'ValueSome value' directly instead of 'ofObj', or consider not using a voption for this value. + + + + You can remove this `nonNull` assertion. + You can remove this `nonNull` assertion. + + The 'let! ... and! ...' construct may only be used if the computation expression builder defines either a '{0}' method or appropriate 'MergeSources' and 'Bind' methods 'let! ... and! ...' コンストラクトは、コンピュテーション式ビルダーが '{0}' メソッドまたは適切な 'MergeSource' および 'Bind' メソッドのいずれかを定義している場合にのみ使用できます diff --git a/src/Compiler/xlf/FSComp.txt.ko.xlf b/src/Compiler/xlf/FSComp.txt.ko.xlf index a049149d3d3..5d36e61d8b0 100644 --- a/src/Compiler/xlf/FSComp.txt.ko.xlf +++ b/src/Compiler/xlf/FSComp.txt.ko.xlf @@ -1422,6 +1422,36 @@ 구문 '(expr1)[expr2]'는 이제 인덱싱을 위해 예약되었으며 인수로 사용될 때 모호합니다. https://aka.ms/fsharp-index-notation을 참조하세요. 여러 개의 커리된 인수로 함수를 호출하는 경우 그 사이에 공백을 추가하세요(예: 'someFunction (expr1) [expr2]'). + + Value known to be without null passed to a function meant for nullables: {0} + Value known to be without null passed to a function meant for nullables: {0} + + + + You can remove this |Null|NonNull| pattern usage. + You can remove this |Null|NonNull| pattern usage. + + + + You can remove this |NonNullQuick| pattern usage. + You can remove this |NonNullQuick| pattern usage. + + + + You can create 'Some value' directly instead of 'ofObj', or consider not using an option for this value. + You can create 'Some value' directly instead of 'ofObj', or consider not using an option for this value. + + + + You can create 'ValueSome value' directly instead of 'ofObj', or consider not using a voption for this value. + You can create 'ValueSome value' directly instead of 'ofObj', or consider not using a voption for this value. + + + + You can remove this `nonNull` assertion. + You can remove this `nonNull` assertion. + + The 'let! ... and! ...' construct may only be used if the computation expression builder defines either a '{0}' method or appropriate 'MergeSources' and 'Bind' methods 'let! ... and! ...' 구문은 계산 식 작성기에서 '{0}' 메서드 또는 적절한 'MergeSources' 및 'Bind' 메서드를 정의한 경우에만 사용할 수 있습니다. diff --git a/src/Compiler/xlf/FSComp.txt.pl.xlf b/src/Compiler/xlf/FSComp.txt.pl.xlf index 0ea54240967..dc6e106076e 100644 --- a/src/Compiler/xlf/FSComp.txt.pl.xlf +++ b/src/Compiler/xlf/FSComp.txt.pl.xlf @@ -1422,6 +1422,36 @@ Składnia wyrażenia „(expr1)[expr2]” jest teraz zarezerwowana do indeksowania i jest niejednoznaczna, gdy jest używana jako argument. Zobacz: https://aka.ms/fsharp-index-notation. Jeśli wywołujesz funkcję z wieloma argumentami typu curried, dodaj spację między nimi, np. „someFunction (expr1) [expr2]”. + + Value known to be without null passed to a function meant for nullables: {0} + Value known to be without null passed to a function meant for nullables: {0} + + + + You can remove this |Null|NonNull| pattern usage. + You can remove this |Null|NonNull| pattern usage. + + + + You can remove this |NonNullQuick| pattern usage. + You can remove this |NonNullQuick| pattern usage. + + + + You can create 'Some value' directly instead of 'ofObj', or consider not using an option for this value. + You can create 'Some value' directly instead of 'ofObj', or consider not using an option for this value. + + + + You can create 'ValueSome value' directly instead of 'ofObj', or consider not using a voption for this value. + You can create 'ValueSome value' directly instead of 'ofObj', or consider not using a voption for this value. + + + + You can remove this `nonNull` assertion. + You can remove this `nonNull` assertion. + + The 'let! ... and! ...' construct may only be used if the computation expression builder defines either a '{0}' method or appropriate 'MergeSources' and 'Bind' methods Konstrukcji „let! ... and! ...” można użyć tylko wtedy, gdy konstruktor wyrażeń obliczeniowych definiuje metodę „{0}” lub odpowiednie metody „MergeSource” i „Bind” diff --git a/src/Compiler/xlf/FSComp.txt.pt-BR.xlf b/src/Compiler/xlf/FSComp.txt.pt-BR.xlf index fb0a5424787..b9cca9c2c13 100644 --- a/src/Compiler/xlf/FSComp.txt.pt-BR.xlf +++ b/src/Compiler/xlf/FSComp.txt.pt-BR.xlf @@ -1422,6 +1422,36 @@ A sintaxe 'expr1[expr2]' agora está reservada para indexação e é ambígua quando usada como um argumento. Consulte https://aka.ms/fsharp-index-notation. Se chamar uma função com vários argumentos na forma curried, adicione um espaço entre eles, por exemplo, 'someFunction expr1 [expr2]'. + + Value known to be without null passed to a function meant for nullables: {0} + Value known to be without null passed to a function meant for nullables: {0} + + + + You can remove this |Null|NonNull| pattern usage. + You can remove this |Null|NonNull| pattern usage. + + + + You can remove this |NonNullQuick| pattern usage. + You can remove this |NonNullQuick| pattern usage. + + + + You can create 'Some value' directly instead of 'ofObj', or consider not using an option for this value. + You can create 'Some value' directly instead of 'ofObj', or consider not using an option for this value. + + + + You can create 'ValueSome value' directly instead of 'ofObj', or consider not using a voption for this value. + You can create 'ValueSome value' directly instead of 'ofObj', or consider not using a voption for this value. + + + + You can remove this `nonNull` assertion. + You can remove this `nonNull` assertion. + + The 'let! ... and! ...' construct may only be used if the computation expression builder defines either a '{0}' method or appropriate 'MergeSources' and 'Bind' methods O “let! ... and! ...” só poderá ser usada se o construtor de expressão de cálculo definir um método “{0}” ou métodos “MergeSources” e “Bind” apropriados diff --git a/src/Compiler/xlf/FSComp.txt.ru.xlf b/src/Compiler/xlf/FSComp.txt.ru.xlf index 4688e6d71db..cdf57a1f7c1 100644 --- a/src/Compiler/xlf/FSComp.txt.ru.xlf +++ b/src/Compiler/xlf/FSComp.txt.ru.xlf @@ -1422,6 +1422,36 @@ Синтаксис "(expr1)[expr2]" теперь зарезервирован для индексирования и неоднозначен при использовании в качестве аргумента. См. https://aka.ms/fsharp-index-notation. При вызове функции с несколькими каррированными аргументами добавьте между ними пробел, например "someFunction (expr1) [expr2]". + + Value known to be without null passed to a function meant for nullables: {0} + Value known to be without null passed to a function meant for nullables: {0} + + + + You can remove this |Null|NonNull| pattern usage. + You can remove this |Null|NonNull| pattern usage. + + + + You can remove this |NonNullQuick| pattern usage. + You can remove this |NonNullQuick| pattern usage. + + + + You can create 'Some value' directly instead of 'ofObj', or consider not using an option for this value. + You can create 'Some value' directly instead of 'ofObj', or consider not using an option for this value. + + + + You can create 'ValueSome value' directly instead of 'ofObj', or consider not using a voption for this value. + You can create 'ValueSome value' directly instead of 'ofObj', or consider not using a voption for this value. + + + + You can remove this `nonNull` assertion. + You can remove this `nonNull` assertion. + + The 'let! ... and! ...' construct may only be used if the computation expression builder defines either a '{0}' method or appropriate 'MergeSources' and 'Bind' methods Конструкцию "let! ... and! ..." можно использовать только в том случае, если построитель выражений с вычислениями определяет либо метод "{0}", либо соответствующие методы "MergeSources" и "Bind" diff --git a/src/Compiler/xlf/FSComp.txt.tr.xlf b/src/Compiler/xlf/FSComp.txt.tr.xlf index bb2598eaa5a..cc6de83c58b 100644 --- a/src/Compiler/xlf/FSComp.txt.tr.xlf +++ b/src/Compiler/xlf/FSComp.txt.tr.xlf @@ -1422,6 +1422,36 @@ Söz dizimi “(expr1)[expr2]” artık dizin oluşturma için ayrılmıştır ve bağımsız değişken olarak kullanıldığında belirsizdir. https://aka.ms/fsharp-index-notation'a bakın. Birden çok curry bağımsız değişkenli bir işlev çağırıyorsanız, aralarına bir boşluk ekleyin, örn. “someFunction (expr1) [expr2]”. + + Value known to be without null passed to a function meant for nullables: {0} + Value known to be without null passed to a function meant for nullables: {0} + + + + You can remove this |Null|NonNull| pattern usage. + You can remove this |Null|NonNull| pattern usage. + + + + You can remove this |NonNullQuick| pattern usage. + You can remove this |NonNullQuick| pattern usage. + + + + You can create 'Some value' directly instead of 'ofObj', or consider not using an option for this value. + You can create 'Some value' directly instead of 'ofObj', or consider not using an option for this value. + + + + You can create 'ValueSome value' directly instead of 'ofObj', or consider not using a voption for this value. + You can create 'ValueSome value' directly instead of 'ofObj', or consider not using a voption for this value. + + + + You can remove this `nonNull` assertion. + You can remove this `nonNull` assertion. + + The 'let! ... and! ...' construct may only be used if the computation expression builder defines either a '{0}' method or appropriate 'MergeSources' and 'Bind' methods 'let! ... and! ...' yapısı, yalnızca hesaplama ifadesi oluşturucu bir '{0}' metodunu ya da uygun 'MergeSources' ve 'Bind' metotlarını tanımlarsa kullanılabilir diff --git a/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf b/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf index f0eba23c13e..e461bfdb4c9 100644 --- a/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf +++ b/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf @@ -1422,6 +1422,36 @@ 语法“(expr1)[expr2]”现在保留用于索引,用作参数时不明确。请参见 https://aka.ms/fsharp-index-notation。如果使用多个扩充参数调用函数, 请在它们之间添加空格,例如“someFunction (expr1) [expr2]”。 + + Value known to be without null passed to a function meant for nullables: {0} + Value known to be without null passed to a function meant for nullables: {0} + + + + You can remove this |Null|NonNull| pattern usage. + You can remove this |Null|NonNull| pattern usage. + + + + You can remove this |NonNullQuick| pattern usage. + You can remove this |NonNullQuick| pattern usage. + + + + You can create 'Some value' directly instead of 'ofObj', or consider not using an option for this value. + You can create 'Some value' directly instead of 'ofObj', or consider not using an option for this value. + + + + You can create 'ValueSome value' directly instead of 'ofObj', or consider not using a voption for this value. + You can create 'ValueSome value' directly instead of 'ofObj', or consider not using a voption for this value. + + + + You can remove this `nonNull` assertion. + You can remove this `nonNull` assertion. + + The 'let! ... and! ...' construct may only be used if the computation expression builder defines either a '{0}' method or appropriate 'MergeSources' and 'Bind' methods 仅当计算表达式生成器定义了 "{0}" 方法或适当的 "MergeSources" 和 "Bind" 方法时,才可以使用 "let! ... and! ..." 构造 diff --git a/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf b/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf index 08411b992e9..13ed93bebc0 100644 --- a/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf +++ b/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf @@ -1422,6 +1422,36 @@ 語法 '(expr1)[expr2]' 現已為編製索引保留,但用作引數時不明確。請參閱 https://aka.ms/fsharp-index-notation。如果要呼叫具有多個調用引數的函式,請在它們之間新增空格,例如 'someFunction (expr1) [expr2]'。 + + Value known to be without null passed to a function meant for nullables: {0} + Value known to be without null passed to a function meant for nullables: {0} + + + + You can remove this |Null|NonNull| pattern usage. + You can remove this |Null|NonNull| pattern usage. + + + + You can remove this |NonNullQuick| pattern usage. + You can remove this |NonNullQuick| pattern usage. + + + + You can create 'Some value' directly instead of 'ofObj', or consider not using an option for this value. + You can create 'Some value' directly instead of 'ofObj', or consider not using an option for this value. + + + + You can create 'ValueSome value' directly instead of 'ofObj', or consider not using a voption for this value. + You can create 'ValueSome value' directly instead of 'ofObj', or consider not using a voption for this value. + + + + You can remove this `nonNull` assertion. + You can remove this `nonNull` assertion. + + The 'let! ... and! ...' construct may only be used if the computation expression builder defines either a '{0}' method or appropriate 'MergeSources' and 'Bind' methods 只有在計算運算式產生器定義 '{0}' 方法或正確的 'MergeSource' 和 'Bind' 方法時,才可使用 'let! ... and! ...' 建構 diff --git a/src/FSharp.Build/FSharpEmbedResourceText.fs b/src/FSharp.Build/FSharpEmbedResourceText.fs index 397842b41f6..ebaff1986a6 100644 --- a/src/FSharp.Build/FSharpEmbedResourceText.fs +++ b/src/FSharp.Build/FSharpEmbedResourceText.fs @@ -361,6 +361,8 @@ open Printf messageString <- postProcessString messageString createMessageString messageString fmt + static member GetTextOpt(key:string) = GetString(key) |> Option.ofObj + /// If set to true, then all error messages will just return the filled 'holes' delimited by ',,,'s - this is for language-neutral testing (e.g. localization-invariant baselines). static member SwallowResourceText with get () = swallowResourceText and set (b) = swallowResourceText <- b @@ -369,6 +371,9 @@ open Printf let StringBoilerPlateSignature = " // BEGIN BOILERPLATE + + static member GetTextOpt: key:string -> string option + /// If set to true, then all error messages will just return the filled 'holes' delimited by ',,,'s - this is for language-neutral testing (e.g. localization-invariant baselines). static member SwallowResourceText: bool with get, set // END BOILERPLATE" diff --git a/src/FSharp.Core/option.fs b/src/FSharp.Core/option.fs index 9d3e3bd7a3b..9c441bcad01 100644 --- a/src/FSharp.Core/option.fs +++ b/src/FSharp.Core/option.fs @@ -165,7 +165,7 @@ module Option = | None -> null | Some x -> x #else - [] + [] let inline ofObj (value: 'T | null) : 'T option when 'T: not struct and 'T : not null = match value with | null -> None @@ -343,7 +343,7 @@ module ValueOption = | ValueNone -> null | ValueSome x -> x #else - [] + [] let inline ofObj (value: 'T | null) : 'T voption when 'T: not struct and 'T : not null = match value with | null -> ValueNone diff --git a/src/FSharp.Core/option.fsi b/src/FSharp.Core/option.fsi index 2aa2e315d51..6b9f00be200 100644 --- a/src/FSharp.Core/option.fsi +++ b/src/FSharp.Core/option.fsi @@ -444,6 +444,7 @@ module Option = val inline ofObj: value: 'T -> 'T option when 'T : null #else // TODO NULLNESS: assess this change - is it a breaking change? + [] val inline ofObj: value: 'T | null -> 'T option when 'T : not null and 'T : not struct #endif @@ -902,6 +903,7 @@ module ValueOption = val inline ofObj: value: 'T -> 'T voption when 'T : null #else // TODO NULLNESS: assess this change - is it a breaking change? + [] val inline ofObj: value: 'T | null -> 'T voption when 'T : not struct and 'T : not null #endif diff --git a/src/FSharp.Core/prim-types.fs b/src/FSharp.Core/prim-types.fs index 0e87a04c6bb..93179adcc4c 100644 --- a/src/FSharp.Core/prim-types.fs +++ b/src/FSharp.Core/prim-types.fs @@ -377,6 +377,13 @@ namespace Microsoft.FSharp.Core type NoCompilerInliningAttribute() = inherit Attribute() + [] + [] + type WarnOnWithoutNullArgumentAttribute(warningMessage:string) = + inherit Attribute() + member _.WarningMessage = warningMessage + member val internal Localize = false with get, set + [] [] type TailCallAttribute() = diff --git a/src/FSharp.Core/prim-types.fsi b/src/FSharp.Core/prim-types.fsi index eb85b05d6e0..59693d30537 100644 --- a/src/FSharp.Core/prim-types.fsi +++ b/src/FSharp.Core/prim-types.fsi @@ -1,3 +1,4 @@ + // Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. #nowarn "35" // This construct is deprecated: the treatment of this operator is now handled directly by the F# compiler and its meaning may not be redefined. @@ -954,6 +955,24 @@ namespace Microsoft.FSharp.Core /// NoCompilerInliningAttribute new: unit -> NoCompilerInliningAttribute + /// When used in a compilation with null-checking enabled, indicates that a function is meant to be used only with potentially-nullable values and warns accordingly. + /// + /// Attributes + [] + [] + type WarnOnWithoutNullArgumentAttribute = + inherit Attribute + + /// Creates an instance of the attribute + /// The message displayed when the annotated function is used with a value known to be without null + /// WarnOnWithoutNullArgumentAttribute + new: warningMessage:string -> WarnOnWithoutNullArgumentAttribute + + /// Warning message displayed when the annotated function is used with a value known to be without null + member WarningMessage: string + + member internal Localize: bool with get,set + /// Indicates a function that should be called in a tail recursive way inside its recursive scope. /// A warning is emitted if the function is analyzed as not tail recursive after the optimization phase. /// @@ -3448,6 +3467,7 @@ namespace Microsoft.FSharp.Core /// A choice indicating whether the value is null or not-null. [] [] + [] val inline (|Null|NonNull|) : value: 'T | null -> Choice when 'T : not null and 'T : not struct /// Determines whether the given value is null. @@ -3463,6 +3483,7 @@ namespace Microsoft.FSharp.Core /// The non-null value. [] [] + [] val inline (|NonNullQuick|) : value: 'T | null -> 'T when 'T : not null and 'T : not struct /// When used in a pattern checks the given value is not null. @@ -3510,6 +3531,7 @@ namespace Microsoft.FSharp.Core /// The value when it is not null. If the value is null an exception is raised. [] [] + [] val inline nonNull : value: 'T | null -> 'T when 'T : not null and 'T : not struct /// Asserts that the value is non-null. diff --git a/tests/FSharp.Compiler.ComponentTests/Language/NullableReferenceTypesTests.fs b/tests/FSharp.Compiler.ComponentTests/Language/NullableReferenceTypesTests.fs index 9dca34495c5..296d057ee74 100644 --- a/tests/FSharp.Compiler.ComponentTests/Language/NullableReferenceTypesTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/Language/NullableReferenceTypesTests.fs @@ -183,4 +183,132 @@ let myFunction (input1 : string | null) (input2 : string | null): (string*string |> asLibrary |> typeCheckWithStrictNullness |> shouldFail - |> withErrorCode 3261 \ No newline at end of file + |> withErrorCode 3261 + + +[] +let ``Static member on Record with null arg`` () = + FSharp """module MyLibrary + +type MyRecord = {X:string;Y:int} + with static member Create(x:string) = {X=x;Y = 42} + +let thisWorks = MyRecord.Create("xx") +let thisShouldWarn = MyRecord.Create(null) +let maybeNull : string | null = "abc" +let thisShouldAlsoWarn = MyRecord.Create(maybeNull) + +""" + |> asLibrary + |> typeCheckWithStrictNullness + |> shouldFail + |> withDiagnostics + [Error 3261, Line 7, Col 38, Line 7, Col 42, "Nullness warning: The type 'string' does not support 'null'." + Error 3261, Line 9, Col 42, Line 9, Col 51, "Nullness warning: The types 'string' and 'string | null' do not have equivalent nullability."] + + +[] +let ``Option ofObj should remove nullness when used in a function`` () = + FSharp """module MyLibrary + +let processOpt2 (s: string | null) : string option = Option.ofObj s""" + |> asLibrary + |> typeCheckWithStrictNullness + |> shouldSucceed + +[] +let ``Option ofObj should remove nullness when piping`` () = + FSharp """module MyLibrary +let processOpt (s: string | null) : string option = + let stringOpt = Option.ofObj s + stringOpt + +let processOpt3 (s: string | null) : string option = s |> Option.ofObj +""" + |> asLibrary + |> typeCheckWithStrictNullness + |> shouldSucceed + +[] +let ``Option ofObj called in a useless way raises warning`` () = + FSharp """module MyLibrary + +let processOpt1 (s: string) = Option.ofObj s +let processOpt2 (s: string) : option = + Option.ofObj s +let processOpt3 (s: string) : string option = + let sOpt = Option.ofObj s + sOpt +""" + |> asLibrary + |> typeCheckWithStrictNullness + |> shouldFail + |> withDiagnostics + [ Error 3262, Line 3, Col 44, Line 3, Col 45, "Value known to be without null passed to a function meant for nullables: You can create 'Some value' directly instead of 'ofObj', or consider not using an option for this value." + Error 3262, Line 5, Col 18, Line 5, Col 19, "Value known to be without null passed to a function meant for nullables: You can create 'Some value' directly instead of 'ofObj', or consider not using an option for this value." + Error 3262, Line 7, Col 29, Line 7, Col 30, "Value known to be without null passed to a function meant for nullables: You can create 'Some value' directly instead of 'ofObj', or consider not using an option for this value."] + + +[] +let ``Option ofObj called on a string literal`` () = + FSharp """module MyLibrary + +let whatIsThis = Option.ofObj "abc123" +""" + |> asLibrary + |> typeCheckWithStrictNullness + |> shouldFail + |> withErrorCodes [3262] + +[] +let ``Useless null pattern match`` () = + FSharp """module MyLibrary + +let clearlyNotNull = "42" +let mappedVal = + match clearlyNotNull with + | null -> 42 + | _ -> 43 +""" + |> asLibrary + |> typeCheckWithStrictNullness + |> shouldFail + |> withDiagnostics [Error 3261, Line 6, Col 7, Line 6, Col 11, "Nullness warning: The type 'string' does not support 'null'."] + +[] +let ``Useless usage of nonNull utility from fscore`` () = + FSharp """module MyLibrary + +let clearlyNotNull = "42" +let mappedVal = nonNull clearlyNotNull +let maybeNull : string | null = null +let mappedMaybe = nonNull maybeNull + +""" + |> asLibrary + |> typeCheckWithStrictNullness + |> shouldFail + |> withDiagnostics [Error 3262, Line 4, Col 25, Line 4, Col 39, "Value known to be without null passed to a function meant for nullables: You can remove this `nonNull` assertion."] + +[] +let ``Useless usage of null active patterns from fscore`` () = + FSharp """module MyLibrary + +let clearlyNotNull = "42" +let mapped1 = + match clearlyNotNull with + | NonNullQuick safe -> safe + +let mapped2 = + match clearlyNotNull with + |Null -> 0 + |NonNull _ -> 1 +""" + |> asLibrary + |> typeCheckWithStrictNullness + |> shouldFail + |> withDiagnostics + [ Error 3262, Line 6, Col 7, Line 6, Col 24, "Value known to be without null passed to a function meant for nullables: You can remove this |NonNullQuick| pattern usage." + Error 3262, Line 10, Col 6, Line 10, Col 10, "Value known to be without null passed to a function meant for nullables: You can remove this |Null|NonNull| pattern usage." + Error 3262, Line 11, Col 6, Line 11, Col 15, "Value known to be without null passed to a function meant for nullables: You can remove this |Null|NonNull| pattern usage."] + \ No newline at end of file diff --git a/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard20.debug.bsl b/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard20.debug.bsl index 7d20a88524a..e91b310e560 100644 --- a/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard20.debug.bsl +++ b/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard20.debug.bsl @@ -2091,6 +2091,9 @@ Microsoft.FSharp.Core.ValueOption: TState Fold[T,TState](Microsoft.FSharp.Core.F Microsoft.FSharp.Core.ValueOption: T[] ToArray[T](Microsoft.FSharp.Core.FSharpValueOption`1[T]) Microsoft.FSharp.Core.ValueOption: Void Iterate[T](Microsoft.FSharp.Core.FSharpFunc`2[T,Microsoft.FSharp.Core.Unit], Microsoft.FSharp.Core.FSharpValueOption`1[T]) Microsoft.FSharp.Core.VolatileFieldAttribute: Void .ctor() +Microsoft.FSharp.Core.WarnOnWithoutNullArgumentAttribute: System.String WarningMessage +Microsoft.FSharp.Core.WarnOnWithoutNullArgumentAttribute: System.String get_WarningMessage() +Microsoft.FSharp.Core.WarnOnWithoutNullArgumentAttribute: Void .ctor(System.String) Microsoft.FSharp.Linq.NullableModule: System.Nullable`1[System.Byte] ToByte$W[T](Microsoft.FSharp.Core.FSharpFunc`2[T,System.Byte], System.Nullable`1[T]) Microsoft.FSharp.Linq.NullableModule: System.Nullable`1[System.Byte] ToByte[T](System.Nullable`1[T]) Microsoft.FSharp.Linq.NullableModule: System.Nullable`1[System.Byte] ToUInt8$W[T](Microsoft.FSharp.Core.FSharpFunc`2[T,System.Byte], System.Nullable`1[T]) diff --git a/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard20.release.bsl b/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard20.release.bsl index e923211a3a2..de927f601db 100644 --- a/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard20.release.bsl +++ b/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard20.release.bsl @@ -2090,6 +2090,9 @@ Microsoft.FSharp.Core.ValueOption: TState Fold[T,TState](Microsoft.FSharp.Core.F Microsoft.FSharp.Core.ValueOption: T[] ToArray[T](Microsoft.FSharp.Core.FSharpValueOption`1[T]) Microsoft.FSharp.Core.ValueOption: Void Iterate[T](Microsoft.FSharp.Core.FSharpFunc`2[T,Microsoft.FSharp.Core.Unit], Microsoft.FSharp.Core.FSharpValueOption`1[T]) Microsoft.FSharp.Core.VolatileFieldAttribute: Void .ctor() +Microsoft.FSharp.Core.WarnOnWithoutNullArgumentAttribute: System.String WarningMessage +Microsoft.FSharp.Core.WarnOnWithoutNullArgumentAttribute: System.String get_WarningMessage() +Microsoft.FSharp.Core.WarnOnWithoutNullArgumentAttribute: Void .ctor(System.String) Microsoft.FSharp.Linq.NullableModule: System.Nullable`1[System.Byte] ToByte$W[T](Microsoft.FSharp.Core.FSharpFunc`2[T,System.Byte], System.Nullable`1[T]) Microsoft.FSharp.Linq.NullableModule: System.Nullable`1[System.Byte] ToByte[T](System.Nullable`1[T]) Microsoft.FSharp.Linq.NullableModule: System.Nullable`1[System.Byte] ToUInt8$W[T](Microsoft.FSharp.Core.FSharpFunc`2[T,System.Byte], System.Nullable`1[T]) diff --git a/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard21.debug.bsl b/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard21.debug.bsl index 7abc18b3057..36bc2485587 100644 --- a/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard21.debug.bsl +++ b/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard21.debug.bsl @@ -2092,6 +2092,9 @@ Microsoft.FSharp.Core.ValueOption: TState Fold[T,TState](Microsoft.FSharp.Core.F Microsoft.FSharp.Core.ValueOption: T[] ToArray[T](Microsoft.FSharp.Core.FSharpValueOption`1[T]) Microsoft.FSharp.Core.ValueOption: Void Iterate[T](Microsoft.FSharp.Core.FSharpFunc`2[T,Microsoft.FSharp.Core.Unit], Microsoft.FSharp.Core.FSharpValueOption`1[T]) Microsoft.FSharp.Core.VolatileFieldAttribute: Void .ctor() +Microsoft.FSharp.Core.WarnOnWithoutNullArgumentAttribute: System.String WarningMessage +Microsoft.FSharp.Core.WarnOnWithoutNullArgumentAttribute: System.String get_WarningMessage() +Microsoft.FSharp.Core.WarnOnWithoutNullArgumentAttribute: Void .ctor(System.String) Microsoft.FSharp.Linq.NullableModule: System.Nullable`1[System.Byte] ToByte$W[T](Microsoft.FSharp.Core.FSharpFunc`2[T,System.Byte], System.Nullable`1[T]) Microsoft.FSharp.Linq.NullableModule: System.Nullable`1[System.Byte] ToByte[T](System.Nullable`1[T]) Microsoft.FSharp.Linq.NullableModule: System.Nullable`1[System.Byte] ToUInt8$W[T](Microsoft.FSharp.Core.FSharpFunc`2[T,System.Byte], System.Nullable`1[T]) diff --git a/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard21.release.bsl b/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard21.release.bsl index a633fb0dc1b..33946760bbe 100644 --- a/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard21.release.bsl +++ b/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard21.release.bsl @@ -2091,6 +2091,9 @@ Microsoft.FSharp.Core.ValueOption: TState Fold[T,TState](Microsoft.FSharp.Core.F Microsoft.FSharp.Core.ValueOption: T[] ToArray[T](Microsoft.FSharp.Core.FSharpValueOption`1[T]) Microsoft.FSharp.Core.ValueOption: Void Iterate[T](Microsoft.FSharp.Core.FSharpFunc`2[T,Microsoft.FSharp.Core.Unit], Microsoft.FSharp.Core.FSharpValueOption`1[T]) Microsoft.FSharp.Core.VolatileFieldAttribute: Void .ctor() +Microsoft.FSharp.Core.WarnOnWithoutNullArgumentAttribute: System.String WarningMessage +Microsoft.FSharp.Core.WarnOnWithoutNullArgumentAttribute: System.String get_WarningMessage() +Microsoft.FSharp.Core.WarnOnWithoutNullArgumentAttribute: Void .ctor(System.String) Microsoft.FSharp.Linq.NullableModule: System.Nullable`1[System.Byte] ToByte$W[T](Microsoft.FSharp.Core.FSharpFunc`2[T,System.Byte], System.Nullable`1[T]) Microsoft.FSharp.Linq.NullableModule: System.Nullable`1[System.Byte] ToByte[T](System.Nullable`1[T]) Microsoft.FSharp.Linq.NullableModule: System.Nullable`1[System.Byte] ToUInt8$W[T](Microsoft.FSharp.Core.FSharpFunc`2[T,System.Byte], System.Nullable`1[T]) diff --git a/tests/adhoc/nullness/out.fsi b/tests/adhoc/nullness/out.fsi index 0d8f27e5550..9e577b20247 100644 --- a/tests/adhoc/nullness/out.fsi +++ b/tests/adhoc/nullness/out.fsi @@ -188,7 +188,7 @@ module Extractions0e = module Extractions1 = - val mutable x: string + val mutable x: string | null module InteropBasics =