From d20430082165d6b592db4a8ebf4b4294346eafe4 Mon Sep 17 00:00:00 2001 From: Leilei Zhang Date: Thu, 24 Sep 2026 12:33:23 +0800 Subject: [PATCH 1/5] Route Python output annotations through one nullability policy Output annotations (method and property results, out tuples, async results, returned collection elements and collection-class items) were rendered by four overlapping recursive helpers that each decided `| None` from the type alone, with no notion of position, member facts or target file. Add python/nullability.rs with the AnnotationSurface (runtime .py vs .pyi stub), OutputPosition and OutputSite model and output_admits_none, the only place that decides whether an output admits None. Replace py_return_type_safe, py_output_type, py_return_type, py_async_return_type, py_array_return_type and py_collection_return_type with one recursive py_output_annotation that separates spelling from nullability, and thread the surface through the .py and .pyi call sites. Callback parameters keep the legacy py_return_type_safe wrapper. No generated output changes: a 40-namespace Windows.winmd corpus (26,940 .py and 27,022 .pyi files) is byte-identical before and after. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../src/codegen/winrt/python/method.rs | 16 +- .../src/codegen/winrt/python/mod.rs | 1 + .../src/codegen/winrt/python/nullability.rs | 164 +++++ .../src/codegen/winrt/python/stub_helpers.rs | 16 +- .../src/codegen/winrt/python/stubs.rs | 74 +-- .../src/codegen/winrt/python/type_helpers.rs | 585 +++++++++++------- 6 files changed, 590 insertions(+), 266 deletions(-) create mode 100644 tools/dynwinrt-codegen/src/codegen/winrt/python/nullability.rs diff --git a/tools/dynwinrt-codegen/src/codegen/winrt/python/method.rs b/tools/dynwinrt-codegen/src/codegen/winrt/python/method.rs index 3cb9aea5..caf2071b 100644 --- a/tools/dynwinrt-codegen/src/codegen/winrt/python/method.rs +++ b/tools/dynwinrt-codegen/src/codegen/winrt/python/method.rs @@ -10,13 +10,14 @@ use crate::codegen::winrt::shared::imports::{ }; use super::naming::{PythonProjectionContext, PythonTypeIdentity, to_snake_case}; +use super::nullability::AnnotationSurface; use super::signature::{ py_convert_return, py_runtime_named_symbol, py_runtime_symbol, py_type_guard, py_wrap_arg, py_wrap_async, py_wrap_async_with_converters, }; use super::type_helpers::{ method_pydoc, py_delegate_callable_type, py_factory_return_type, py_method_abi_output_count, - py_method_outputs, py_method_return_type, py_output_type, py_param_list, + py_method_outputs, py_method_return_type, py_param_list, py_property_type, }; fn is_delegate_type(typ: &TypeMeta, context: &PythonProjectionContext) -> bool { @@ -282,7 +283,12 @@ fn generate_factory_method_invoke_named( let in_params = get_in_params(method); let py_params = py_param_list(&in_params, context); - let return_py_type = py_factory_return_type(&context.class_name(class), method, context); + let return_py_type = py_factory_return_type( + &context.class_name(class), + method, + AnnotationSurface::Runtime, + context, + ); let mut out = String::new(); let method_name = name_override @@ -360,7 +366,7 @@ fn generate_static_method_invoke_named( let in_params = get_in_params(method); let py_params = py_param_list(&in_params, context); - let py_return = py_method_return_type(method, context); + let py_return = py_method_return_type(method, AnnotationSurface::Runtime, context); let mut out = String::new(); let iface_symbol = context.reference_name(&iface.type_identity()); @@ -780,7 +786,7 @@ pub(crate) fn generate_method_body( if method.is_property_getter && in_params.is_empty() { let prop_name = to_snake_case(method.name.strip_prefix("get_").unwrap_or(&method.name)); let py_return = return_type - .map(|typ| py_output_type(typ, context)) + .map(|typ| py_property_type(typ, AnnotationSurface::Runtime, context)) .unwrap_or_else(|| "None".to_string()); out.push_str(" @_property\n"); out.push_str(&format!(" def {}(self) -> {}:\n", prop_name, py_return)); @@ -830,7 +836,7 @@ pub(crate) fn generate_method_body( )); } else { let py_params = py_param_list(&in_params, context); - let py_return = py_method_return_type(method, context); + let py_return = py_method_return_type(method, AnnotationSurface::Runtime, context); let method_name = name_override .map(|s| s.to_string()) .unwrap_or_else(|| to_snake_case(&method.name)); diff --git a/tools/dynwinrt-codegen/src/codegen/winrt/python/mod.rs b/tools/dynwinrt-codegen/src/codegen/winrt/python/mod.rs index 84a04006..d2194c4e 100644 --- a/tools/dynwinrt-codegen/src/codegen/winrt/python/mod.rs +++ b/tools/dynwinrt-codegen/src/codegen/winrt/python/mod.rs @@ -8,6 +8,7 @@ mod implementation; pub(crate) mod method; pub(crate) mod naming; mod native_types; +pub(crate) mod nullability; pub(crate) mod overloads; mod shared; pub(crate) mod signature; diff --git a/tools/dynwinrt-codegen/src/codegen/winrt/python/nullability.rs b/tools/dynwinrt-codegen/src/codegen/winrt/python/nullability.rs new file mode 100644 index 00000000..7b5e1536 --- /dev/null +++ b/tools/dynwinrt-codegen/src/codegen/winrt/python/nullability.rs @@ -0,0 +1,164 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +//! The one policy deciding whether a projected output annotation admits +//! `None`. +//! +//! WinRT metadata carries no nullability, so whether a consumer-facing value +//! is annotated `T | None` depends on the value's type, the position it is +//! received at, facts about the member producing it, and the generated file +//! the annotation is rendered into. Renderers in `type_helpers` spell the +//! non-null type expression; only [`output_admits_none`] decides whether +//! `| None` is appended. + +use crate::codegen::winrt::shared::imports::ireference_inner_type; +use crate::meta::MethodMeta; +use crate::types::TypeMeta; + +use super::naming::PythonProjectionContext; + +/// The generated artifact an annotation is rendered into. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub(crate) enum AnnotationSurface { + /// Inline annotations of runtime `.py` modules. `typing.get_type_hints()` + /// exposes them, and checkers read them when stubs are not generated. + Runtime, + /// `.pyi` stubs read by type checkers. + Stub, +} + +/// Where the consumer receives a value from the projection. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub(crate) enum OutputPosition { + /// Return value of a method. + Return, + /// Out value in a method's result tuple. + OutParam, + /// Value read from a property getter. + Property, + /// Completed value of an async operation. + AsyncResult, + /// Progress value reported by an async operation. + AsyncProgress, + /// Element, key or value read from a collection or an array. + CollectionElement, + /// Sender or argument the runtime passes to a consumer callback. + CallbackParam, + /// Instance created by an activation factory. Activation reports failure + /// by raising, so the instance is never null unless the factory follows + /// the `Try*` pattern. + Activation, +} + +/// A position plus the facts about the member producing the value. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub(crate) struct OutputSite { + pub(crate) position: OutputPosition, + /// The member follows the `Try*` pattern, so a null result is part of + /// its contract ("not found", "could not parse"). + pub(crate) try_method: bool, +} + +impl OutputSite { + pub(crate) fn of(position: OutputPosition) -> Self { + Self { + position, + try_method: false, + } + } + + pub(crate) fn for_method(method: &MethodMeta, position: OutputPosition) -> Self { + Self { + position, + try_method: is_try_method(method), + } + } + + /// The site of a value nested in this one, such as an async result or a + /// collection element. Member facts carry over; the policy decides where + /// they apply. + pub(crate) fn nested(self, position: OutputPosition) -> Self { + Self { position, ..self } + } +} + +/// `TryParse`, `TryGetItemAsync`, ...: the CLR name is `Try` followed by an +/// uppercase letter. +pub(crate) fn is_try_method(method: &MethodMeta) -> bool { + method + .raw_name + .strip_prefix("Try") + .and_then(|rest| rest.chars().next()) + .is_some_and(|next| next.is_ascii_uppercase()) +} + +/// Whether the runtime converts a null ABI value of `typ` to `None`: +/// interface pointers (objects, classes, interfaces, delegates and +/// parameterized interfaces, including `IReference`). Value types, +/// strings, arrays and async operations never project as `None`. +pub(crate) fn may_project_none(typ: &TypeMeta) -> bool { + matches!( + typ, + TypeMeta::Object + | TypeMeta::Delegate { .. } + | TypeMeta::RuntimeClass { .. } + | TypeMeta::Interface { .. } + | TypeMeta::Parameterized { .. } + ) +} + +/// Whether the annotation of `typ` received at `site` admits `None` on +/// `surface`. This is the only place that makes that decision for outputs. +pub(crate) fn output_admits_none( + typ: &TypeMeta, + site: OutputSite, + surface: AnnotationSurface, + _context: &PythonProjectionContext, +) -> bool { + if ireference_inner_type(typ).is_some() { + return true; + } + if !may_project_none(typ) { + return false; + } + match (surface, site.position) { + (_, OutputPosition::Activation) => false, + (AnnotationSurface::Runtime, _) => true, + (AnnotationSurface::Stub, _) => true, + } +} + +#[cfg(test)] +mod tests { + use super::*; + + fn method(raw_name: &str) -> MethodMeta { + MethodMeta { + name: raw_name.into(), + raw_name: raw_name.into(), + ..Default::default() + } + } + + #[test] + fn try_methods_require_an_uppercase_continuation() { + assert!(is_try_method(&method("TryParse"))); + assert!(is_try_method(&method("TryGetItemAsync"))); + assert!(!is_try_method(&method("Try"))); + assert!(!is_try_method(&method("Trying"))); + assert!(!is_try_method(&method("GetTryCount"))); + assert!(!is_try_method(&method("get_TryCount"))); + } + + #[test] + fn nested_sites_keep_member_facts() { + let site = OutputSite::for_method(&method("TryGetItemAsync"), OutputPosition::Return); + assert_eq!( + site.nested(OutputPosition::AsyncResult), + OutputSite { + position: OutputPosition::AsyncResult, + try_method: true, + } + ); + } +} diff --git a/tools/dynwinrt-codegen/src/codegen/winrt/python/stub_helpers.rs b/tools/dynwinrt-codegen/src/codegen/winrt/python/stub_helpers.rs index 316b4319..b1eaee21 100644 --- a/tools/dynwinrt-codegen/src/codegen/winrt/python/stub_helpers.rs +++ b/tools/dynwinrt-codegen/src/codegen/winrt/python/stub_helpers.rs @@ -9,10 +9,12 @@ use crate::types::{FieldMeta, TypeMeta}; use super::naming::{PythonProjectionContext, PythonSymbol, STRUCT_SYMBOLS, to_snake_case}; use super::native_types::{FoundationType, foundation_type}; +use super::nullability::AnnotationSurface; use super::structs::{py_struct_field_read_type, py_struct_field_type}; use super::type_helpers::{ - method_pydoc_with_indent, py_delegate_callable_type, py_factory_return_type, - py_method_return_type, py_output_type, py_param_list, py_param_type_safe, + method_pydoc_with_indent, py_collection_item_type, py_delegate_callable_type, + py_factory_return_type, py_method_return_type, py_param_list, py_param_type_safe, + py_property_type, }; use crate::codegen::winrt::shared::imports::ireference_inner_type; @@ -274,7 +276,7 @@ pub(super) fn emit_method_stub_named( if method.is_property_getter && in_params.is_empty() { let prop_name = to_snake_case(method.name.strip_prefix("get_").unwrap_or(&method.name)); let py_return = return_type - .map(|typ| py_output_type(typ, context)) + .map(|typ| py_property_type(typ, AnnotationSurface::Stub, context)) .unwrap_or_else(|| "None".to_string()); out.push_str(&format!("{indent}@builtins.property\n")); emit_documented_stub( @@ -317,7 +319,7 @@ pub(super) fn emit_method_stub_named( } } else { let py_params = py_param_list(&in_params, context); - let py_return = py_method_return_type(method, context); + let py_return = py_method_return_type(method, AnnotationSurface::Stub, context); let method_name = name_override .map(str::to_string) .unwrap_or_else(|| to_snake_case(&method.name)); @@ -334,7 +336,7 @@ pub(super) fn emit_method_stub_named( && method_name == "append" && in_params.first().is_some_and(|param| { py_param_type_safe(¶m.typ, context) - != super::type_helpers::py_return_type_safe(Some(¶m.typ), context) + != py_collection_item_type(¶m.typ, AnnotationSurface::Stub, context) }) { " # type: ignore[override, unused-ignore]" } else { @@ -372,9 +374,9 @@ pub(super) fn emit_static_method_stub_named( let py_params = py_param_list(&in_params, context); let py_return = if is_factory { - py_factory_return_type(class_name, method, context) + py_factory_return_type(class_name, method, AnnotationSurface::Stub, context) } else { - py_method_return_type(method, context) + py_method_return_type(method, AnnotationSurface::Stub, context) }; let mut out = String::new(); diff --git a/tools/dynwinrt-codegen/src/codegen/winrt/python/stubs.rs b/tools/dynwinrt-codegen/src/codegen/winrt/python/stubs.rs index 0568bbf9..15baf2ea 100644 --- a/tools/dynwinrt-codegen/src/codegen/winrt/python/stubs.rs +++ b/tools/dynwinrt-codegen/src/codegen/winrt/python/stubs.rs @@ -29,6 +29,7 @@ use super::collections::{ }; use super::naming::{PythonProjectionContext, PythonSupportSymbol, is_py_reserved, to_snake_case}; use super::native_types::foundation_type; +use super::nullability::AnnotationSurface; use super::shared::reorder_getters_before_setters; use super::signature::py_dynwinrt_type; use super::stub_helpers::{ @@ -451,23 +452,14 @@ pub fn generate_interface_stub(context: &PythonProjectionContext, iface: &Interf } } - let collection_base = - collection_kind - .and_then(abc_name) - .and_then(|abc| match iface.generic_args.as_slice() { - [element] => Some(format!( - "{}[{}]", - abc, - super::type_helpers::py_return_type_safe(Some(element), context) - )), - [key, value] => Some(format!( - "{}[{}, {}]", - abc, - super::type_helpers::py_return_type_safe(Some(key), context), - super::type_helpers::py_return_type_safe(Some(value), context) - )), - _ => None, - }); + let collection_base = collection_kind.and_then(abc_name).and_then(|abc| { + super::type_helpers::py_collection_base_type( + abc, + &iface.generic_args, + AnnotationSurface::Stub, + context, + ) + }); let identity_name = format!("_{}Identity", iface.name); out.push_str(&format!("\nclass {identity_name}(Protocol):\n")); out.push_str(&format!(" def {marker}(self) -> None: ...\n")); @@ -865,19 +857,13 @@ pub fn generate_class_stub( let collection_base = collection_iface .zip(collection_kind.and_then(abc_name)) - .and_then(|(iface, abc)| match iface.generic_args.as_slice() { - [element] => Some(format!( - "{}[{}]", - abc, - super::type_helpers::py_return_type_safe(Some(element), context) - )), - [key, value] => Some(format!( - "{}[{}, {}]", + .and_then(|(iface, abc)| { + super::type_helpers::py_collection_base_type( abc, - super::type_helpers::py_return_type_safe(Some(key), context), - super::type_helpers::py_return_type_safe(Some(value), context) - )), - _ => None, + &iface.generic_args, + AnnotationSurface::Stub, + context, + ) }); let mut instance_stub_body = emit_class_instance_stubs(class, context, collection_iface, false, has_closable); @@ -1061,19 +1047,13 @@ pub fn generate_class_stub( out.push('\n'); let required_base = interface_kind(req_iface) .and_then(abc_name) - .and_then(|abc| match req_iface.generic_args.as_slice() { - [element] => Some(format!( - "{}[{}]", + .and_then(|abc| { + super::type_helpers::py_collection_base_type( abc, - super::type_helpers::py_return_type_safe(Some(element), context) - )), - [key, value] => Some(format!( - "{}[{}, {}]", - abc, - super::type_helpers::py_return_type_safe(Some(key), context), - super::type_helpers::py_return_type_safe(Some(value), context) - )), - _ => None, + &req_iface.generic_args, + AnnotationSurface::Stub, + context, + ) }); if let Some(base) = required_base { out.push_str(&format!("\nclass {symbol}({base}):\n")); @@ -1298,7 +1278,9 @@ fn collection_protocol_stubs( let item_type = iface .generic_args .first() - .map(|typ| super::type_helpers::py_return_type_safe(Some(typ), context)) + .map(|typ| { + super::type_helpers::py_collection_item_type(typ, AnnotationSurface::Stub, context) + }) .unwrap_or_else(|| "object".to_string()); let item_input = iface .generic_args @@ -1341,7 +1323,13 @@ fn collection_protocol_stubs( let value_type = iface .generic_args .get(1) - .map(|typ| super::type_helpers::py_return_type_safe(Some(typ), context)) + .map(|typ| { + super::type_helpers::py_collection_item_type( + typ, + AnnotationSurface::Stub, + context, + ) + }) .unwrap_or_else(|| "object".to_string()); let mut result = format!( "\n{indent}def __len__(self) -> int: ...\n\ diff --git a/tools/dynwinrt-codegen/src/codegen/winrt/python/type_helpers.rs b/tools/dynwinrt-codegen/src/codegen/winrt/python/type_helpers.rs index 34d75531..4d20ab69 100644 --- a/tools/dynwinrt-codegen/src/codegen/winrt/python/type_helpers.rs +++ b/tools/dynwinrt-codegen/src/codegen/winrt/python/type_helpers.rs @@ -10,11 +10,14 @@ use crate::codegen::winrt::shared::imports::{ use crate::meta::MethodMeta; use crate::types::TypeMeta; -use super::collections::{CollectionKind, is_mapping_input, type_kind}; +use super::collections::{CollectionKind, abc_name, is_mapping_input, type_kind}; use super::docs::format_pydoc; use super::naming::to_snake_case; use super::naming::{PythonProjectionContext, PythonSupportSymbol, PythonSymbol}; use super::native_types::{FoundationType, foundation_type}; +use super::nullability::{ + AnnotationSurface, OutputPosition, OutputSite, may_project_none, output_admits_none, +}; /// Build the Python docstring for a method body. Uses snake_case param display /// names (matching the generated signature). Returns an empty string when no @@ -58,25 +61,17 @@ pub(super) fn method_pydoc_with_indent( // ====================================================================== pub(crate) fn py_optional_type(typ: String) -> String { - let unquoted = typ - .strip_prefix('\'') - .and_then(|value| value.strip_suffix('\'')) - .unwrap_or(&typ); + let unquoted = unquoted(&typ); if unquoted.split('|').any(|part| part.trim() == "None") { return unquoted.to_string(); } format!("{} | None", unquoted) } -fn is_nullable_reference_type(typ: &TypeMeta) -> bool { - matches!( - typ, - TypeMeta::Object - | TypeMeta::Delegate { .. } - | TypeMeta::RuntimeClass { .. } - | TypeMeta::Interface { .. } - | TypeMeta::Parameterized { .. } - ) +fn unquoted(typ: &str) -> &str { + typ.strip_prefix('\'') + .and_then(|value| value.strip_suffix('\'')) + .unwrap_or(typ) } fn py_param_type(typ: &TypeMeta, context: &PythonProjectionContext) -> String { @@ -152,85 +147,324 @@ pub(super) fn py_collection_input_type( ) -> String { let input = py_param_type_safe(typ, context); // Keep the existing nullable ABC contract; only widen its projected inputs. - if is_nullable_reference_type(typ) { + if may_project_none(typ) { py_optional_type(input) } else { input } } -pub(crate) fn py_return_type_safe( - typ: Option<&TypeMeta>, +// ====================================================================== +// Output annotations +// +// Rendering and nullability are separate layers: the `spell_*` functions +// produce the non-null type expression for a position, and +// `nullability::output_admits_none` alone decides whether `| None` is added. +// ====================================================================== + +/// Spelling rules of the rendering layer. They predate the nullability policy +/// and are preserved byte-for-byte. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +enum Spelling { + /// Method, out and property results: delegates are raw `DynWinRTValue` + /// handles, also inside async results and arrays. + Member, + /// A standalone value, such as the item type of a collection class. + Value, + /// An element of a returned collection or array: nested generics are + /// named by their projected class. + Element, +} + +impl Spelling { + fn at(position: OutputPosition) -> Self { + match position { + OutputPosition::CollectionElement | OutputPosition::CallbackParam => Self::Value, + OutputPosition::Return + | OutputPosition::OutParam + | OutputPosition::Property + | OutputPosition::AsyncResult + | OutputPosition::AsyncProgress + | OutputPosition::Activation => Self::Member, + } + } +} + +/// Renders the annotation of a value the consumer receives at `site`. +/// +/// Nullability never changes how the base type is spelled: a value that may +/// project as `None` renders the same unquoted expression with or without +/// ` | None`. +pub(crate) fn py_output_annotation( + typ: &TypeMeta, + site: OutputSite, + surface: AnnotationSurface, context: &PythonProjectionContext, ) -> String { - if let Some(inner) = typ.and_then(ireference_inner_type) { - return py_optional_type(py_return_type_safe(Some(inner), context)); - } - if let Some(async_type) = typ.and_then(|typ| py_async_return_type(typ, context)) { - return async_type; + render_output(typ, Spelling::at(site.position), site, surface, context) +} + +fn render_output( + typ: &TypeMeta, + spelling: Spelling, + site: OutputSite, + surface: AnnotationSurface, + context: &PythonProjectionContext, +) -> String { + let base = match spelling { + Spelling::Member => spell_member(typ, site, surface, context), + Spelling::Value => spell_value(typ, site, surface, context), + Spelling::Element => py_native_element_type(typ, context), + }; + if !may_project_none(typ) { + return base; } - if let Some(annotation) = typ.and_then(|typ| py_collection_return_type(typ, context)) { - return py_optional_type(annotation); + if output_admits_none(typ, site, surface, context) { + py_optional_type(base) + } else { + unquoted(&base).to_string() } +} +fn spell_member( + typ: &TypeMeta, + site: OutputSite, + surface: AnnotationSurface, + context: &PythonProjectionContext, +) -> String { + if context.is_delegate_type(typ) { + return "DynWinRTValue".to_string(); + } + let nested = |typ: &TypeMeta, position: OutputPosition| { + render_output( + typ, + Spelling::Member, + site.nested(position), + surface, + context, + ) + }; match typ { - Some(typ @ TypeMeta::Enum { .. }) if !context.is_known_type(typ) => "int".to_string(), - Some(typ @ (TypeMeta::RuntimeClass { .. } | TypeMeta::Interface { .. })) - if !context.is_known_type(typ) => - { - "DynWinRTValue | None".to_string() + TypeMeta::Array(inner) if context.is_delegate_type(inner) => { + format!("list[{}]", nested(inner, OutputPosition::CollectionElement)) } - Some(TypeMeta::Array(inner)) => py_array_return_type(inner, context), - Some(typ) if is_nullable_reference_type(typ) => { - py_optional_type(py_return_type(Some(typ), context)) + TypeMeta::AsyncOperation(result) => { + format!( + "WinRTCoroutine[{}]", + nested(result, OutputPosition::AsyncResult) + ) } - _ => py_return_type(typ, context), + TypeMeta::AsyncOperationWithProgress(result, progress) => format!( + "WinRTCoroutineWithProgress[{}, {}]", + nested(result, OutputPosition::AsyncResult), + nested(progress, OutputPosition::AsyncProgress) + ), + TypeMeta::AsyncActionWithProgress(progress) => format!( + "WinRTCoroutineWithProgress[None, {}]", + nested(progress, OutputPosition::AsyncProgress) + ), + _ => spell_value(typ, site, surface, context), } } -fn py_async_return_type_with_result( +fn spell_value( typ: &TypeMeta, - result_override: Option, + site: OutputSite, + surface: AnnotationSurface, context: &PythonProjectionContext, -) -> Option { +) -> String { + if let Some(inner) = ireference_inner_type(typ) { + return render_output(inner, Spelling::Value, site, surface, context); + } + if let Some(collection) = spell_collection(typ, site, surface, context) { + return collection; + } + let nested = |typ: &TypeMeta, spelling: Spelling, position: OutputPosition| { + render_output(typ, spelling, site.nested(position), surface, context) + }; match typ { - TypeMeta::AsyncAction => Some("WinRTCoroutine[None]".to_string()), - TypeMeta::AsyncOperation(result) => Some(format!( + TypeMeta::AsyncAction => "WinRTCoroutine[None]".to_string(), + TypeMeta::AsyncOperation(result) => format!( "WinRTCoroutine[{}]", - result_override.unwrap_or_else(|| py_return_type_safe(Some(result), context)) - )), - TypeMeta::AsyncActionWithProgress(progress) => Some(format!( + nested(result, Spelling::Value, OutputPosition::AsyncResult) + ), + TypeMeta::AsyncActionWithProgress(progress) => format!( "WinRTCoroutineWithProgress[None, {}]", - py_return_type_safe(Some(progress), context) - )), - TypeMeta::AsyncOperationWithProgress(result, progress) => Some(format!( + nested(progress, Spelling::Value, OutputPosition::AsyncProgress) + ), + TypeMeta::AsyncOperationWithProgress(result, progress) => format!( "WinRTCoroutineWithProgress[{}, {}]", - result_override.unwrap_or_else(|| py_return_type_safe(Some(result), context)), - py_return_type_safe(Some(progress), context) - )), - _ => None, + nested(result, Spelling::Value, OutputPosition::AsyncResult), + nested(progress, Spelling::Value, OutputPosition::AsyncProgress) + ), + TypeMeta::Enum { .. } if !context.is_known_type(typ) => "int".to_string(), + TypeMeta::RuntimeClass { .. } | TypeMeta::Interface { .. } + if !context.is_known_type(typ) => + { + "DynWinRTValue".to_string() + } + TypeMeta::Array(inner) if matches!(inner.as_ref(), TypeMeta::U8) => "bytes".to_string(), + TypeMeta::Array(inner) => format!( + "list[{}]", + nested(inner, Spelling::Element, OutputPosition::CollectionElement) + ), + TypeMeta::String | TypeMeta::Char16 => "str".to_string(), + TypeMeta::Guid => "UUID".to_string(), + TypeMeta::Bool => "bool".to_string(), + TypeMeta::I8 + | TypeMeta::U8 + | TypeMeta::I16 + | TypeMeta::U16 + | TypeMeta::I32 + | TypeMeta::U32 + | TypeMeta::I64 + | TypeMeta::U64 => "int".to_string(), + TypeMeta::F32 | TypeMeta::F64 => "float".to_string(), + TypeMeta::RuntimeClass { .. } + | TypeMeta::Enum { .. } + | TypeMeta::Interface { .. } + | TypeMeta::Parameterized { .. } => { + format!("'{}'", context.reference_name_for_type(typ)) + } + TypeMeta::Object | TypeMeta::Delegate { .. } => "'DynWinRTValue'".to_string(), + TypeMeta::Struct { name, .. } if name == "HResult" => "int".to_string(), + typ if foundation_type(typ) == Some(FoundationType::DateTime) => "datetime".to_string(), + typ if foundation_type(typ) == Some(FoundationType::TimeSpan) => "timedelta".to_string(), + TypeMeta::Struct { .. } => format!("'{}'", context.reference_name_for_type(typ)), } } -pub(super) fn py_async_return_type( +fn spell_collection( + typ: &TypeMeta, + site: OutputSite, + surface: AnnotationSurface, + context: &PythonProjectionContext, +) -> Option { + let TypeMeta::Parameterized { args, .. } = typ else { + return None; + }; + let abc = type_kind(typ).and_then(abc_name)?; + let elements = args + .iter() + .map(|arg| { + render_output( + arg, + Spelling::Element, + site.nested(OutputPosition::CollectionElement), + surface, + context, + ) + }) + .collect::>(); + Some(format!("{abc}[{}]", elements.join(", "))) +} + +/// Pessimistic rendering for callers outside the output policy (callback +/// parameters and the `IReference` input arm): every value that may +/// project as `None` admits it, as on the runtime surface. +pub(crate) fn py_return_type_safe( + typ: Option<&TypeMeta>, + context: &PythonProjectionContext, +) -> String { + typ.map(|typ| { + render_output( + typ, + Spelling::Value, + OutputSite::of(OutputPosition::CallbackParam), + AnnotationSurface::Runtime, + context, + ) + }) + .unwrap_or_else(|| "None".to_string()) +} + +/// Annotation of a property getter's value. +pub(super) fn py_property_type( typ: &TypeMeta, + surface: AnnotationSurface, + context: &PythonProjectionContext, +) -> String { + py_output_annotation( + typ, + OutputSite::of(OutputPosition::Property), + surface, + context, + ) +} + +/// Item, key or value type of a projected collection class. +pub(super) fn py_collection_item_type( + typ: &TypeMeta, + surface: AnnotationSurface, + context: &PythonProjectionContext, +) -> String { + py_output_annotation( + typ, + OutputSite::of(OutputPosition::CollectionElement), + surface, + context, + ) +} + +/// `Sequence[T]` / `Mapping[K, V]` base of a projected collection class. +pub(super) fn py_collection_base_type( + abc: &str, + args: &[TypeMeta], + surface: AnnotationSurface, context: &PythonProjectionContext, ) -> Option { - py_async_return_type_with_result(typ, None, context) + let item = |typ| py_collection_item_type(typ, surface, context); + match args { + [element] => Some(format!("{abc}[{}]", item(element))), + [key, value] => Some(format!("{abc}[{}, {}]", item(key), item(value))), + _ => None, + } } +/// Result annotation of an activation factory creating `class_name`. pub(super) fn py_factory_return_type( class_name: &str, method: &MethodMeta, + surface: AnnotationSurface, context: &PythonProjectionContext, ) -> String { - method - .return_type - .as_ref() - .and_then(|typ| { - py_async_return_type_with_result(typ, Some(format!("'{}'", class_name)), context) - }) - .unwrap_or_else(|| format!("'{}'", class_name)) + let instance = |typ: &TypeMeta| { + let instance = format!("'{class_name}'"); + let site = OutputSite::for_method(method, OutputPosition::Activation); + if output_admits_none(typ, site, surface, context) { + py_optional_type(instance) + } else { + instance + } + }; + let progress = |typ: &TypeMeta| { + render_output( + typ, + Spelling::Value, + OutputSite::for_method(method, OutputPosition::AsyncProgress), + surface, + context, + ) + }; + match method.return_type.as_ref() { + None => format!("'{class_name}'"), + Some(TypeMeta::AsyncAction) => "WinRTCoroutine[None]".to_string(), + Some(TypeMeta::AsyncOperation(result)) => { + format!("WinRTCoroutine[{}]", instance(result)) + } + Some(TypeMeta::AsyncActionWithProgress(progress_type)) => { + format!( + "WinRTCoroutineWithProgress[None, {}]", + progress(progress_type) + ) + } + Some(TypeMeta::AsyncOperationWithProgress(result, progress_type)) => format!( + "WinRTCoroutineWithProgress[{}, {}]", + instance(result), + progress(progress_type) + ), + Some(typ) => instance(typ), + } } pub(super) fn methods_have_async_output<'a>( @@ -249,20 +483,26 @@ pub(super) fn py_method_abi_output_count(method: &MethodMeta) -> usize { } pub(super) fn py_method_outputs(method: &MethodMeta) -> Vec<(usize, &TypeMeta)> { - let mut result_index = 0; + py_method_output_positions(method) + .into_iter() + .enumerate() + .map(|(result_index, (typ, _))| (result_index, typ)) + .collect() +} + +/// Logical outputs in result order: out values first, then the return value. +fn py_method_output_positions(method: &MethodMeta) -> Vec<(&TypeMeta, OutputPosition)> { let mut outputs = Vec::new(); for param in &method.params { match param.direction { crate::meta::ParamDirection::Out => { - outputs.push((result_index, ¶m.typ)); - result_index += 1; + outputs.push((¶m.typ, OutputPosition::OutParam)); } crate::meta::ParamDirection::OutFill => { // The runtime allocates a distinct filled result buffer; the // caller-provided array supplies capacity and is not mutated. - outputs.push((result_index, ¶m.typ)); - result_index += 1; + outputs.push((¶m.typ, OutputPosition::OutParam)); } crate::meta::ParamDirection::In => {} } @@ -273,108 +513,32 @@ pub(super) fn py_method_outputs(method: &MethodMeta) -> Vec<(usize, &TypeMeta)> .as_ref() .filter(|_| !fill_array_uses_retval_count(method)) { - outputs.push((result_index, return_type)); + outputs.push((return_type, OutputPosition::Return)); } outputs } -fn is_delegate_output(typ: &TypeMeta, context: &PythonProjectionContext) -> bool { - context.is_delegate_type(typ) -} - -pub(super) fn py_output_type(typ: &TypeMeta, context: &PythonProjectionContext) -> String { - match typ { - _ if is_delegate_output(typ, context) => "DynWinRTValue | None".to_string(), - TypeMeta::Array(inner) if is_delegate_output(inner, context) => { - "list[DynWinRTValue | None]".to_string() - } - TypeMeta::AsyncOperation(inner) => { - format!("WinRTCoroutine[{}]", py_output_type(inner, context)) - } - TypeMeta::AsyncOperationWithProgress(result, progress) => format!( - "WinRTCoroutineWithProgress[{}, {}]", - py_output_type(result, context), - py_output_type(progress, context) - ), - TypeMeta::AsyncActionWithProgress(progress) => format!( - "WinRTCoroutineWithProgress[None, {}]", - py_output_type(progress, context) - ), - _ => py_return_type_safe(Some(typ), context), - } -} - pub(super) fn py_method_return_type( method: &MethodMeta, + surface: AnnotationSurface, context: &PythonProjectionContext, ) -> String { - let outputs = py_method_outputs(method); + let outputs = py_method_output_positions(method) + .into_iter() + .map(|(typ, position)| { + py_output_annotation( + typ, + OutputSite::for_method(method, position), + surface, + context, + ) + }) + .collect::>(); match outputs.as_slice() { [] => "None".to_string(), - [(_, typ)] => py_output_type(typ, context), - _ => format!( - "tuple[{}]", - outputs - .iter() - .map(|(_, typ)| py_output_type(typ, context)) - .collect::>() - .join(", ") - ), - } -} - -fn py_return_type(typ: Option<&TypeMeta>, context: &PythonProjectionContext) -> String { - match typ { - Some(TypeMeta::String) => "str".to_string(), - Some(TypeMeta::Guid) => "UUID".to_string(), - Some(TypeMeta::Bool) => "bool".to_string(), - Some( - TypeMeta::I8 - | TypeMeta::U8 - | TypeMeta::I16 - | TypeMeta::U16 - | TypeMeta::I32 - | TypeMeta::U32 - | TypeMeta::I64 - | TypeMeta::U64, - ) => "int".to_string(), - Some(TypeMeta::Char16) => "str".to_string(), - Some(TypeMeta::F32 | TypeMeta::F64) => "float".to_string(), - Some(typ @ TypeMeta::RuntimeClass { .. }) - | Some(typ @ TypeMeta::Enum { .. }) - | Some(typ @ TypeMeta::Interface { .. }) => { - format!("'{}'", context.reference_name_for_type(typ)) - } - Some(typ @ TypeMeta::Parameterized { .. }) => { - format!("'{}'", context.reference_name_for_type(typ)) - } - Some(TypeMeta::AsyncOperation(inner)) => { - format!("WinRTCoroutine[{}]", py_return_type(Some(inner), context)) - } - Some(TypeMeta::AsyncOperationWithProgress(result, progress)) => format!( - "WinRTCoroutineWithProgress[{}, {}]", - py_return_type(Some(result), context), - py_return_type(Some(progress), context) - ), - Some(TypeMeta::AsyncAction) => "WinRTCoroutine[None]".to_string(), - Some(TypeMeta::AsyncActionWithProgress(progress)) => format!( - "WinRTCoroutineWithProgress[None, {}]", - py_return_type(Some(progress), context) - ), - Some(TypeMeta::Array(inner)) => py_array_return_type(inner, context), - Some(TypeMeta::Object) | Some(TypeMeta::Delegate { .. }) => "'DynWinRTValue'".to_string(), - Some(TypeMeta::Struct { name, .. }) if name == "HResult" => "int".to_string(), - Some(typ) if foundation_type(typ) == Some(FoundationType::DateTime) => { - "datetime".to_string() - } - Some(typ) if foundation_type(typ) == Some(FoundationType::TimeSpan) => { - "timedelta".to_string() - } - Some(typ @ TypeMeta::Struct { .. }) => { - format!("'{}'", context.reference_name_for_type(typ)) - } - None => "None".to_string(), + [output] => output.clone(), + _ => format!("tuple[{}]", outputs.join(", ")), } } @@ -441,20 +605,6 @@ fn py_native_param_element_type(inner: &TypeMeta, context: &PythonProjectionCont } } -fn py_array_return_type(inner: &TypeMeta, context: &PythonProjectionContext) -> String { - if matches!(inner, TypeMeta::U8) { - "bytes".to_string() - } else { - let element = py_native_element_type(inner, context); - let element = if is_nullable_reference_type(inner) { - py_optional_type(element) - } else { - element - }; - format!("list[{element}]") - } -} - fn py_collection_param_type(typ: &TypeMeta, context: &PythonProjectionContext) -> Option { let TypeMeta::Parameterized { args, .. } = typ else { return None; @@ -500,26 +650,6 @@ fn py_collection_param_type(typ: &TypeMeta, context: &PythonProjectionContext) - } } -fn py_collection_return_type(typ: &TypeMeta, context: &PythonProjectionContext) -> Option { - let TypeMeta::Parameterized { args, .. } = typ else { - return None; - }; - let kind = type_kind(typ)?; - let abc = super::collections::abc_name(kind)?; - let types = args - .iter() - .map(|arg| { - let element = py_native_element_type(arg, context); - if is_nullable_reference_type(arg) { - py_optional_type(element) - } else { - element - } - }) - .collect::>(); - Some(format!("{abc}[{}]", types.join(", "))) -} - pub(super) fn py_param_list( in_params: &[&crate::meta::ParamMeta], context: &PythonProjectionContext, @@ -587,6 +717,19 @@ mod tests { use super::*; use crate::meta::{ParamDirection, ParamMeta}; + fn returned( + typ: &TypeMeta, + surface: AnnotationSurface, + context: &PythonProjectionContext, + ) -> String { + py_output_annotation( + typ, + OutputSite::of(OutputPosition::Return), + surface, + context, + ) + } + #[test] fn multi_out_returns_typed_tuple_in_abi_order() { let method = MethodMeta { @@ -613,7 +756,11 @@ mod tests { assert_eq!(outputs[0], (0, &TypeMeta::U32)); assert_eq!(outputs[1], (1, &TypeMeta::Bool)); assert_eq!( - py_method_return_type(&method, &PythonProjectionContext::default()), + py_method_return_type( + &method, + AnnotationSurface::Stub, + &PythonProjectionContext::default() + ), "tuple[int, bool]" ); } @@ -646,7 +793,11 @@ mod tests { (0, &TypeMeta::Array(Box::new(TypeMeta::String))) ); assert_eq!( - py_method_return_type(&method, &PythonProjectionContext::default()), + py_method_return_type( + &method, + AnnotationSurface::Stub, + &PythonProjectionContext::default() + ), "list[str]" ); assert_eq!( @@ -665,10 +816,13 @@ mod tests { #[test] fn object_arrays_return_typed_runtime_values() { - assert_eq!( - py_array_return_type(&TypeMeta::Object, &PythonProjectionContext::default()), - "list[DynWinRTValue | None]" - ); + let array = TypeMeta::Array(Box::new(TypeMeta::Object)); + for surface in [AnnotationSurface::Runtime, AnnotationSurface::Stub] { + assert_eq!( + returned(&array, surface, &PythonProjectionContext::default()), + "list[DynWinRTValue | None]" + ); + } } #[test] @@ -705,7 +859,7 @@ mod tests { assert_eq!(py_param_type_safe(&typ, &context), expected); } assert_eq!( - py_return_type_safe(Some(&TypeMeta::Object), &context), + returned(&TypeMeta::Object, AnnotationSurface::Stub, &context), "DynWinRTValue | None" ); assert_eq!( @@ -739,17 +893,21 @@ mod tests { "DynWinRTValue | _DynWinRTObject_2 | None" ); assert_eq!( - py_return_type_safe(Some(&TypeMeta::Object), &context), + returned(&TypeMeta::Object, AnnotationSurface::Stub, &context), "DynWinRTValue | None" ); assert_eq!( - py_array_return_type(&TypeMeta::Object, &context), + returned( + &TypeMeta::Array(Box::new(TypeMeta::Object)), + AnnotationSurface::Stub, + &context + ), "list[DynWinRTValue | None]" ); } #[test] - fn reference_returns_are_annotated_as_nullable() { + fn runtime_reference_outputs_stay_nullable() { let runtime_class = TypeMeta::RuntimeClass { namespace: "Contoso".into(), name: "Widget".into(), @@ -765,23 +923,26 @@ mod tests { interface.type_identity(), ]) .unwrap(); + let runtime = AnnotationSurface::Runtime; + assert_eq!(returned(&runtime_class, runtime, &context), "Widget | None"); + assert_eq!(returned(&interface, runtime, &context), "IWidget | None"); assert_eq!( - py_return_type_safe(Some(&runtime_class), &context), - "Widget | None" - ); - assert_eq!( - py_return_type_safe(Some(&interface), &context), - "IWidget | None" - ); - assert_eq!( - py_return_type_safe(Some(&TypeMeta::Object), &context), + returned(&TypeMeta::Object, runtime, &context), "DynWinRTValue | None" ); assert_eq!( - py_array_return_type(&runtime_class, &context), + returned( + &TypeMeta::Array(Box::new(runtime_class.clone())), + runtime, + &context + ), "list[Widget | None]" ); + assert_eq!( + py_return_type_safe(Some(&runtime_class), &context), + "Widget | None" + ); } #[test] @@ -815,10 +976,12 @@ mod tests { args: vec![TypeMeta::U32], }; - assert_eq!( - py_return_type_safe(Some(&reference), &PythonProjectionContext::default()), - "int | None" - ); + for surface in [AnnotationSurface::Runtime, AnnotationSurface::Stub] { + assert_eq!( + returned(&reference, surface, &PythonProjectionContext::default()), + "int | None" + ); + } assert_eq!( py_param_type_safe(&reference, &PythonProjectionContext::default()), "int | None | IReference_UInt32" From 357e5f5fa46bb9058901db0f1a5a966fa498350b Mon Sep 17 00:00:00 2001 From: Leilei Zhang Date: Thu, 24 Sep 2026 13:21:42 +0800 Subject: [PATCH 2/5] Type Python stub outputs as non-null by default WinRT metadata has no nullability and most APIs raise instead of returning null, so the .pyi stubs now type received values as non-null by default, like the JavaScript declarations: method and property results, async results, out values, and returned collections with their elements, keys and values. The stub arm of output_admits_none keeps `| None` for IReference values everywhere, for the Return/OutParam/AsyncResult/activation results of members whose CLR name is Try followed by an uppercase letter, for Object values, and for delegate-typed values outside callback parameters. Runtime .py annotations stay pessimistic, and inputs, struct fields, implementation protocols and callback parameters are unchanged. On a 40-namespace Windows.winmd corpus every .py file is byte-identical and 40,947 stub annotations lose `| None`, all at output positions. A natural consumer script goes from 30 pyright / 29 mypy errors to 0. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- bindings/py/README.md | 25 +++ tests/e2e/typecheck/python_generated_api.py | 20 ++- .../src/codegen/winrt/python/nullability.rs | 144 +++++++++++++++- .../src/codegen/winrt/python/stub_helpers.rs | 9 +- .../src/codegen/winrt/python/type_helpers.rs | 130 ++++++++++++++ .../tests/implementation_naming_test.rs | 8 +- .../tests/python_constructor_boundary_test.rs | 2 +- .../tests/python_consumer_typing_test.rs | 93 +++++++++- .../tests/python_inheritance_typing_test.rs | 2 +- .../tests/python_stub_nullability_test.rs | 163 ++++++++++++++++++ .../tests/python_symbol_mapping_test.rs | 38 ++-- ..._iterator_i_www_form_url_decoder_entry.pyi | 10 +- .../tests/snapshots/uri_pyi/uri.pyi | 4 +- .../uri_pyi/www_form_url_decoder.pyi | 38 ++-- 14 files changed, 620 insertions(+), 66 deletions(-) create mode 100644 tools/dynwinrt-codegen/tests/python_stub_nullability_test.rs diff --git a/bindings/py/README.md b/bindings/py/README.md index 2a1cb72c..a079c2fc 100644 --- a/bindings/py/README.md +++ b/bindings/py/README.md @@ -19,6 +19,31 @@ Generated package manifests pin `dynwinrt` to the exact version of Generated `IReference` values are projected as `T | None`; native values, `None`, and generated `IReference_*` wrappers are accepted as inputs. +### Nullability in type stubs + +WinRT metadata does not record which values can be null, and most APIs raise +an exception instead of returning null. The generated `.pyi` stubs therefore +type the values you receive as non-null by default: method and property +results, async results, out values, and the elements, keys and values of +returned collections. For example, `StorageFolder.create_file_async()` returns +`WinRTCoroutine[StorageFile]` and `get_files_async()` returns +`WinRTCoroutine[Sequence[StorageFile]]`. + +These values keep `| None`: + +- `IReference` values, projected as `T | None` everywhere; +- results of `Try*` members, such as `try_get_item_async()` or + `JsonObject.try_parse()`, where null means "not found"; +- `Object`/`IInspectable` values (`DynWinRTValue | None`) and delegate-typed + values, which are often null. + +Arguments keep accepting `None` where they did before. The stubs are +optimistic, like the generated TypeScript declarations: the runtime still +returns `None` when a WinRT API returns null, so check the API documentation +when a result can legitimately be absent. The inline annotations of the +generated `.py` modules, which `typing.get_type_hints()` and `--no-pyi` output +expose, still mark every object result `| None`. + ## Async WinRT operations Generated async methods return typed, asyncio-compatible operation objects: diff --git a/tests/e2e/typecheck/python_generated_api.py b/tests/e2e/typecheck/python_generated_api.py index 3902d0c9..6230b2e6 100644 --- a/tests/e2e/typecheck/python_generated_api.py +++ b/tests/e2e/typecheck/python_generated_api.py @@ -3,7 +3,7 @@ import asyncio from collections.abc import Coroutine, Generator, Sequence -from typing import Any, Awaitable, List, Tuple +from typing import Any, Awaitable, List, Tuple, assert_type from dynwinrt import ( DynWinRTArray, @@ -22,7 +22,9 @@ IWwwFormUrlDecoderEntry, Uri, ) +from python_bindings.windows.foundation.collections import ValueSet from python_bindings.windows.globalization import Calendar +from python_bindings.windows.storage import IStorageItem, StorageFile, StorageFolder from python_bindings.windows.storage.streams import ( Buffer as WinRTBuffer, DataWriter, @@ -69,8 +71,9 @@ def check_uri() -> None: uri: Uri = Uri("https://example.com") relative: Uri = Uri("https://example.com/root/", "child") host: str = uri.host - combined: Uri | None = uri.combine_uri("child") - _: Tuple[str, Uri, Uri | None] = (host, relative, combined) + combined: Uri = uri.combine_uri("child") + absolute: str = combined.absolute_uri + _: Tuple[str, Uri, Uri, str] = (host, relative, combined, absolute) def check_nullable_value( @@ -86,8 +89,7 @@ def check_nullable_value( def check_string_vector(calendar: Calendar) -> None: - languages: Sequence[str] | None = calendar.languages - assert languages is not None + languages: Sequence[str] = calendar.languages first: str = languages[0] located: int = languages.index(first) many: List[str] = list(languages[:4]) @@ -152,3 +154,11 @@ def check_ibuffer_bytes() -> None: interface_bytes: bytes = interface_buffer.to_bytes() runtime_bytes: bytes = runtime_buffer.to_bytes() _: Tuple[bytes, bytes] = (interface_bytes, runtime_bytes) + + +async def check_output_nullability(folder: StorageFolder, values: ValueSet) -> None: + created: StorageFile = await folder.create_file_async("notes.txt") + names: List[str] = [item.name for item in await folder.get_files_async()] + assert_type(folder.try_get_item_async("notes.txt"), WinRTCoroutine[IStorageItem | None]) + assert_type(values["key"], DynWinRTValue | None) + _: Tuple[StorageFile, List[str]] = (created, names) diff --git a/tools/dynwinrt-codegen/src/codegen/winrt/python/nullability.rs b/tools/dynwinrt-codegen/src/codegen/winrt/python/nullability.rs index 7b5e1536..7077b9a0 100644 --- a/tools/dynwinrt-codegen/src/codegen/winrt/python/nullability.rs +++ b/tools/dynwinrt-codegen/src/codegen/winrt/python/nullability.rs @@ -113,7 +113,7 @@ pub(crate) fn output_admits_none( typ: &TypeMeta, site: OutputSite, surface: AnnotationSurface, - _context: &PythonProjectionContext, + context: &PythonProjectionContext, ) -> bool { if ireference_inner_type(typ).is_some() { return true; @@ -121,13 +121,36 @@ pub(crate) fn output_admits_none( if !may_project_none(typ) { return false; } - match (surface, site.position) { - (_, OutputPosition::Activation) => false, - (AnnotationSurface::Runtime, _) => true, - (AnnotationSurface::Stub, _) => true, + match surface { + // Runtime annotations stay pessimistic: `typing.get_type_hints()` and + // `--no-pyi` consumers see every reference output as optional. + AnnotationSurface::Runtime => site.position != OutputPosition::Activation, + AnnotationSurface::Stub => stub_output_admits_none(typ, site, context), } } +/// Stubs are optimistic, like the JavaScript declarations: WinRT metadata has +/// no nullability, and most APIs raise instead of returning null. +fn stub_output_admits_none( + typ: &TypeMeta, + site: OutputSite, + context: &PythonProjectionContext, +) -> bool { + use OutputPosition::{Activation, AsyncResult, CallbackParam, OutParam, Return}; + + // `Object` positions are frequently null, e.g. the arguments of a + // `TypedEventHandler`. + if matches!(typ, TypeMeta::Object) { + return true; + } + // Delegate-typed values are raw handles that are null while unset. + if context.is_delegate_type(typ) { + return site.position != CallbackParam; + } + // `Try*` members report "not found" through a null result. + site.try_method && matches!(site.position, Return | OutParam | AsyncResult | Activation) +} + #[cfg(test)] mod tests { use super::*; @@ -161,4 +184,115 @@ mod tests { } ); } + + const POSITIONS: [OutputPosition; 8] = [ + OutputPosition::Return, + OutputPosition::OutParam, + OutputPosition::Property, + OutputPosition::AsyncResult, + OutputPosition::AsyncProgress, + OutputPosition::CollectionElement, + OutputPosition::CallbackParam, + OutputPosition::Activation, + ]; + + fn widget() -> TypeMeta { + TypeMeta::RuntimeClass { + namespace: "Contoso".into(), + name: "Widget".into(), + default_interface: None, + } + } + + fn handler() -> TypeMeta { + TypeMeta::Delegate { + namespace: "Contoso".into(), + name: "Handler".into(), + iid: "11111111-1111-1111-1111-111111111111".into(), + } + } + + fn nullable_u32() -> TypeMeta { + TypeMeta::Parameterized { + namespace: "Windows.Foundation".into(), + name: "IReference`1".into(), + piid: "61c17706-2d65-11e0-9ae8-d48564015472".into(), + args: vec![TypeMeta::U32], + } + } + + fn admits(typ: &TypeMeta, site: OutputSite, surface: AnnotationSurface) -> bool { + output_admits_none(typ, site, surface, &PythonProjectionContext::default()) + } + + #[test] + fn runtime_annotations_stay_pessimistic() { + for position in POSITIONS { + let site = OutputSite::of(position); + let expected = position != OutputPosition::Activation; + for typ in [widget(), handler(), TypeMeta::Object] { + assert_eq!( + admits(&typ, site, AnnotationSurface::Runtime), + expected, + "{typ:?} at {position:?}" + ); + } + assert!(admits(&nullable_u32(), site, AnnotationSurface::Runtime)); + assert!(!admits(&TypeMeta::String, site, AnnotationSurface::Runtime)); + } + } + + #[test] + fn stubs_are_non_null_by_default() { + for position in POSITIONS { + let site = OutputSite::of(position); + assert!( + !admits(&widget(), site, AnnotationSurface::Stub), + "{position:?}" + ); + assert!(!admits(&TypeMeta::I32, site, AnnotationSurface::Stub)); + } + } + + #[test] + fn stub_exceptions_keep_none() { + for position in POSITIONS { + let site = OutputSite::of(position); + assert!(admits(&nullable_u32(), site, AnnotationSurface::Stub)); + assert!(admits(&TypeMeta::Object, site, AnnotationSurface::Stub)); + assert_eq!( + admits(&handler(), site, AnnotationSurface::Stub), + position != OutputPosition::CallbackParam, + "{position:?}" + ); + } + } + + #[test] + fn try_members_keep_none_on_their_results_only() { + let try_get = method("TryGetItemAsync"); + for position in POSITIONS { + let expected = matches!( + position, + OutputPosition::Return + | OutputPosition::OutParam + | OutputPosition::AsyncResult + | OutputPosition::Activation + ); + assert_eq!( + admits( + &widget(), + OutputSite::for_method(&try_get, position), + AnnotationSurface::Stub + ), + expected, + "{position:?}" + ); + } + assert!(!admits( + &TypeMeta::Bool, + OutputSite::for_method(&try_get, OutputPosition::Return), + AnnotationSurface::Stub + )); + } } diff --git a/tools/dynwinrt-codegen/src/codegen/winrt/python/stub_helpers.rs b/tools/dynwinrt-codegen/src/codegen/winrt/python/stub_helpers.rs index b1eaee21..853b36d5 100644 --- a/tools/dynwinrt-codegen/src/codegen/winrt/python/stub_helpers.rs +++ b/tools/dynwinrt-codegen/src/codegen/winrt/python/stub_helpers.rs @@ -328,10 +328,11 @@ pub(super) fn emit_method_stub_named( } else { format!("self, {}", py_params) }; - // WinRT vectors may reject null on mutation while returning null - // interface elements. That asymmetric native contract cannot satisfy - // MutableSequence[T | None]'s append signature exactly. Empty - // structural protocols can make mypy consider the override compatible. + // `append` takes the projected input annotation, which can differ from + // the element annotation of the MutableSequence base: WinRT vectors + // may reject null on mutation, while `Object` elements are read back + // as `DynWinRTValue | None`. Empty structural protocols can make mypy + // consider the override compatible. let override_ignore = if overrides_mutable_sequence && method_name == "append" && in_params.first().is_some_and(|param| { diff --git a/tools/dynwinrt-codegen/src/codegen/winrt/python/type_helpers.rs b/tools/dynwinrt-codegen/src/codegen/winrt/python/type_helpers.rs index 4d20ab69..1b75501a 100644 --- a/tools/dynwinrt-codegen/src/codegen/winrt/python/type_helpers.rs +++ b/tools/dynwinrt-codegen/src/codegen/winrt/python/type_helpers.rs @@ -945,6 +945,136 @@ mod tests { ); } + #[test] + fn stub_outputs_are_non_null_except_policy_exceptions() { + let widget = TypeMeta::RuntimeClass { + namespace: "Contoso".into(), + name: "Widget".into(), + default_interface: None, + }; + let interface = TypeMeta::Interface { + namespace: "Contoso".into(), + name: "IWidget".into(), + iid: "11111111-1111-1111-1111-111111111111".into(), + }; + let unknown = TypeMeta::RuntimeClass { + namespace: "Contoso".into(), + name: "NotGenerated".into(), + default_interface: None, + }; + let context = PythonProjectionContext::standalone([ + widget.type_identity(), + interface.type_identity(), + ]) + .unwrap(); + let widgets = TypeMeta::Parameterized { + namespace: "Windows.Foundation.Collections".into(), + name: "IVectorView`1".into(), + piid: crate::codegen::winrt::python::collections::IVECTOR_VIEW_PIID.into(), + args: vec![widget.clone()], + }; + let stub = AnnotationSurface::Stub; + let async_of = |typ: &TypeMeta| TypeMeta::AsyncOperation(Box::new(typ.clone())); + let method = |raw_name: &str, params: Vec, return_type: TypeMeta| MethodMeta { + name: raw_name.into(), + raw_name: raw_name.into(), + params, + return_type: Some(return_type), + ..Default::default() + }; + + assert_eq!(returned(&widget, stub, &context), "Widget"); + assert_eq!(returned(&interface, stub, &context), "IWidget"); + assert_eq!(returned(&unknown, stub, &context), "DynWinRTValue"); + assert_eq!( + returned(&TypeMeta::Array(Box::new(widget.clone())), stub, &context), + "list[Widget]" + ); + assert_eq!( + returned(&async_of(&widget), stub, &context), + "WinRTCoroutine[Widget]" + ); + assert_eq!( + returned(&async_of(&widgets), stub, &context), + "WinRTCoroutine[Sequence[Widget]]" + ); + assert_eq!( + returned(&TypeMeta::Object, stub, &context), + "DynWinRTValue | None" + ); + assert_eq!(py_property_type(&widget, stub, &context), "Widget"); + assert_eq!(py_collection_item_type(&widget, stub, &context), "Widget"); + assert_eq!( + py_collection_base_type("Sequence", std::slice::from_ref(&widget), stub, &context), + Some("Sequence[Widget]".to_string()) + ); + + let get_item = method("GetItemAsync", vec![], async_of(&widget)); + let try_get_item = method("TryGetItemAsync", vec![], async_of(&widget)); + let try_get_items = method("TryGetItemsAsync", vec![], async_of(&widgets)); + let try_parse = method( + "TryParse", + vec![ + ParamMeta { + name: "input".into(), + typ: TypeMeta::String, + direction: ParamDirection::In, + }, + ParamMeta { + name: "result".into(), + typ: widget.clone(), + direction: ParamDirection::Out, + }, + ], + TypeMeta::Bool, + ); + for (method, stub_type, runtime_type) in [ + ( + &get_item, + "WinRTCoroutine[Widget]", + "WinRTCoroutine[Widget | None]", + ), + ( + &try_get_item, + "WinRTCoroutine[Widget | None]", + "WinRTCoroutine[Widget | None]", + ), + ( + &try_get_items, + "WinRTCoroutine[Sequence[Widget] | None]", + "WinRTCoroutine[Sequence[Widget | None] | None]", + ), + ( + &try_parse, + "tuple[Widget | None, bool]", + "tuple[Widget | None, bool]", + ), + ] { + assert_eq!(py_method_return_type(method, stub, &context), stub_type); + assert_eq!( + py_method_return_type(method, AnnotationSurface::Runtime, &context), + runtime_type + ); + } + + let create = method("CreateWidget", vec![], widget.clone()); + let try_create = method("TryCreateWidget", vec![], widget.clone()); + for surface in [AnnotationSurface::Runtime, stub] { + assert_eq!( + py_factory_return_type("Widget", &create, surface, &context), + "'Widget'" + ); + } + assert_eq!( + py_factory_return_type("Widget", &try_create, stub, &context), + "Widget | None" + ); + assert_eq!( + py_factory_return_type("Widget", &try_create, AnnotationSurface::Runtime, &context), + "'Widget'" + ); + } + #[test] fn delegate_inputs_accept_callables_and_runtime_values() { let param = ParamMeta { diff --git a/tools/dynwinrt-codegen/tests/implementation_naming_test.rs b/tools/dynwinrt-codegen/tests/implementation_naming_test.rs index 16bd74e5..02a888a9 100644 --- a/tools/dynwinrt-codegen/tests/implementation_naming_test.rs +++ b/tools/dynwinrt-codegen/tests/implementation_naming_test.rs @@ -1116,10 +1116,10 @@ fn python_full_identity_collision_imports_named_peer_not_generic_self() { from pyviews.{peer_module} import {peer_name} as Peer\n\ from pyviews.{foreign_module} import {foreign_name} as Foreign\n\ def check(box: Box, peer: Peer, foreign: Foreign) -> None:\n\ - \x20 assert_type(box.echo_self(box), Box | None)\n\ - \x20 assert_type(box.echo_peer(peer), Peer | None)\n\ - \x20 assert_type(box.echo_foreign(foreign), Foreign | None)\n\ - \x20 assert_type(peer.echo_self(peer), Peer | None)\n" + \x20 assert_type(box.echo_self(box), Box)\n\ + \x20 assert_type(box.echo_peer(peer), Peer)\n\ + \x20 assert_type(box.echo_foreign(foreign), Foreign)\n\ + \x20 assert_type(peer.echo_self(peer), Peer)\n" ), ); typecheck_py_package(&fixture.0); diff --git a/tools/dynwinrt-codegen/tests/python_constructor_boundary_test.rs b/tools/dynwinrt-codegen/tests/python_constructor_boundary_test.rs index 9b73889b..65ee2ac7 100644 --- a/tools/dynwinrt-codegen/tests/python_constructor_boundary_test.rs +++ b/tools/dynwinrt-codegen/tests/python_constructor_boundary_test.rs @@ -72,7 +72,7 @@ fn system_returned_class_keeps_only_internal_native_wrapping() { assert!(!py.contains("self._set_native(type(self).create(")); assert!(!py.contains("_IActivationFactory =")); assert!(pyi.contains("def __init__(self, _not_constructible: NoReturn) -> None: ...")); - assert!(pyi.contains("def get_current() -> SystemResult | None: ...")); + assert!(pyi.contains("def get_current() -> SystemResult: ...")); assert!(!pyi.contains("def from_value("), "{pyi}"); assert!(!pyi.contains("def __init__(self, obj: DynWinRTValue)")); assert!(!pyi.contains("def __init__(self)")); diff --git a/tools/dynwinrt-codegen/tests/python_consumer_typing_test.rs b/tools/dynwinrt-codegen/tests/python_consumer_typing_test.rs index 622a464b..04191b2c 100644 --- a/tools/dynwinrt-codegen/tests/python_consumer_typing_test.rs +++ b/tools/dynwinrt-codegen/tests/python_consumer_typing_test.rs @@ -421,7 +421,7 @@ def collections(resource: Resource, derived: OtherDerived, raw: DynWinRTValue, mapping[resource] = derived assert_type(vector[0], DynWinRTValue | None) assert_type(vector[:], list[DynWinRTValue | None]) - assert_type(resources[0], Resource | None) + assert_type(resources[0], Resource) assert_type(mapping[resource], DynWinRTValue | None) del mapping[resource] "#, @@ -813,6 +813,97 @@ print("collection-subscript-native-ok", flush=True) } } +#[test] +fn natural_sdk_consumers_need_no_none_guards() { + let winmd = Path::new( + r"C:\Program Files (x86)\Windows Kits\10\UnionMetadata\10.0.26100.0\Windows.winmd", + ); + if !winmd.is_file() || !has_mypy() { + eprintln!("Skipping natural SDK consumers: Windows.winmd or mypy unavailable."); + return; + } + let fixture = Fixture::new(); + let output = Command::new(env!("CARGO_BIN_EXE_dynwinrt-codegen")) + .args(["generate", "--winmd"]) + .arg(winmd) + .args([ + "--class-name", + "Windows.Foundation.Uri,Windows.Foundation.Collections.PropertySet,\ + Windows.Data.Json.JsonObject,Windows.Globalization.Calendar,\ + Windows.Security.Cryptography.CryptographicBuffer,\ + Windows.Security.Cryptography.Core.HashAlgorithmProvider,\ + Windows.Storage.StorageFolder,Windows.Storage.FileIO,\ + Windows.Storage.Streams.DataReader,Windows.Storage.Streams.DataWriter,\ + Windows.Storage.Streams.InMemoryRandomAccessStream", + "--lang", + "py", + "--output", + ]) + .arg(fixture.0.join("sdk")) + .output() + .unwrap(); + assert!(output.status.success(), "{}", diagnostics(&output)); + let imports = r#"from collections.abc import Sequence +from typing import assert_type +from dynwinrt import DynWinRTValue, WinRTCoroutine +from sdk.windows.data.json import JsonObject +from sdk.windows.foundation import Uri +from sdk.windows.foundation.collections import PropertySet +from sdk.windows.globalization import Calendar +from sdk.windows.security.cryptography import CryptographicBuffer +from sdk.windows.security.cryptography.core import HashAlgorithmProvider +from sdk.windows.storage import CreationCollisionOption, FileIO, IStorageItem, StorageFile, StorageFolder +from sdk.windows.storage.streams import DataReader, DataWriter, InMemoryRandomAccessStream +"#; + typecheck( + &fixture, + &["sdk"], + &format!( + r#"{imports} +def uri_demo() -> str: + uri = Uri("https://example.com/a/b?x=1&y=two") + query = {{entry.name: entry.value for entry in uri.query_parsed}} + return uri.combine_uri("c/d").absolute_uri + str(query) + +def json_demo() -> list[str]: + parsed = JsonObject.parse('{{"tags": ["a", "b"]}}') + assert_type(JsonObject.try_parse("{{}}"), tuple[JsonObject | None, bool]) + return [value.get_string() for value in parsed.get_named_array("tags")] + +def calendar_demo(calendar: Calendar) -> str: + languages: Sequence[str] = calendar.languages + return languages[0] + +def crypto_demo(data: bytes) -> str: + buffer = CryptographicBuffer.create_from_byte_array(data) + digest = HashAlgorithmProvider.open_algorithm("SHA256").hash_data(buffer) + return CryptographicBuffer.encode_to_hex_string(digest) + +def object_values(properties: PropertySet) -> DynWinRTValue | None: + assert_type(properties["count"], DynWinRTValue | None) + return properties.lookup("count") + +async def streams_demo() -> str: + stream = InMemoryRandomAccessStream() + writer = DataWriter(stream.get_output_stream_at(0)) + writer.write_string("streamed text") + written = await writer.store_async() + reader = DataReader(stream.get_input_stream_at(0)) + return reader.read_string(await reader.load_async(written)) + +async def storage_demo(path: str) -> list[str]: + folder = await StorageFolder.get_folder_from_path_async(path) + file = await folder.create_file_async("notes.txt", CreationCollisionOption.ReplaceExisting) + await FileIO.write_text_async(file, "first line") + assert_type(folder.create_file_async("a.txt"), WinRTCoroutine[StorageFile]) + assert_type(folder.try_get_item_async("notes.txt"), WinRTCoroutine[IStorageItem | None]) + return [item.name for item in await folder.get_files_async()] +"# + ), + &[], + ); +} + #[test] fn native_object_inputs_keep_projection_factories_and_context_lifetimes() { if !has_implementation_runtime() { diff --git a/tools/dynwinrt-codegen/tests/python_inheritance_typing_test.rs b/tools/dynwinrt-codegen/tests/python_inheritance_typing_test.rs index bdf317d0..2bd3505d 100644 --- a/tools/dynwinrt-codegen/tests/python_inheritance_typing_test.rs +++ b/tools/dynwinrt-codegen/tests/python_inheritance_typing_test.rs @@ -135,7 +135,7 @@ fn stubs_model_runtime_class_and_interface_bases_without_runtime_inheritance() { "{class_stub}" ); assert!( - class_stub.contains("def use_base(self, value: 'BaseLike') -> Base | None:"), + class_stub.contains("def use_base(self, value: 'BaseLike') -> Base:"), "{class_stub}" ); assert!( diff --git a/tools/dynwinrt-codegen/tests/python_stub_nullability_test.rs b/tools/dynwinrt-codegen/tests/python_stub_nullability_test.rs new file mode 100644 index 00000000..552988fe --- /dev/null +++ b/tools/dynwinrt-codegen/tests/python_stub_nullability_test.rs @@ -0,0 +1,163 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +//! The stub nullability policy on real Windows SDK metadata. Values received +//! from the projection are non-null in `.pyi` stubs by default, while +//! `IReference`, `Try*` results, `Object` and delegate values keep +//! `| None`. Inputs, implementation protocols and the runtime `.py` +//! annotations are unchanged. + +use std::fs; +use std::path::{Path, PathBuf}; +use std::process::Command; + +const WINDOWS_WINMD: &str = + r"C:\Program Files (x86)\Windows Kits\10\UnionMetadata\10.0.26100.0\Windows.winmd"; + +struct Generated(PathBuf); + +impl Drop for Generated { + fn drop(&mut self) { + let _ = fs::remove_dir_all(&self.0); + } +} + +impl Generated { + fn new(classes: &str) -> Option { + if !Path::new(WINDOWS_WINMD).is_file() { + eprintln!("Skipping: Windows.winmd not found"); + return None; + } + let root = Path::new(env!("CARGO_MANIFEST_DIR")) + .parent() + .unwrap() + .parent() + .unwrap() + .join("target") + .join(format!("pn{}", std::process::id())); + let _ = fs::remove_dir_all(&root); + let output = Command::new(env!("CARGO_BIN_EXE_dynwinrt-codegen")) + .args([ + "generate", + "--winmd", + WINDOWS_WINMD, + "--class-name", + classes, + ]) + .args(["--lang", "py", "--output"]) + .arg(&root) + .output() + .unwrap(); + assert!( + output.status.success(), + "{}\n{}", + String::from_utf8_lossy(&output.stdout), + String::from_utf8_lossy(&output.stderr) + ); + Some(Self(root)) + } + + fn module(&self, name: &str) -> String { + fs::read_to_string(self.0.join(name)).unwrap_or_else(|error| panic!("{name}: {error}")) + } +} + +fn assert_contains(text: &str, expected: &str) { + assert!(text.contains(expected), "missing `{expected}`"); +} + +#[test] +fn stub_outputs_follow_the_nullability_policy() { + let Some(generated) = Generated::new( + "Windows.Storage.StorageFolder,Windows.Web.Http.Headers.HttpContentHeaderCollection,\ + Windows.Foundation.Collections.PropertySet,Windows.Data.Json.JsonObject", + ) else { + return; + }; + let folder = generated.module("windows__storage__storage_folder.pyi"); + let folder_py = generated.module("windows__storage__storage_folder.py"); + let folder_view = generated.module("windows__storage__i_storage_folder.pyi"); + let headers = + generated.module("windows__web__http__headers__http_content_header_collection.pyi"); + let properties = generated.module("windows__foundation__collections__property_set.pyi"); + let json = generated.module("windows__data__json__json_object.pyi"); + + // Method, async, collection and property outputs are non-null by default. + assert_contains( + &folder, + "def create_file_async(self, desired_name: str) -> WinRTCoroutine[StorageFile]: ...", + ); + assert_contains( + &folder, + "def get_files_async(self) -> WinRTCoroutine[Sequence[StorageFile]]: ...", + ); + assert_contains( + &folder, + "def get_folder_from_path_async(path: str) -> WinRTCoroutine[StorageFolder]: ...", + ); + assert_contains( + &folder, + "def properties(self) -> StorageItemContentProperties: ...", + ); + assert_contains( + &json, + "def get_named_object(self, name: str) -> JsonObject: ...", + ); + assert_contains(&json, "def parse(input: str) -> JsonObject: ..."); + + // Try* results keep None on their result, async result and out values. + assert_contains( + &folder, + "def try_get_item_async(self, name: str) -> WinRTCoroutine[IStorageItem | None]: ...", + ); + assert_contains( + &json, + "def try_parse(input: str) -> tuple[JsonObject | None, bool]: ...", + ); + + // IReference values and Object values keep None. + assert_contains(&headers, "def content_length(self) -> int | None: ..."); + assert_contains( + &headers, + "def content_type(self) -> HttpMediaTypeHeaderValue: ...", + ); + assert_contains( + &properties, + "def lookup(self, key: str) -> DynWinRTValue | None: ...", + ); + assert_contains( + &properties, + "def __getitem__(self, key: str) -> DynWinRTValue | None: ...", + ); + + // Inputs are unchanged. + assert_contains( + &headers, + "def content_length(self, value: int | None | IReference_UInt64) -> None: ...", + ); + assert_contains( + &folder, + "def create_folder_query(self, query_options: 'QueryOptionsLike') -> StorageFolderQueryResult: ...", + ); + assert_contains( + &properties, + "def __setitem__(self, key: str, value: DynWinRTValue | _DynWinRTObject | None) -> None: ...", + ); + + // Implementation protocols keep their obligations. + assert_contains(&folder_view, "class IStorageFolderHandlers(Protocol):"); + assert_contains( + &folder_view, + "def create_file_async_overload_default_options(self, desired_name: str) -> DynWinRTValue | None: ...", + ); + + // Runtime annotations stay pessimistic. + assert_contains( + &folder_py, + "def get_folder_from_path_async(path: str) -> WinRTCoroutine[StorageFolder | None]:", + ); + assert_contains( + &folder_py, + "def properties(self) -> StorageItemContentProperties | None:", + ); +} diff --git a/tools/dynwinrt-codegen/tests/python_symbol_mapping_test.rs b/tools/dynwinrt-codegen/tests/python_symbol_mapping_test.rs index 739bda9b..01fbc7d6 100644 --- a/tools/dynwinrt-codegen/tests/python_symbol_mapping_test.rs +++ b/tools/dynwinrt-codegen/tests/python_symbol_mapping_test.rs @@ -331,7 +331,7 @@ fn python_class_self_and_base_markers_use_declarations_in_both_layouts() { "fixture must require a local self binding" ); assert!( - stub.contains("def echo_self(self, value: 'WidgetLike') -> Widget | None:"), + stub.contains("def echo_self(self, value: 'WidgetLike') -> Widget:"), "{stub}" ); assert!( @@ -375,13 +375,13 @@ fn python_class_self_and_base_markers_use_declarations_in_both_layouts() { def alpha(value: AlphaWidget, peer: BetaWidget, child: AlphaDerived) -> None: own: AlphaBaseLike = value foreign: BetaBaseLike = child - assert_type(value.echo_self(value), AlphaWidget | None) - assert_type(value.echo_foreign(peer), BetaWidget | None) + assert_type(value.echo_self(value), AlphaWidget) + assert_type(value.echo_foreign(peer), BetaWidget) def beta(value: BetaWidget, peer: AlphaWidget, child: BetaDerived) -> None: own: BetaBaseLike = value foreign: AlphaBaseLike = child - assert_type(value.echo_self(value), BetaWidget | None) - assert_type(value.echo_foreign(peer), AlphaWidget | None) + assert_type(value.echo_self(value), BetaWidget) + assert_type(value.echo_foreign(peer), AlphaWidget) "#, ); support(&package, &[]); @@ -587,13 +587,13 @@ fn python_named_class_closed_generic_collision_uses_allocated_declaration() { from typing import assert_type assert_type(Named(), Named) assert_type(Named(7), Named) -assert_type(Named.get_current(), Named | None) +assert_type(Named.get_current(), Named) def check(named: Named, generic: Bucket, derived: Derived) -> None: base: NamedLike = derived - assert_type(named.echo_self(named), Named | None) - assert_type(named.echo_generic(generic), Bucket | None) - assert_type(generic.echo_self(generic), Bucket | None) - assert_type(generic.echo_named(named), Named | None) + assert_type(named.echo_self(named), Named) + assert_type(named.echo_generic(generic), Bucket) + assert_type(generic.echo_self(generic), Bucket) + assert_type(generic.echo_named(named), Named) "#, ), ); @@ -2172,8 +2172,8 @@ fn python_cross_role_class_owners_keep_self_bindings_and_qualified_helpers() { r#"{imports} from typing import assert_type def check(value: Owner) -> None: - assert_type(value.echo_self(value), Owner | None) - assert_type(value.get_self(), Owner | None) + assert_type(value.echo_self(value), Owner) + assert_type(value.get_self(), Owner) assert_type(value.echo_payload(URLValue(17)), URLValue) "# ), @@ -2705,9 +2705,9 @@ fn python_enum_closed_generic_collision_preserves_projection_and_native_identity from typing import assert_type assert_type(RootKind(0), Kind) def check(local: IUse, foreign: IForeign) -> None: - assert_type(local.get_bucket(), Bucket | None) - assert_type(local.get_bucket(), RootBucket | None) - assert_type(foreign.get_bucket(), Bucket | None) + assert_type(local.get_bucket(), Bucket) + assert_type(local.get_bucket(), RootBucket) + assert_type(foreign.get_bucket(), Bucket) assert_type(local.echo_kind(Kind.Unknown), Kind) assert_type(foreign.echo_kind(Kind.Unknown), Kind) assert_type(local.echo_kinds([Kind.Unknown]), list[Kind]) @@ -2911,10 +2911,10 @@ fn python_class_companion_aliases_preserve_cli_and_standalone_contracts() { r#"{imports} from typing import assert_type def check(owner: Widget, peer: Peer, use: IUse) -> None: - assert_type(owner.echo(peer), Peer | None) - assert_type(peer.echo(owner), Widget | None) - assert_type(use.echo_owner(owner), Widget | None) - assert_type(use.echo_peer(peer), Peer | None) + assert_type(owner.echo(peer), Peer) + assert_type(peer.echo(owner), Widget) + assert_type(use.echo_owner(owner), Widget) + assert_type(use.echo_peer(peer), Peer) "#, ), ); diff --git a/tools/dynwinrt-codegen/tests/snapshots/uri_pyi/i_iterator_i_www_form_url_decoder_entry.pyi b/tools/dynwinrt-codegen/tests/snapshots/uri_pyi/i_iterator_i_www_form_url_decoder_entry.pyi index ae2e8fc3..4194cea1 100644 --- a/tools/dynwinrt-codegen/tests/snapshots/uri_pyi/i_iterator_i_www_form_url_decoder_entry.pyi +++ b/tools/dynwinrt-codegen/tests/snapshots/uri_pyi/i_iterator_i_www_form_url_decoder_entry.pyi @@ -19,25 +19,25 @@ IID_IIterator_IWwwFormUrlDecoderEntry: WinGUID class _IIterator_IWwwFormUrlDecoderEntryIdentity(Protocol): def _dynwinrt_iid_g2bb0b33cef11eb455ace3197fce2b56f216f6802e23b208c88ebad8f950f8628(self) -> None: ... -class IIterator_IWwwFormUrlDecoderEntry(_IIterator_IWwwFormUrlDecoderEntryIdentity, Iterator[IWwwFormUrlDecoderEntry | None]): +class IIterator_IWwwFormUrlDecoderEntry(_IIterator_IWwwFormUrlDecoderEntryIdentity, Iterator[IWwwFormUrlDecoderEntry]): @builtins.property def _obj(self) -> DynWinRTValue: ... # Windows.Foundation.Collections.IIterator_IWwwFormUrlDecoderEntry cannot be implemented: generic interface implementations are not supported def __init__(self, obj: DynWinRTValue) -> None: ... - def __iter__(self) -> Iterator[IWwwFormUrlDecoderEntry | None]: ... - def __next__(self) -> IWwwFormUrlDecoderEntry | None: ... + def __iter__(self) -> Iterator[IWwwFormUrlDecoderEntry]: ... + def __next__(self) -> IWwwFormUrlDecoderEntry: ... @classmethod def from_value(cls, obj: DynWinRTValue) -> Self: ... def as_interface(self, interface_class: _DynWinRTProjector[_InterfaceT]) -> _InterfaceT: ... @builtins.property - def current(self) -> IWwwFormUrlDecoderEntry | None: ... + def current(self) -> IWwwFormUrlDecoderEntry: ... @builtins.property def has_current(self) -> bool: ... def move_next(self) -> bool: ... - def get_many(self, items: DynWinRTArray | Sequence['IWwwFormUrlDecoderEntry']) -> list[IWwwFormUrlDecoderEntry | None]: ... + def get_many(self, items: DynWinRTArray | Sequence['IWwwFormUrlDecoderEntry']) -> list[IWwwFormUrlDecoderEntry]: ... diff --git a/tools/dynwinrt-codegen/tests/snapshots/uri_pyi/uri.pyi b/tools/dynwinrt-codegen/tests/snapshots/uri_pyi/uri.pyi index 815ffece..728dceb6 100644 --- a/tools/dynwinrt-codegen/tests/snapshots/uri_pyi/uri.pyi +++ b/tools/dynwinrt-codegen/tests/snapshots/uri_pyi/uri.pyi @@ -60,7 +60,7 @@ class UriLike(_UriIdentity, Protocol): def query(self) -> str: ... @builtins.property - def query_parsed(self) -> WwwFormUrlDecoder | None: ... + def query_parsed(self) -> WwwFormUrlDecoder: ... @builtins.property def raw_uri(self) -> str: ... @@ -79,7 +79,7 @@ class UriLike(_UriIdentity, Protocol): def equals(self, p_uri: 'UriLike') -> bool: ... - def combine_uri(self, relative_uri: str) -> Uri | None: ... + def combine_uri(self, relative_uri: str) -> Uri: ... @builtins.property def absolute_canonical_uri(self) -> str: ... diff --git a/tools/dynwinrt-codegen/tests/snapshots/uri_pyi/www_form_url_decoder.pyi b/tools/dynwinrt-codegen/tests/snapshots/uri_pyi/www_form_url_decoder.pyi index 46688389..c223db0a 100644 --- a/tools/dynwinrt-codegen/tests/snapshots/uri_pyi/www_form_url_decoder.pyi +++ b/tools/dynwinrt-codegen/tests/snapshots/uri_pyi/www_form_url_decoder.pyi @@ -34,48 +34,48 @@ class WwwFormUrlDecoderLike(_WwwFormUrlDecoderIdentity, Protocol): def __len__(self) -> int: ... @overload - def __getitem__(self, index: int) -> IWwwFormUrlDecoderEntry | None: ... + def __getitem__(self, index: int) -> IWwwFormUrlDecoderEntry: ... @overload - def __getitem__(self, index: slice) -> list[IWwwFormUrlDecoderEntry | None]: ... + def __getitem__(self, index: slice) -> list[IWwwFormUrlDecoderEntry]: ... def get_first_value_by_name(self, name: str) -> str: ... @builtins.property def size(self) -> int: ... - def get_at(self, index: int) -> IWwwFormUrlDecoderEntry | None: ... + def get_at(self, index: int) -> IWwwFormUrlDecoderEntry: ... def index_of(self, value: 'IWwwFormUrlDecoderEntry') -> tuple[int, bool]: ... - def get_many(self, start_index: int, items: DynWinRTArray | Sequence['IWwwFormUrlDecoderEntry']) -> list[IWwwFormUrlDecoderEntry | None]: ... + def get_many(self, start_index: int, items: DynWinRTArray | Sequence['IWwwFormUrlDecoderEntry']) -> list[IWwwFormUrlDecoderEntry]: ... - def first(self) -> Iterator[IWwwFormUrlDecoderEntry | None] | None: ... + def first(self) -> Iterator[IWwwFormUrlDecoderEntry]: ... def as_interface(self, interface_class: _DynWinRTProjector[_InterfaceT]) -> _InterfaceT: ... -class WwwFormUrlDecoder(_WwwFormUrlDecoderIdentity, Sequence[IWwwFormUrlDecoderEntry | None], _DynWinRTRuntimeClass): +class WwwFormUrlDecoder(_WwwFormUrlDecoderIdentity, Sequence[IWwwFormUrlDecoderEntry], _DynWinRTRuntimeClass): def __init__(self, query: str) -> None: ... @builtins.property def _obj(self) -> DynWinRTValue: ... def __len__(self) -> int: ... @overload - def __getitem__(self, index: int) -> IWwwFormUrlDecoderEntry | None: ... + def __getitem__(self, index: int) -> IWwwFormUrlDecoderEntry: ... @overload - def __getitem__(self, index: slice) -> list[IWwwFormUrlDecoderEntry | None]: ... + def __getitem__(self, index: slice) -> list[IWwwFormUrlDecoderEntry]: ... def get_first_value_by_name(self, name: str) -> str: ... @builtins.property def size(self) -> int: ... - def get_at(self, index: int) -> IWwwFormUrlDecoderEntry | None: ... + def get_at(self, index: int) -> IWwwFormUrlDecoderEntry: ... def index_of(self, value: 'IWwwFormUrlDecoderEntry') -> tuple[int, bool]: ... - def get_many(self, start_index: int, items: DynWinRTArray | Sequence['IWwwFormUrlDecoderEntry']) -> list[IWwwFormUrlDecoderEntry | None]: ... + def get_many(self, start_index: int, items: DynWinRTArray | Sequence['IWwwFormUrlDecoderEntry']) -> list[IWwwFormUrlDecoderEntry]: ... - def first(self) -> Iterator[IWwwFormUrlDecoderEntry | None] | None: ... + def first(self) -> Iterator[IWwwFormUrlDecoderEntry]: ... def as_interface(self, interface_class: _DynWinRTProjector[_InterfaceT]) -> _InterfaceT: ... @@ -83,16 +83,16 @@ class WwwFormUrlDecoder(_WwwFormUrlDecoderIdentity, Sequence[IWwwFormUrlDecoderE def create_www_form_url_decoder(query: str) -> 'WwwFormUrlDecoder': ... -class IVectorView_IWwwFormUrlDecoderEntry(Sequence[IWwwFormUrlDecoderEntry | None]): +class IVectorView_IWwwFormUrlDecoderEntry(Sequence[IWwwFormUrlDecoderEntry]): def __init__(self, obj: DynWinRTValue) -> None: ... @builtins.property def _obj(self) -> DynWinRTValue: ... def __len__(self) -> int: ... @overload - def __getitem__(self, index: int) -> IWwwFormUrlDecoderEntry | None: ... + def __getitem__(self, index: int) -> IWwwFormUrlDecoderEntry: ... @overload - def __getitem__(self, index: slice) -> list[IWwwFormUrlDecoderEntry | None]: ... + def __getitem__(self, index: slice) -> list[IWwwFormUrlDecoderEntry]: ... @classmethod def from_value(cls, obj: DynWinRTValue) -> Self: ... @@ -101,22 +101,22 @@ class IVectorView_IWwwFormUrlDecoderEntry(Sequence[IWwwFormUrlDecoderEntry | Non @builtins.property def size(self) -> int: ... - def get_at(self, index: int) -> IWwwFormUrlDecoderEntry | None: ... + def get_at(self, index: int) -> IWwwFormUrlDecoderEntry: ... def index_of(self, value: 'IWwwFormUrlDecoderEntry') -> tuple[int, bool]: ... - def get_many(self, start_index: int, items: DynWinRTArray | Sequence['IWwwFormUrlDecoderEntry']) -> list[IWwwFormUrlDecoderEntry | None]: ... + def get_many(self, start_index: int, items: DynWinRTArray | Sequence['IWwwFormUrlDecoderEntry']) -> list[IWwwFormUrlDecoderEntry]: ... -class IIterable_IWwwFormUrlDecoderEntry(Iterable[IWwwFormUrlDecoderEntry | None]): +class IIterable_IWwwFormUrlDecoderEntry(Iterable[IWwwFormUrlDecoderEntry]): def __init__(self, obj: DynWinRTValue) -> None: ... @builtins.property def _obj(self) -> DynWinRTValue: ... - def __iter__(self) -> Iterator[IWwwFormUrlDecoderEntry | None]: ... + def __iter__(self) -> Iterator[IWwwFormUrlDecoderEntry]: ... @classmethod def from_value(cls, obj: DynWinRTValue) -> Self: ... def as_interface(self, interface_class: _DynWinRTProjector[_InterfaceT]) -> _InterfaceT: ... - def first(self) -> Iterator[IWwwFormUrlDecoderEntry | None] | None: ... + def first(self) -> Iterator[IWwwFormUrlDecoderEntry]: ... From 1125db1a90dac47af65c833e932c46528605abd9 Mon Sep 17 00:00:00 2001 From: Leilei Zhang Date: Thu, 24 Sep 2026 13:28:11 +0800 Subject: [PATCH 3/5] Drop None guards the new stub policy makes unnecessary in Python samples Remove the `if x is None: raise` guards in five stock-Windows samples where the values are now typed non-null, and keep the `Try*` guard in the OCR sample. With the previous stubs the unguarded samples produce 27 mypy --strict and 25 pyright errors; with the new stubs both report none, and every sample still runs. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- samples/python/async-file-io/app.py | 5 ----- samples/python/cryptography/app.py | 4 ---- samples/python/device-watcher/app.py | 2 -- samples/python/ocr-image/app.py | 11 +---------- samples/python/text-to-speech/app.py | 2 -- 5 files changed, 1 insertion(+), 23 deletions(-) diff --git a/samples/python/async-file-io/app.py b/samples/python/async-file-io/app.py index 6cd7111d..83f015a7 100644 --- a/samples/python/async-file-io/app.py +++ b/samples/python/async-file-io/app.py @@ -15,12 +15,7 @@ async def run() -> None: with tempfile.TemporaryDirectory(prefix="dynwinrt-python-") as directory: with RoApartment(1), projected_lifetime_scope(): folder = await StorageFolder.get_folder_from_path_async(directory) - if folder is None: - raise RuntimeError("StorageFolder returned no temporary folder") - file = await folder.create_file_async("sample.txt") - if file is None: - raise RuntimeError("StorageFolder returned no file") await FileIO.write_text_async(file, "Hello from dynwinrt.") await FileIO.append_text_async( file, diff --git a/samples/python/cryptography/app.py b/samples/python/cryptography/app.py index e0a7c5c3..fbae2298 100644 --- a/samples/python/cryptography/app.py +++ b/samples/python/cryptography/app.py @@ -9,12 +9,8 @@ def sha256(text: str) -> str: with RoApartment(1), projected_lifetime_scope(): provider = HashAlgorithmProvider.open_algorithm("SHA256") - if provider is None: - raise RuntimeError("SHA256 provider is unavailable") data = IBuffer.from_bytes(text.encode("utf-8")) digest = provider.hash_data(data) - if digest is None: - raise RuntimeError("HashAlgorithmProvider returned no digest") copied_digest = digest.to_bytes() expected_length = provider.hash_length if len(copied_digest) != expected_length: diff --git a/samples/python/device-watcher/app.py b/samples/python/device-watcher/app.py index 83f5578e..f329c513 100644 --- a/samples/python/device-watcher/app.py +++ b/samples/python/device-watcher/app.py @@ -15,8 +15,6 @@ async def enumerate_devices(timeout: int, show_names: bool) -> None: with RoApartment(1), projected_lifetime_scope(): watcher = DeviceInformation.create_watcher() - if watcher is None: - raise RuntimeError("DeviceInformation returned no watcher") loop = asyncio.get_running_loop() enumeration_completed = asyncio.Event() diff --git a/samples/python/ocr-image/app.py b/samples/python/ocr-image/app.py index 6cd4c9cc..b5beb506 100644 --- a/samples/python/ocr-image/app.py +++ b/samples/python/ocr-image/app.py @@ -17,30 +17,21 @@ def normalized_words(value: str) -> set[str]: async def recognize(path: Path) -> str: with RoApartment(1), projected_lifetime_scope(): file = await StorageFile.get_file_from_path_async(str(path.resolve())) - if file is None: - raise RuntimeError("StorageFile returned no image file") stream = await file.open_read_async() - if stream is None: - raise RuntimeError("StorageFile returned no image stream") decoder = await BitmapDecoder.create_async( stream.as_interface(IRandomAccessStream) ) - if decoder is None: - raise RuntimeError("BitmapDecoder returned no decoder") bitmap = await decoder.get_software_bitmap_async() - if bitmap is None: - raise RuntimeError("BitmapDecoder returned no SoftwareBitmap") with bitmap: + # Try* members return None instead of raising when nothing matches. engine = OcrEngine.try_create_from_user_profile_languages() if engine is None: raise RuntimeError( "No OCR engine is available for the user profile languages" ) result = await engine.recognize_async(bitmap) - if result is None: - raise RuntimeError("OcrEngine returned no result") return result.text diff --git a/samples/python/text-to-speech/app.py b/samples/python/text-to-speech/app.py index c3267524..3321e080 100644 --- a/samples/python/text-to-speech/app.py +++ b/samples/python/text-to-speech/app.py @@ -13,8 +13,6 @@ async def speak(text: str, smoke: bool) -> None: with RoApartment(1), projected_lifetime_scope(): with SpeechSynthesizer() as synthesizer: stream = await synthesizer.synthesize_text_to_stream_async(text) - if stream is None: - raise RuntimeError("SpeechSynthesizer returned no stream") with stream: if smoke: From 44ab63a7b7a18dc283238e370c45b46a6b188177 Mon Sep 17 00:00:00 2001 From: Leilei Zhang Date: Thu, 24 Sep 2026 16:59:35 +0800 Subject: [PATCH 4/5] Keep | None for documented nulls and mutable collection elements Review of the stub policy found two gaps. Some Windows SDK members are documented to return null, such as Accelerometer.GetDefault and DispatcherQueue.GetForCurrentThread. scripts/extract-null-results.py derives their doc comment IDs from MicrosoftDocs/winrt-api at a pinned commit. It collects returns and property-value sections that say the result can be null, and remarks that say the member itself returns null, then applies reviewed overrides. api-docs/windows-null-results.txt holds only the api-ids (933 members). Metadata parsing records the fact on each method, using the interface definition and the class chain that the documentation lists members under. The stub policy keeps `| None` on those members' results. Anyone can store null in a mutable IVector, IMap or observable collection, so its elements, item positions and element-reading members (GetAt, Lookup, GetMany) keep `| None`. Read-only views, iterators and arrays stay non-null. The element rule is a separate branch of the policy. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- bindings/py/README.md | 22 +- .../windows-null-results.overrides.txt | 17 + .../api-docs/windows-null-results.txt | 939 ++++++++++++++++++ .../scripts/extract-null-results.py | 250 +++++ .../src/codegen/winrt/python/method.rs | 2 +- .../src/codegen/winrt/python/nullability.rs | 196 +++- .../src/codegen/winrt/python/stub_helpers.rs | 21 +- .../src/codegen/winrt/python/stubs.rs | 42 +- .../src/codegen/winrt/python/type_helpers.rs | 131 ++- .../dynwinrt-codegen/src/documented_nulls.rs | 324 ++++++ tools/dynwinrt-codegen/src/lib.rs | 1 + tools/dynwinrt-codegen/src/meta.rs | 99 +- .../tests/python_consumer_typing_test.rs | 21 +- .../tests/python_stub_nullability_test.rs | 128 ++- 14 files changed, 2102 insertions(+), 91 deletions(-) create mode 100644 tools/dynwinrt-codegen/api-docs/windows-null-results.overrides.txt create mode 100644 tools/dynwinrt-codegen/api-docs/windows-null-results.txt create mode 100644 tools/dynwinrt-codegen/scripts/extract-null-results.py create mode 100644 tools/dynwinrt-codegen/src/documented_nulls.rs diff --git a/bindings/py/README.md b/bindings/py/README.md index a079c2fc..77835d7a 100644 --- a/bindings/py/README.md +++ b/bindings/py/README.md @@ -24,19 +24,33 @@ Generated `IReference` values are projected as `T | None`; native values, WinRT metadata does not record which values can be null, and most APIs raise an exception instead of returning null. The generated `.pyi` stubs therefore type the values you receive as non-null by default: method and property -results, async results, out values, and the elements, keys and values of -returned collections. For example, `StorageFolder.create_file_async()` returns -`WinRTCoroutine[StorageFile]` and `get_files_async()` returns -`WinRTCoroutine[Sequence[StorageFile]]`. +results, async results and out values. For example, +`StorageFolder.create_file_async()` returns `WinRTCoroutine[StorageFile]`. These values keep `| None`: - `IReference` values, projected as `T | None` everywhere; - results of `Try*` members, such as `try_get_item_async()` or `JsonObject.try_parse()`, where null means "not found"; +- results of Windows SDK members whose documentation says they can return + null, such as `Accelerometer.get_default()`, + `DispatcherQueue.get_for_current_thread()` or + `StorageFolder.get_parent_async()`. The codegen embeds this list, derived + from the Windows SDK API reference; it does not cover Windows App SDK + (`Microsoft.*`) APIs; - `Object`/`IInspectable` values (`DynWinRTValue | None`) and delegate-typed values, which are often null. +Collection elements follow the collection holding them. Anyone can store null +in a mutable `IVector`, `IMap` or observable collection, so their elements, +item positions (`[index]`, iteration, `get_at()`, `lookup()`) and +`items()`/`values()` are typed `T | None`: a `JsonArray` holds +`IJsonValue | None`. Read-only views, iterators and arrays keep non-null +elements: `get_files_async()` returns `WinRTCoroutine[Sequence[StorageFile]]`. +A view, iterator or key-value pair obtained from a mutable collection, such as +the result of `get_view()` or `first()`, can still contain nulls although its +elements are typed non-null. + Arguments keep accepting `None` where they did before. The stubs are optimistic, like the generated TypeScript declarations: the runtime still returns `None` when a WinRT API returns null, so check the API documentation diff --git a/tools/dynwinrt-codegen/api-docs/windows-null-results.overrides.txt b/tools/dynwinrt-codegen/api-docs/windows-null-results.overrides.txt new file mode 100644 index 00000000..912ee303 --- /dev/null +++ b/tools/dynwinrt-codegen/api-docs/windows-null-results.overrides.txt @@ -0,0 +1,17 @@ +# Reviewed corrections for windows-null-results.txt, applied last by +# scripts/extract-null-results.py. "+pattern" adds and "-pattern" removes +# members. A pattern is an api-id that may use fnmatch wildcards; it must match +# a member documented at the pinned winrt-api commit. + +# Sensors report a missing device through null across the whole family, as +# most of their pages state ("or null if no integrated ... are found"). ++M:Windows.Devices.Sensors.*.GetDefault* + +# Device lookups complete with null when the device is missing or access is +# denied, as many of their pages state. ++M:Windows.Devices.*.FromIdAsync(*) ++M:Windows.Devices.*.GetDefaultAsync* + +# Overridable implementation callbacks, not results that consumers receive. +-M:Windows.UI.Xaml.Automation.Peers.AutomationPeer.GetPatternCore(*) +-M:Windows.UI.Xaml.Controls.StyleSelector.SelectStyleCore(*) diff --git a/tools/dynwinrt-codegen/api-docs/windows-null-results.txt b/tools/dynwinrt-codegen/api-docs/windows-null-results.txt new file mode 100644 index 00000000..186c0eb5 --- /dev/null +++ b/tools/dynwinrt-codegen/api-docs/windows-null-results.txt @@ -0,0 +1,939 @@ +# Windows SDK members whose documented result can be null: a method's +# return value (for asynchronous methods, the completed result) or a +# property's value. dynwinrt-codegen keeps `| None` on these outputs. +# Source: https://github.com/MicrosoftDocs/winrt-api at commit 8448d5eecfbc2ed903f659f350841dcb4888bc8b +# Generated by scripts/extract-null-results.py; do not edit. Reviewed +# corrections belong in windows-null-results.overrides.txt. +M:Windows.AI.MachineLearning.TensorString.CreateReference +M:Windows.ApplicationModel.AppInstance.GetActivatedEventArgs +M:Windows.ApplicationModel.Calls.PhoneCall.GetFromId(System.String) +M:Windows.ApplicationModel.Calls.PhoneLineTransportDevice.FromId(System.String) +M:Windows.ApplicationModel.Contacts.ContactStore.GetContactListAsync(System.String) +M:Windows.ApplicationModel.ConversationalAgent.ActivationSignalDetectionConfiguration.GetModelData +M:Windows.ApplicationModel.ConversationalAgent.ActivationSignalDetectionConfiguration.GetModelDataAsync +M:Windows.ApplicationModel.ConversationalAgent.ActivationSignalDetectionConfiguration.GetModelDataType +M:Windows.ApplicationModel.ConversationalAgent.ActivationSignalDetectionConfiguration.GetModelDataTypeAsync +M:Windows.ApplicationModel.Package.GetContentGroupAsync(System.String) +M:Windows.ApplicationModel.UserDataAccounts.UserDataAccountManager.RequestStoreAsync(Windows.ApplicationModel.UserDataAccounts.UserDataAccountStoreAccessType) +M:Windows.ApplicationModel.UserDataTasks.UserDataTaskStore.GetListAsync(System.String) +M:Windows.ApplicationModel.Wallet.WalletItemStore.GetWalletItemAsync(System.String) +M:Windows.Data.Xml.Dom.DtdEntity.AppendChild(Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.DtdEntity.InsertBefore(Windows.Data.Xml.Dom.IXmlNode,Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.DtdEntity.RemoveChild(Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.DtdEntity.ReplaceChild(Windows.Data.Xml.Dom.IXmlNode,Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.DtdEntity.SelectNodesNS(System.String,System.Object) +M:Windows.Data.Xml.Dom.DtdEntity.SelectSingleNode(System.String) +M:Windows.Data.Xml.Dom.DtdEntity.SelectSingleNodeNS(System.String,System.Object) +M:Windows.Data.Xml.Dom.DtdNotation.AppendChild(Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.DtdNotation.InsertBefore(Windows.Data.Xml.Dom.IXmlNode,Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.DtdNotation.RemoveChild(Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.DtdNotation.ReplaceChild(Windows.Data.Xml.Dom.IXmlNode,Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.DtdNotation.SelectSingleNode(System.String) +M:Windows.Data.Xml.Dom.DtdNotation.SelectSingleNodeNS(System.String,System.Object) +M:Windows.Data.Xml.Dom.IXmlNode.AppendChild(Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.IXmlNode.InsertBefore(Windows.Data.Xml.Dom.IXmlNode,Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.IXmlNode.RemoveChild(Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.IXmlNode.ReplaceChild(Windows.Data.Xml.Dom.IXmlNode,Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.IXmlNodeSelector.SelectSingleNode(System.String) +M:Windows.Data.Xml.Dom.IXmlNodeSelector.SelectSingleNodeNS(System.String,System.Object) +M:Windows.Data.Xml.Dom.XmlAttribute.AppendChild(Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlAttribute.InsertBefore(Windows.Data.Xml.Dom.IXmlNode,Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlAttribute.RemoveChild(Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlAttribute.ReplaceChild(Windows.Data.Xml.Dom.IXmlNode,Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlAttribute.SelectSingleNode(System.String) +M:Windows.Data.Xml.Dom.XmlAttribute.SelectSingleNodeNS(System.String,System.Object) +M:Windows.Data.Xml.Dom.XmlCDataSection.AppendChild(Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlCDataSection.InsertBefore(Windows.Data.Xml.Dom.IXmlNode,Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlCDataSection.RemoveChild(Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlCDataSection.ReplaceChild(Windows.Data.Xml.Dom.IXmlNode,Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlCDataSection.SelectSingleNode(System.String) +M:Windows.Data.Xml.Dom.XmlCDataSection.SelectSingleNodeNS(System.String,System.Object) +M:Windows.Data.Xml.Dom.XmlComment.AppendChild(Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlComment.InsertBefore(Windows.Data.Xml.Dom.IXmlNode,Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlComment.RemoveChild(Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlComment.ReplaceChild(Windows.Data.Xml.Dom.IXmlNode,Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlComment.SelectSingleNode(System.String) +M:Windows.Data.Xml.Dom.XmlComment.SelectSingleNodeNS(System.String,System.Object) +M:Windows.Data.Xml.Dom.XmlDocument.AppendChild(Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlDocument.GetElementById(System.String) +M:Windows.Data.Xml.Dom.XmlDocument.InsertBefore(Windows.Data.Xml.Dom.IXmlNode,Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlDocument.RemoveChild(Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlDocument.ReplaceChild(Windows.Data.Xml.Dom.IXmlNode,Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlDocument.SelectSingleNode(System.String) +M:Windows.Data.Xml.Dom.XmlDocument.SelectSingleNodeNS(System.String,System.Object) +M:Windows.Data.Xml.Dom.XmlDocumentFragment.AppendChild(Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlDocumentFragment.InsertBefore(Windows.Data.Xml.Dom.IXmlNode,Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlDocumentFragment.RemoveChild(Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlDocumentFragment.ReplaceChild(Windows.Data.Xml.Dom.IXmlNode,Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlDocumentFragment.SelectSingleNode(System.String) +M:Windows.Data.Xml.Dom.XmlDocumentFragment.SelectSingleNodeNS(System.String,System.Object) +M:Windows.Data.Xml.Dom.XmlDocumentType.AppendChild(Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlDocumentType.InsertBefore(Windows.Data.Xml.Dom.IXmlNode,Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlDocumentType.RemoveChild(Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlDocumentType.ReplaceChild(Windows.Data.Xml.Dom.IXmlNode,Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlDocumentType.SelectSingleNode(System.String) +M:Windows.Data.Xml.Dom.XmlDocumentType.SelectSingleNodeNS(System.String,System.Object) +M:Windows.Data.Xml.Dom.XmlElement.AppendChild(Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlElement.InsertBefore(Windows.Data.Xml.Dom.IXmlNode,Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlElement.RemoveChild(Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlElement.ReplaceChild(Windows.Data.Xml.Dom.IXmlNode,Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlElement.SelectSingleNode(System.String) +M:Windows.Data.Xml.Dom.XmlElement.SelectSingleNodeNS(System.String,System.Object) +M:Windows.Data.Xml.Dom.XmlElement.SetAttributeNodeNS(Windows.Data.Xml.Dom.XmlAttribute) +M:Windows.Data.Xml.Dom.XmlEntityReference.AppendChild(Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlEntityReference.InsertBefore(Windows.Data.Xml.Dom.IXmlNode,Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlEntityReference.RemoveChild(Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlEntityReference.ReplaceChild(Windows.Data.Xml.Dom.IXmlNode,Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlEntityReference.SelectSingleNode(System.String) +M:Windows.Data.Xml.Dom.XmlEntityReference.SelectSingleNodeNS(System.String,System.Object) +M:Windows.Data.Xml.Dom.XmlNamedNodeMap.GetNamedItem(System.String) +M:Windows.Data.Xml.Dom.XmlNamedNodeMap.GetNamedItemNS(System.Object,System.String) +M:Windows.Data.Xml.Dom.XmlNamedNodeMap.Item(System.UInt32) +M:Windows.Data.Xml.Dom.XmlNamedNodeMap.RemoveNamedItem(System.String) +M:Windows.Data.Xml.Dom.XmlNamedNodeMap.RemoveNamedItemNS(System.Object,System.String) +M:Windows.Data.Xml.Dom.XmlNamedNodeMap.SetNamedItem(Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlNamedNodeMap.SetNamedItemNS(Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlNodeList.Item(System.UInt32) +M:Windows.Data.Xml.Dom.XmlProcessingInstruction.AppendChild(Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlProcessingInstruction.InsertBefore(Windows.Data.Xml.Dom.IXmlNode,Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlProcessingInstruction.RemoveChild(Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlProcessingInstruction.ReplaceChild(Windows.Data.Xml.Dom.IXmlNode,Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlProcessingInstruction.SelectSingleNode(System.String) +M:Windows.Data.Xml.Dom.XmlProcessingInstruction.SelectSingleNodeNS(System.String,System.Object) +M:Windows.Data.Xml.Dom.XmlText.AppendChild(Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlText.InsertBefore(Windows.Data.Xml.Dom.IXmlNode,Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlText.RemoveChild(Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlText.ReplaceChild(Windows.Data.Xml.Dom.IXmlNode,Windows.Data.Xml.Dom.IXmlNode) +M:Windows.Data.Xml.Dom.XmlText.SelectSingleNode(System.String) +M:Windows.Data.Xml.Dom.XmlText.SelectSingleNodeNS(System.String,System.Object) +M:Windows.Devices.Adc.AdcController.GetDefaultAsync +M:Windows.Devices.AllJoyn.AllJoynServiceInfo.FromIdAsync(System.String) +M:Windows.Devices.Bluetooth.BluetoothAdapter.FromIdAsync(System.String) +M:Windows.Devices.Bluetooth.BluetoothAdapter.GetDefaultAsync +M:Windows.Devices.Bluetooth.BluetoothDevice.FromBluetoothAddressAsync(System.UInt64) +M:Windows.Devices.Bluetooth.BluetoothDevice.FromIdAsync(System.String) +M:Windows.Devices.Bluetooth.BluetoothLEDevice.FromBluetoothAddressAsync(System.UInt64) +M:Windows.Devices.Bluetooth.BluetoothLEDevice.FromBluetoothAddressAsync(System.UInt64,Windows.Devices.Bluetooth.BluetoothAddressType) +M:Windows.Devices.Bluetooth.BluetoothLEDevice.FromIdAsync(System.String) +M:Windows.Devices.Bluetooth.GenericAttributeProfile.GattDeviceService.FromIdAsync(System.String) +M:Windows.Devices.Bluetooth.GenericAttributeProfile.GattDeviceService.FromIdAsync(System.String,Windows.Devices.Bluetooth.GenericAttributeProfile.GattSharingMode) +M:Windows.Devices.Bluetooth.Rfcomm.RfcommDeviceService.FromIdAsync(System.String) +M:Windows.Devices.Custom.CustomDevice.FromIdAsync(System.String,Windows.Devices.Custom.DeviceAccessMode,Windows.Devices.Custom.DeviceSharingMode) +M:Windows.Devices.Display.Core.DisplayTarget.TryGetMonitor +M:Windows.Devices.Display.DisplayMonitor.FromIdAsync(System.String) +M:Windows.Devices.Gpio.GpioController.GetDefault +M:Windows.Devices.Gpio.GpioController.GetDefaultAsync +M:Windows.Devices.Haptics.InputHapticsManager.TryGetForThread(System.UInt32) +M:Windows.Devices.Haptics.VibrationDevice.FromIdAsync(System.String) +M:Windows.Devices.Haptics.VibrationDevice.GetDefaultAsync +M:Windows.Devices.HumanInterfaceDevice.HidDevice.FromIdAsync(System.String,Windows.Storage.FileAccessMode) +M:Windows.Devices.I2c.I2cController.GetDefaultAsync +M:Windows.Devices.I2c.I2cDevice.FromIdAsync(System.String,Windows.Devices.I2c.I2cConnectionSettings) +M:Windows.Devices.I2c.II2cDeviceStatics.FromIdAsync(System.String,Windows.Devices.I2c.I2cConnectionSettings) +M:Windows.Devices.Input.PenDevice.GetFromPointerId(System.UInt32) +M:Windows.Devices.Lights.Lamp.FromIdAsync(System.String) +M:Windows.Devices.Lights.Lamp.GetDefaultAsync +M:Windows.Devices.Lights.LampArray.FromIdAsync(System.String) +M:Windows.Devices.Midi.MidiInPort.FromIdAsync(System.String) +M:Windows.Devices.Midi.MidiOutPort.FromIdAsync(System.String) +M:Windows.Devices.Perception.PerceptionColorFrameReader.TryReadLatestFrame +M:Windows.Devices.Perception.PerceptionColorFrameSource.AcquireControlSession +M:Windows.Devices.Perception.PerceptionColorFrameSource.FromIdAsync(System.String) +M:Windows.Devices.Perception.PerceptionColorFrameSource.TryGetDepthCorrelatedCameraIntrinsicsAsync(Windows.Devices.Perception.PerceptionDepthFrameSource) +M:Windows.Devices.Perception.PerceptionColorFrameSource.TryGetDepthCorrelatedCoordinateMapperAsync(System.String,Windows.Devices.Perception.PerceptionDepthFrameSource) +M:Windows.Devices.Perception.PerceptionDepthFrameReader.TryReadLatestFrame +M:Windows.Devices.Perception.PerceptionDepthFrameSource.AcquireControlSession +M:Windows.Devices.Perception.PerceptionDepthFrameSource.FromIdAsync(System.String) +M:Windows.Devices.Perception.PerceptionDepthFrameSource.TryGetDepthCorrelatedCameraIntrinsicsAsync(Windows.Devices.Perception.PerceptionDepthFrameSource) +M:Windows.Devices.Perception.PerceptionDepthFrameSource.TryGetDepthCorrelatedCoordinateMapperAsync(System.String,Windows.Devices.Perception.PerceptionDepthFrameSource) +M:Windows.Devices.Perception.PerceptionInfraredFrameReader.TryReadLatestFrame +M:Windows.Devices.Perception.PerceptionInfraredFrameSource.AcquireControlSession +M:Windows.Devices.Perception.PerceptionInfraredFrameSource.FromIdAsync(System.String) +M:Windows.Devices.Perception.PerceptionInfraredFrameSource.TryGetDepthCorrelatedCameraIntrinsicsAsync(Windows.Devices.Perception.PerceptionDepthFrameSource) +M:Windows.Devices.Perception.PerceptionInfraredFrameSource.TryGetDepthCorrelatedCoordinateMapperAsync(System.String,Windows.Devices.Perception.PerceptionDepthFrameSource) +M:Windows.Devices.Perception.Provider.IPerceptionFrameProviderManager.GetFrameProvider(Windows.Devices.Perception.Provider.PerceptionFrameProviderInfo) +M:Windows.Devices.PointOfService.BarcodeScanner.FromIdAsync(System.String) +M:Windows.Devices.PointOfService.BarcodeScanner.GetDefaultAsync +M:Windows.Devices.PointOfService.CashDrawer.FromIdAsync(System.String) +M:Windows.Devices.PointOfService.CashDrawer.GetDefaultAsync +M:Windows.Devices.PointOfService.ClaimedLineDisplay.FromIdAsync(System.String) +M:Windows.Devices.PointOfService.LineDisplay.FromIdAsync(System.String) +M:Windows.Devices.PointOfService.LineDisplay.GetDefaultAsync +M:Windows.Devices.PointOfService.MagneticStripeReader.ClaimReaderAsync +M:Windows.Devices.PointOfService.MagneticStripeReader.FromIdAsync(System.String) +M:Windows.Devices.PointOfService.MagneticStripeReader.GetDefaultAsync +M:Windows.Devices.PointOfService.PosPrinter.FromIdAsync(System.String) +M:Windows.Devices.PointOfService.PosPrinter.GetDefaultAsync +M:Windows.Devices.Power.Battery.FromIdAsync(System.String) +M:Windows.Devices.Printers.Print3DDevice.FromIdAsync(System.String) +M:Windows.Devices.Pwm.PwmController.FromIdAsync(System.String) +M:Windows.Devices.Pwm.PwmController.GetDefaultAsync +M:Windows.Devices.Radios.Radio.FromIdAsync(System.String) +M:Windows.Devices.Scanners.ImageScanner.FromIdAsync(System.String) +M:Windows.Devices.Sensors.Accelerometer.FromIdAsync(System.String) +M:Windows.Devices.Sensors.Accelerometer.GetDefault +M:Windows.Devices.Sensors.Accelerometer.GetDefault(Windows.Devices.Sensors.AccelerometerReadingType) +M:Windows.Devices.Sensors.Accelerometer.GetDeviceSelector(Windows.Devices.Sensors.AccelerometerReadingType) +M:Windows.Devices.Sensors.ActivitySensor.FromIdAsync(System.String) +M:Windows.Devices.Sensors.ActivitySensor.GetDefaultAsync +M:Windows.Devices.Sensors.ActivitySensor.GetDeviceSelector +M:Windows.Devices.Sensors.Altimeter.GetDefault +M:Windows.Devices.Sensors.Barometer.FromIdAsync(System.String) +M:Windows.Devices.Sensors.Barometer.GetDefault +M:Windows.Devices.Sensors.Barometer.GetDeviceSelector +M:Windows.Devices.Sensors.Compass.FromIdAsync(System.String) +M:Windows.Devices.Sensors.Compass.GetDefault +M:Windows.Devices.Sensors.Compass.GetDeviceSelector +M:Windows.Devices.Sensors.Custom.CustomSensor.FromIdAsync(System.String) +M:Windows.Devices.Sensors.Custom.CustomSensor.GetDeviceSelector(System.Guid) +M:Windows.Devices.Sensors.Gyrometer.FromIdAsync(System.String) +M:Windows.Devices.Sensors.Gyrometer.GetDefault +M:Windows.Devices.Sensors.Gyrometer.GetDeviceSelector +M:Windows.Devices.Sensors.HingeAngleSensor.FromIdAsync(System.String) +M:Windows.Devices.Sensors.HingeAngleSensor.GetDefaultAsync +M:Windows.Devices.Sensors.HingeAngleSensor.GetDeviceSelector +M:Windows.Devices.Sensors.HumanPresenceSensor.FromIdAsync(System.String) +M:Windows.Devices.Sensors.HumanPresenceSensor.GetDefault +M:Windows.Devices.Sensors.HumanPresenceSensor.GetDefaultAsync +M:Windows.Devices.Sensors.Inclinometer.FromIdAsync(System.String) +M:Windows.Devices.Sensors.Inclinometer.GetDefault +M:Windows.Devices.Sensors.Inclinometer.GetDefault(Windows.Devices.Sensors.SensorReadingType) +M:Windows.Devices.Sensors.Inclinometer.GetDefaultForRelativeReadings +M:Windows.Devices.Sensors.Inclinometer.GetDeviceSelector(Windows.Devices.Sensors.SensorReadingType) +M:Windows.Devices.Sensors.LightSensor.FromIdAsync(System.String) +M:Windows.Devices.Sensors.LightSensor.GetDefault +M:Windows.Devices.Sensors.LightSensor.GetDeviceSelector +M:Windows.Devices.Sensors.Magnetometer.FromIdAsync(System.String) +M:Windows.Devices.Sensors.Magnetometer.GetDefault +M:Windows.Devices.Sensors.Magnetometer.GetDeviceSelector +M:Windows.Devices.Sensors.OrientationSensor.FromIdAsync(System.String) +M:Windows.Devices.Sensors.OrientationSensor.GetDefault +M:Windows.Devices.Sensors.OrientationSensor.GetDefault(Windows.Devices.Sensors.SensorReadingType) +M:Windows.Devices.Sensors.OrientationSensor.GetDefault(Windows.Devices.Sensors.SensorReadingType,Windows.Devices.Sensors.SensorOptimizationGoal) +M:Windows.Devices.Sensors.OrientationSensor.GetDefaultForRelativeReadings +M:Windows.Devices.Sensors.OrientationSensor.GetDeviceSelector(Windows.Devices.Sensors.SensorReadingType) +M:Windows.Devices.Sensors.OrientationSensor.GetDeviceSelector(Windows.Devices.Sensors.SensorReadingType,Windows.Devices.Sensors.SensorOptimizationGoal) +M:Windows.Devices.Sensors.Pedometer.FromIdAsync(System.String) +M:Windows.Devices.Sensors.Pedometer.GetDefaultAsync +M:Windows.Devices.Sensors.Pedometer.GetDeviceSelector +M:Windows.Devices.Sensors.ProximitySensor.GetDeviceSelector +M:Windows.Devices.Sensors.SimpleOrientationSensor.FromIdAsync(System.String) +M:Windows.Devices.Sensors.SimpleOrientationSensor.GetDefault +M:Windows.Devices.Sensors.SimpleOrientationSensor.GetDeviceSelector +M:Windows.Devices.SerialCommunication.SerialDevice.FromIdAsync(System.String) +M:Windows.Devices.SmartCards.SmartCardEmulator.GetDefaultAsync +M:Windows.Devices.SmartCards.SmartCardReader.FromIdAsync(System.String) +M:Windows.Devices.Sms.SmsDevice.FromIdAsync(System.String) +M:Windows.Devices.Sms.SmsDevice.GetDefaultAsync +M:Windows.Devices.Spi.ISpiDeviceStatics.FromIdAsync(System.String,Windows.Devices.Spi.SpiConnectionSettings) +M:Windows.Devices.Spi.SpiController.GetDefaultAsync +M:Windows.Devices.Spi.SpiDevice.FromIdAsync(System.String,Windows.Devices.Spi.SpiConnectionSettings) +M:Windows.Devices.Usb.UsbDevice.FromIdAsync(System.String) +M:Windows.Devices.WiFi.WiFiAdapter.FromIdAsync(System.String) +M:Windows.Devices.WiFiDirect.Services.WiFiDirectService.FromIdAsync(System.String) +M:Windows.Devices.WiFiDirect.WiFiDirectDevice.FromIdAsync(System.String) +M:Windows.Devices.WiFiDirect.WiFiDirectDevice.FromIdAsync(System.String,Windows.Devices.WiFiDirect.WiFiDirectConnectionParameters) +M:Windows.Foundation.Diagnostics.FileLoggingSession.CloseAndSaveToFileAsync +M:Windows.Gaming.Input.Custom.GameControllerFactoryManager.TryGetFactoryControllerFromGameController(Windows.Gaming.Input.Custom.ICustomGameControllerFactory,Windows.Gaming.Input.IGameController) +M:Windows.Gaming.Input.Preview.LegacyGipGameControllerProvider.FromGameController(Windows.Gaming.Input.IGameController) +M:Windows.Gaming.Input.Preview.LegacyGipGameControllerProvider.FromGameControllerProvider(Windows.Gaming.Input.Custom.IGameControllerProvider) +M:Windows.Gaming.Input.Preview.LegacyGipGameControllerProvider.IsCopilot(Windows.System.User,System.String) +M:Windows.Gaming.Input.Preview.LegacyGipGameControllerProvider.IsPilot(Windows.System.User,System.String) +M:Windows.Gaming.UI.GameChatOverlay.GetDefault +M:Windows.Gaming.UI.GameMonitor.GetDefault +M:Windows.Globalization.NumberFormatting.CurrencyFormatter.ParseDouble(System.String) +M:Windows.Globalization.NumberFormatting.CurrencyFormatter.ParseInt(System.String) +M:Windows.Globalization.NumberFormatting.CurrencyFormatter.ParseUInt(System.String) +M:Windows.Globalization.NumberFormatting.DecimalFormatter.ParseDouble(System.String) +M:Windows.Globalization.NumberFormatting.DecimalFormatter.ParseInt(System.String) +M:Windows.Globalization.NumberFormatting.DecimalFormatter.ParseUInt(System.String) +M:Windows.Globalization.NumberFormatting.INumberParser.ParseDouble(System.String) +M:Windows.Globalization.NumberFormatting.INumberParser.ParseInt(System.String) +M:Windows.Globalization.NumberFormatting.INumberParser.ParseUInt(System.String) +M:Windows.Globalization.NumberFormatting.PercentFormatter.ParseDouble(System.String) +M:Windows.Globalization.NumberFormatting.PercentFormatter.ParseInt(System.String) +M:Windows.Globalization.NumberFormatting.PercentFormatter.ParseUInt(System.String) +M:Windows.Globalization.NumberFormatting.PermilleFormatter.ParseDouble(System.String) +M:Windows.Globalization.NumberFormatting.PermilleFormatter.ParseInt(System.String) +M:Windows.Globalization.NumberFormatting.PermilleFormatter.ParseUInt(System.String) +M:Windows.Graphics.Capture.Direct3D11CaptureFramePool.TryGetNextFrame +M:Windows.Graphics.Holographic.HolographicCameraPose.TryGetCullingFrustum(Windows.Perception.Spatial.SpatialCoordinateSystem) +M:Windows.Graphics.Holographic.HolographicCameraPose.TryGetViewTransform(Windows.Perception.Spatial.SpatialCoordinateSystem) +M:Windows.Graphics.Holographic.HolographicCameraPose.TryGetVisibleFrustum(Windows.Perception.Spatial.SpatialCoordinateSystem) +M:Windows.Graphics.Holographic.HolographicDisplay.TryGetViewConfiguration(Windows.Graphics.Holographic.HolographicViewConfigurationKind) +M:Windows.Graphics.Printing.Workflow.PrintWorkflowVirtualPrinterDataAvailableEventArgs.GetTargetFileAsync +M:Windows.Management.Deployment.PackageManager.FindPackage(System.String) +M:Windows.Management.Update.PreviewBuildsManager.GetDefault +M:Windows.Management.Update.WindowsUpdate.GetPropertyValue(System.String) +M:Windows.Media.Audio.AudioPlaybackConnection.TryCreateFromId(System.String) +M:Windows.Media.Capture.AppBroadcastStreamReader.TryGetNextAudioFrame +M:Windows.Media.Capture.AppBroadcastStreamReader.TryGetNextVideoFrame +M:Windows.Media.Capture.Frames.DepthMediaFrame.TryCreateCoordinateMapper(Windows.Media.Devices.Core.CameraIntrinsics,Windows.Perception.Spatial.SpatialCoordinateSystem) +M:Windows.Media.Capture.Frames.MediaFrameSource.TryGetCameraIntrinsics(Windows.Media.Capture.Frames.MediaFrameFormat) +M:Windows.Media.Devices.CallControl.FromId(System.String) +M:Windows.Media.Devices.CallControl.GetDefault +M:Windows.Media.Ocr.OcrEngine.TryCreateFromLanguage(Windows.Globalization.Language) +M:Windows.Media.Ocr.OcrEngine.TryCreateFromUserProfileLanguages +M:Windows.Networking.Connectivity.NetworkInformation.GetInternetConnectionProfile +M:Windows.Networking.NetworkOperators.ESimManager.TryCreateESimWatcher +M:Windows.Networking.Proximity.ProximityDevice.GetDefault +M:Windows.Networking.XboxLive.XboxLiveEndpointPair.FindEndpointPairByHostNamesAndPorts(Windows.Networking.HostName,System.String,Windows.Networking.HostName,System.String) +M:Windows.Networking.XboxLive.XboxLiveEndpointPair.FindEndpointPairBySocketAddressBytes(System.Byte[],System.Byte[]) +M:Windows.Perception.Spatial.SpatialAnchor.TryCreateRelativeTo(Windows.Perception.Spatial.SpatialCoordinateSystem) +M:Windows.Perception.Spatial.SpatialAnchor.TryCreateRelativeTo(Windows.Perception.Spatial.SpatialCoordinateSystem,Windows.Foundation.Numerics.Vector3) +M:Windows.Perception.Spatial.SpatialAnchor.TryCreateRelativeTo(Windows.Perception.Spatial.SpatialCoordinateSystem,Windows.Foundation.Numerics.Vector3,Windows.Foundation.Numerics.Quaternion) +M:Windows.Perception.Spatial.SpatialCoordinateSystem.TryGetTransformTo(Windows.Perception.Spatial.SpatialCoordinateSystem) +M:Windows.Perception.Spatial.SpatialLocatorAttachedFrameOfReference.TryGetRelativeHeadingAtTimestamp(Windows.Perception.PerceptionTimestamp) +M:Windows.Perception.Spatial.SpatialStageFrameOfReference.TryGetMovementBounds(Windows.Perception.Spatial.SpatialCoordinateSystem) +M:Windows.Perception.Spatial.Surfaces.SpatialSurfaceInfo.TryComputeLatestMeshAsync(System.Double) +M:Windows.Perception.Spatial.Surfaces.SpatialSurfaceInfo.TryGetBounds(Windows.Perception.Spatial.SpatialCoordinateSystem) +M:Windows.Services.Maps.Guidance.GuidanceRoute.TryCreateFromMapRoute(Windows.Services.Maps.MapRoute) +M:Windows.Storage.FileProperties.StorageItemContentProperties.RetrievePropertiesAsync(Windows.Foundation.Collections.IIterable{System.String}) +M:Windows.Storage.IStorageItem2.GetParentAsync +M:Windows.Storage.IStorageItemProperties.GetThumbnailAsync(Windows.Storage.FileProperties.ThumbnailMode) +M:Windows.Storage.IStorageItemProperties.GetThumbnailAsync(Windows.Storage.FileProperties.ThumbnailMode,System.UInt32) +M:Windows.Storage.IStorageItemProperties.GetThumbnailAsync(Windows.Storage.FileProperties.ThumbnailMode,System.UInt32,Windows.Storage.FileProperties.ThumbnailOptions) +M:Windows.Storage.IStorageItemProperties2.GetScaledImageAsThumbnailAsync(Windows.Storage.FileProperties.ThumbnailMode) +M:Windows.Storage.IStorageItemProperties2.GetScaledImageAsThumbnailAsync(Windows.Storage.FileProperties.ThumbnailMode,System.UInt32) +M:Windows.Storage.IStorageItemProperties2.GetScaledImageAsThumbnailAsync(Windows.Storage.FileProperties.ThumbnailMode,System.UInt32,Windows.Storage.FileProperties.ThumbnailOptions) +M:Windows.Storage.StorageFile.GetScaledImageAsThumbnailAsync(Windows.Storage.FileProperties.ThumbnailMode) +M:Windows.Storage.StorageFile.GetScaledImageAsThumbnailAsync(Windows.Storage.FileProperties.ThumbnailMode,System.UInt32) +M:Windows.Storage.StorageFile.GetScaledImageAsThumbnailAsync(Windows.Storage.FileProperties.ThumbnailMode,System.UInt32,Windows.Storage.FileProperties.ThumbnailOptions) +M:Windows.Storage.StorageFile.GetThumbnailAsync(Windows.Storage.FileProperties.ThumbnailMode) +M:Windows.Storage.StorageFile.GetThumbnailAsync(Windows.Storage.FileProperties.ThumbnailMode,System.UInt32) +M:Windows.Storage.StorageFile.GetThumbnailAsync(Windows.Storage.FileProperties.ThumbnailMode,System.UInt32,Windows.Storage.FileProperties.ThumbnailOptions) +M:Windows.Storage.StorageFolder.GetScaledImageAsThumbnailAsync(Windows.Storage.FileProperties.ThumbnailMode) +M:Windows.Storage.StorageFolder.GetScaledImageAsThumbnailAsync(Windows.Storage.FileProperties.ThumbnailMode,System.UInt32) +M:Windows.Storage.StorageFolder.GetScaledImageAsThumbnailAsync(Windows.Storage.FileProperties.ThumbnailMode,System.UInt32,Windows.Storage.FileProperties.ThumbnailOptions) +M:Windows.Storage.StorageFolder.GetThumbnailAsync(Windows.Storage.FileProperties.ThumbnailMode) +M:Windows.Storage.StorageFolder.GetThumbnailAsync(Windows.Storage.FileProperties.ThumbnailMode,System.UInt32) +M:Windows.Storage.StorageFolder.GetThumbnailAsync(Windows.Storage.FileProperties.ThumbnailMode,System.UInt32,Windows.Storage.FileProperties.ThumbnailOptions) +M:Windows.Storage.StorageFolder.TryGetItemAsync(System.String) +M:Windows.Storage.StorageLibrary.RequestAddFolderAsync +M:Windows.System.AppDiagnosticInfo.LaunchAsync +M:Windows.System.AppUriHandlerRegistrationManager.TryGetRegistration(System.String) +M:Windows.System.DispatcherQueue.GetForCurrentThread +M:Windows.System.RemoteSystems.RemoteSystem.FindByHostNameAsync(Windows.Networking.HostName) +M:Windows.System.User.GetFromId(System.String) +M:Windows.System.User.GetPictureAsync(Windows.System.UserPictureSize) +M:Windows.System.User.GetUserAgeRangeAsync +M:Windows.System.UserPicker.PickSingleUserAsync +M:Windows.System.UserProfile.UserInformation.GetAccountPicture(Windows.System.UserProfile.AccountPictureKind) +M:Windows.System.UserProfile.UserInformation.GetSessionInitiationProtocolUriAsync +M:Windows.UI.Composition.CompositionObject.TryGetAnimationController(System.String) +M:Windows.UI.Composition.Diagnostics.CompositionDebugSettings.TryGetSettings(Windows.UI.Composition.Compositor) +M:Windows.UI.Core.CoreWindow.GetForCurrentThread +M:Windows.UI.Core.CoreWindow.GetKeyState(Windows.System.VirtualKey) +M:Windows.UI.Input.Preview.Injection.InputInjector.TryCreate +M:Windows.UI.Input.Preview.Injection.InputInjector.TryCreateForAppBroadcastOnly +M:Windows.UI.Input.RadialControllerMenu.GetSelectedMenuItem +M:Windows.UI.Input.Spatial.SpatialHoldStartedEventArgs.TryGetPointerPose(Windows.Perception.Spatial.SpatialCoordinateSystem) +M:Windows.UI.Input.Spatial.SpatialInteractionController.TryGetBatteryReport +M:Windows.UI.Input.Spatial.SpatialInteractionController.TryGetRenderableModelAsync +M:Windows.UI.Input.Spatial.SpatialInteractionDetectedEventArgs.TryGetPointerPose(Windows.Perception.Spatial.SpatialCoordinateSystem) +M:Windows.UI.Input.Spatial.SpatialInteractionSource.TryCreateHandMeshObserver +M:Windows.UI.Input.Spatial.SpatialInteractionSource.TryCreateHandMeshObserverAsync +M:Windows.UI.Input.Spatial.SpatialInteractionSourceProperties.TryGetLocation(Windows.Perception.Spatial.SpatialCoordinateSystem) +M:Windows.UI.Input.Spatial.SpatialInteractionSourceProperties.TryGetSourceLossMitigationDirection(Windows.Perception.Spatial.SpatialCoordinateSystem) +M:Windows.UI.Input.Spatial.SpatialInteractionSourceState.TryGetHandPose +M:Windows.UI.Input.Spatial.SpatialInteractionSourceState.TryGetPointerPose(Windows.Perception.Spatial.SpatialCoordinateSystem) +M:Windows.UI.Input.Spatial.SpatialManipulationCompletedEventArgs.TryGetCumulativeDelta(Windows.Perception.Spatial.SpatialCoordinateSystem) +M:Windows.UI.Input.Spatial.SpatialManipulationStartedEventArgs.TryGetPointerPose(Windows.Perception.Spatial.SpatialCoordinateSystem) +M:Windows.UI.Input.Spatial.SpatialManipulationUpdatedEventArgs.TryGetCumulativeDelta(Windows.Perception.Spatial.SpatialCoordinateSystem) +M:Windows.UI.Input.Spatial.SpatialNavigationStartedEventArgs.TryGetPointerPose(Windows.Perception.Spatial.SpatialCoordinateSystem) +M:Windows.UI.Input.Spatial.SpatialPointerPose.TryGetAtTimestamp(Windows.Perception.Spatial.SpatialCoordinateSystem,Windows.Perception.PerceptionTimestamp) +M:Windows.UI.Input.Spatial.SpatialPointerPose.TryGetInteractionSourcePose(Windows.UI.Input.Spatial.SpatialInteractionSource) +M:Windows.UI.Input.Spatial.SpatialRecognitionStartedEventArgs.TryGetPointerPose(Windows.Perception.Spatial.SpatialCoordinateSystem) +M:Windows.UI.Input.Spatial.SpatialTappedEventArgs.TryGetPointerPose(Windows.Perception.Spatial.SpatialCoordinateSystem) +M:Windows.UI.Notifications.Management.UserNotificationListener.GetNotification(System.UInt32) +M:Windows.UI.Notifications.NotificationVisual.GetBinding(System.String) +M:Windows.UI.Popups.PopupMenu.ShowAsync(Windows.Foundation.Point) +M:Windows.UI.Popups.PopupMenu.ShowForSelectionAsync(Windows.Foundation.Rect) +M:Windows.UI.Popups.PopupMenu.ShowForSelectionAsync(Windows.Foundation.Rect,Windows.UI.Popups.Placement) +M:Windows.UI.Shell.FocusSessionManager.TryStartFocusSession +M:Windows.UI.Shell.FocusSessionManager.TryStartFocusSession(Windows.Foundation.DateTime) +M:Windows.UI.Text.ITextRange.InRange(Windows.UI.Text.ITextRange) +M:Windows.UI.Text.ITextRange.InStory(Windows.UI.Text.ITextRange) +M:Windows.UI.Text.ITextRange.MoveStart(Windows.UI.Text.TextRangeUnit,System.Int32) +M:Windows.UI.Text.RichEditTextRange.InRange(Windows.UI.Text.ITextRange) +M:Windows.UI.Text.RichEditTextRange.InStory(Windows.UI.Text.ITextRange) +M:Windows.UI.Text.RichEditTextRange.MoveStart(Windows.UI.Text.TextRangeUnit,System.Int32) +M:Windows.UI.UIAutomation.Core.AutomationRemoteOperationResult.GetOperand(Windows.UI.UIAutomation.Core.AutomationRemoteOperationOperandId) +M:Windows.UI.Xaml.Automation.Peers.AutomationPeer.GetPattern(Windows.UI.Xaml.Automation.Peers.PatternInterface) +M:Windows.UI.Xaml.Automation.Peers.FrameworkElementAutomationPeer.CreatePeerForElement(Windows.UI.Xaml.UIElement) +M:Windows.UI.Xaml.Automation.Peers.FrameworkElementAutomationPeer.FromElement(Windows.UI.Xaml.UIElement) +M:Windows.UI.Xaml.Automation.Peers.ItemsControlAutomationPeer.FindItemByProperty(Windows.UI.Xaml.Automation.Provider.IRawElementProviderSimple,Windows.UI.Xaml.Automation.AutomationProperty,System.Object) +M:Windows.UI.Xaml.Automation.Peers.LoopingSelectorAutomationPeer.FindItemByProperty(Windows.UI.Xaml.Automation.Provider.IRawElementProviderSimple,Windows.UI.Xaml.Automation.AutomationProperty,System.Object) +M:Windows.UI.Xaml.Automation.Provider.IDragProvider.GetGrabbedItems +M:Windows.UI.Xaml.Automation.Provider.IItemContainerProvider.FindItemByProperty(Windows.UI.Xaml.Automation.Provider.IRawElementProviderSimple,Windows.UI.Xaml.Automation.AutomationProperty,System.Object) +M:Windows.UI.Xaml.Automation.Provider.ITextRangeProvider.FindAttribute(System.Int32,System.Object,System.Boolean) +M:Windows.UI.Xaml.Automation.Provider.ITextRangeProvider.FindText(System.String,System.Boolean,System.Boolean) +M:Windows.UI.Xaml.Controls.Control.GetTemplateChild(System.String) +M:Windows.UI.Xaml.Controls.DataTemplateSelector.GetElement(Windows.UI.Xaml.ElementFactoryGetArgs) +M:Windows.UI.Xaml.Controls.DatePickerFlyoutItem.GetIndexedProperty(System.String,Windows.UI.Xaml.Interop.TypeName) +M:Windows.UI.Xaml.Controls.IItemContainerMapping.ContainerFromIndex(System.Int32) +M:Windows.UI.Xaml.Controls.IItemContainerMapping.ContainerFromItem(System.Object) +M:Windows.UI.Xaml.Controls.InkToolbar.GetToolButton(Windows.UI.Xaml.Controls.InkToolbarTool) +M:Windows.UI.Xaml.Controls.ItemContainerGenerator.ContainerFromIndex(System.Int32) +M:Windows.UI.Xaml.Controls.ItemContainerGenerator.ContainerFromItem(System.Object) +M:Windows.UI.Xaml.Controls.ItemsControl.ContainerFromIndex(System.Int32) +M:Windows.UI.Xaml.Controls.ItemsControl.ContainerFromItem(System.Object) +M:Windows.UI.Xaml.Controls.ItemsControl.GetItemsOwner(Windows.UI.Xaml.DependencyObject) +M:Windows.UI.Xaml.Controls.ItemsControl.ItemsControlFromItemContainer(Windows.UI.Xaml.DependencyObject) +M:Windows.UI.Xaml.Controls.Maps.MapControl.GetVisibleRegion(Windows.UI.Xaml.Controls.Maps.MapVisibleRegionKind) +M:Windows.UI.Xaml.Controls.Maps.StreetsidePanorama.FindNearbyAsync(Windows.Devices.Geolocation.Geopoint) +M:Windows.UI.Xaml.Controls.Maps.StreetsidePanorama.FindNearbyAsync(Windows.Devices.Geolocation.Geopoint,System.Double) +M:Windows.UI.Xaml.Controls.NavigationView.ContainerFromMenuItem(System.Object) +M:Windows.UI.Xaml.Controls.StyleSelector.SelectStyle(System.Object,Windows.UI.Xaml.DependencyObject) +M:Windows.UI.Xaml.Controls.SwapChainPanel.CreateCoreIndependentInputSource(Windows.UI.Core.CoreInputDeviceTypes) +M:Windows.UI.Xaml.Controls.TreeView.ContainerFromItem(System.Object) +M:Windows.UI.Xaml.Controls.TreeView.ContainerFromNode(Windows.UI.Xaml.Controls.TreeViewNode) +M:Windows.UI.Xaml.Data.ICustomPropertyProvider.GetCustomProperty(System.String) +M:Windows.UI.Xaml.Data.ICustomPropertyProvider.GetIndexedProperty(System.String,Windows.UI.Xaml.Interop.TypeName) +M:Windows.UI.Xaml.DataTemplate.GetElement(Windows.UI.Xaml.ElementFactoryGetArgs) +M:Windows.UI.Xaml.Documents.TextElement.FindName(System.String) +M:Windows.UI.Xaml.Documents.TextPointer.GetPositionAtOffset(System.Int32,Windows.UI.Xaml.Documents.LogicalDirection) +M:Windows.UI.Xaml.FrameworkElement.FindName(System.String) +M:Windows.UI.Xaml.FrameworkElement.GetBindingExpression(Windows.UI.Xaml.DependencyProperty) +M:Windows.UI.Xaml.Input.FocusManager.FindNextFocusableElement(Windows.UI.Xaml.Input.FocusNavigationDirection) +M:Windows.UI.Xaml.Input.FocusManager.FindNextFocusableElement(Windows.UI.Xaml.Input.FocusNavigationDirection,Windows.Foundation.Rect) +M:Windows.UI.Xaml.Input.FocusManager.GetFocusedElement +M:Windows.UI.Xaml.Input.PointerRoutedEventArgs.GetIntermediatePoints(Windows.UI.Xaml.UIElement) +M:Windows.UI.Xaml.Markup.IXamlType.GetMember(System.String) +M:Windows.UI.Xaml.Media.Animation.ConnectedAnimationService.GetAnimation(System.String) +M:Windows.UI.Xaml.Media.Animation.Storyboard.GetCurrentTime +M:Windows.Web.IUriToStreamResolver.UriToStreamAsync(Windows.Foundation.Uri) +P:Windows.ApplicationModel.Activation.AppointmentsProviderAddAppointmentActivatedEventArgs.User +P:Windows.ApplicationModel.Activation.AppointmentsProviderRemoveAppointmentActivatedEventArgs.User +P:Windows.ApplicationModel.Activation.AppointmentsProviderReplaceAppointmentActivatedEventArgs.User +P:Windows.ApplicationModel.Activation.AppointmentsProviderShowAppointmentDetailsActivatedEventArgs.User +P:Windows.ApplicationModel.Activation.AppointmentsProviderShowTimeFrameActivatedEventArgs.User +P:Windows.ApplicationModel.Activation.CachedFileUpdaterActivatedEventArgs.User +P:Windows.ApplicationModel.Activation.DeviceActivatedEventArgs.User +P:Windows.ApplicationModel.Activation.DeviceActivatedEventArgs.ViewSwitcher +P:Windows.ApplicationModel.Activation.DialReceiverActivatedEventArgs.ViewSwitcher +P:Windows.ApplicationModel.Activation.FileActivatedEventArgs.NeighboringFilesQuery +P:Windows.ApplicationModel.Activation.FileActivatedEventArgs.User +P:Windows.ApplicationModel.Activation.FileActivatedEventArgs.ViewSwitcher +P:Windows.ApplicationModel.Activation.FileOpenPickerActivatedEventArgs.User +P:Windows.ApplicationModel.Activation.FileOpenPickerContinuationEventArgs.User +P:Windows.ApplicationModel.Activation.FileSavePickerActivatedEventArgs.User +P:Windows.ApplicationModel.Activation.FileSavePickerContinuationEventArgs.User +P:Windows.ApplicationModel.Activation.FolderPickerContinuationEventArgs.User +P:Windows.ApplicationModel.Activation.IViewSwitcherProvider.ViewSwitcher +P:Windows.ApplicationModel.Activation.LaunchActivatedEventArgs.User +P:Windows.ApplicationModel.Activation.LockScreenCallActivatedEventArgs.ViewSwitcher +P:Windows.ApplicationModel.Activation.ProtocolActivatedEventArgs.Data +P:Windows.ApplicationModel.Activation.ProtocolActivatedEventArgs.User +P:Windows.ApplicationModel.Activation.ProtocolActivatedEventArgs.ViewSwitcher +P:Windows.ApplicationModel.Activation.ProtocolForResultsActivatedEventArgs.Data +P:Windows.ApplicationModel.Activation.ProtocolForResultsActivatedEventArgs.ViewSwitcher +P:Windows.ApplicationModel.Activation.RestrictedLaunchActivatedEventArgs.User +P:Windows.ApplicationModel.Activation.SearchActivatedEventArgs.User +P:Windows.ApplicationModel.Activation.SearchActivatedEventArgs.ViewSwitcher +P:Windows.ApplicationModel.Activation.ShareTargetActivatedEventArgs.User +P:Windows.ApplicationModel.Activation.VoiceCommandActivatedEventArgs.User +P:Windows.ApplicationModel.AppInstallerInfo.PausedUntil +P:Windows.ApplicationModel.AppInstance.RecommendedInstance +P:Windows.ApplicationModel.Appointments.Appointment.Reminder +P:Windows.ApplicationModel.Background.BackgroundTaskRegistration.Trigger +P:Windows.ApplicationModel.Background.BluetoothLEAdvertisementPublisherTrigger.PreferredTransmitPowerLevelInDBm +P:Windows.ApplicationModel.Background.IBackgroundTaskRegistration2.Trigger +P:Windows.ApplicationModel.Background.RfcommConnectionTrigger.InboundConnection +P:Windows.ApplicationModel.Background.RfcommConnectionTrigger.OutboundConnection +P:Windows.ApplicationModel.Calls.PhoneLineDialResult.DialedCall +P:Windows.ApplicationModel.Package.MachineExternalLocation +P:Windows.ApplicationModel.Package.UserExternalLocation +P:Windows.ApplicationModel.PackageCatalogAddOptionalPackageResult.ExtendedError +P:Windows.ApplicationModel.PackageCatalogAddOptionalPackageResult.Package +P:Windows.ApplicationModel.PackageId.ResourceId +P:Windows.ApplicationModel.Search.Core.SearchSuggestion.DetailText +P:Windows.ApplicationModel.Search.Core.SearchSuggestion.Image +P:Windows.ApplicationModel.Search.Core.SearchSuggestion.ImageAlternateText +P:Windows.ApplicationModel.Search.Core.SearchSuggestion.Tag +P:Windows.ApplicationModel.UserDataAccounts.UserDataAccount.EnterpriseId +P:Windows.ApplicationModel.UserDataAccounts.UserDataAccount.Icon +P:Windows.ApplicationModel.UserDataTasks.UserDataTask.Reminder +P:Windows.ApplicationModel.Wallet.WalletItem.ExpirationDate +P:Windows.ApplicationModel.Wallet.WalletItem.LastUpdated +P:Windows.ApplicationModel.Wallet.WalletItem.RelevantDate +P:Windows.ApplicationModel.Wallet.WalletTransaction.TransactionDate +P:Windows.Data.Xml.Dom.DtdEntity.FirstChild +P:Windows.Data.Xml.Dom.DtdEntity.LastChild +P:Windows.Data.Xml.Dom.DtdEntity.NextSibling +P:Windows.Data.Xml.Dom.DtdEntity.NodeValue +P:Windows.Data.Xml.Dom.DtdNotation.FirstChild +P:Windows.Data.Xml.Dom.DtdNotation.LastChild +P:Windows.Data.Xml.Dom.DtdNotation.NextSibling +P:Windows.Data.Xml.Dom.DtdNotation.NodeValue +P:Windows.Data.Xml.Dom.IXmlNode.Attributes +P:Windows.Data.Xml.Dom.IXmlNode.FirstChild +P:Windows.Data.Xml.Dom.IXmlNode.LastChild +P:Windows.Data.Xml.Dom.XmlAttribute.FirstChild +P:Windows.Data.Xml.Dom.XmlAttribute.LastChild +P:Windows.Data.Xml.Dom.XmlAttribute.NextSibling +P:Windows.Data.Xml.Dom.XmlCDataSection.ChildNodes +P:Windows.Data.Xml.Dom.XmlCDataSection.FirstChild +P:Windows.Data.Xml.Dom.XmlCDataSection.LastChild +P:Windows.Data.Xml.Dom.XmlComment.ChildNodes +P:Windows.Data.Xml.Dom.XmlComment.FirstChild +P:Windows.Data.Xml.Dom.XmlComment.LastChild +P:Windows.Data.Xml.Dom.XmlDocument.FirstChild +P:Windows.Data.Xml.Dom.XmlDocument.LastChild +P:Windows.Data.Xml.Dom.XmlDocument.ParentNode +P:Windows.Data.Xml.Dom.XmlDocumentFragment.Attributes +P:Windows.Data.Xml.Dom.XmlDocumentFragment.FirstChild +P:Windows.Data.Xml.Dom.XmlDocumentFragment.LastChild +P:Windows.Data.Xml.Dom.XmlDocumentType.Attributes +P:Windows.Data.Xml.Dom.XmlDocumentType.FirstChild +P:Windows.Data.Xml.Dom.XmlDocumentType.LastChild +P:Windows.Data.Xml.Dom.XmlElement.Attributes +P:Windows.Data.Xml.Dom.XmlElement.FirstChild +P:Windows.Data.Xml.Dom.XmlElement.LastChild +P:Windows.Data.Xml.Dom.XmlEntityReference.Attributes +P:Windows.Data.Xml.Dom.XmlEntityReference.FirstChild +P:Windows.Data.Xml.Dom.XmlEntityReference.LastChild +P:Windows.Data.Xml.Dom.XmlProcessingInstruction.Attributes +P:Windows.Data.Xml.Dom.XmlProcessingInstruction.FirstChild +P:Windows.Data.Xml.Dom.XmlProcessingInstruction.LastChild +P:Windows.Data.Xml.Dom.XmlText.Attributes +P:Windows.Data.Xml.Dom.XmlText.FirstChild +P:Windows.Data.Xml.Dom.XmlText.LastChild +P:Windows.Devices.Bluetooth.Advertisement.BluetoothLEAdvertisementPublisher.PreferredTransmitPowerLevelInDBm +P:Windows.Devices.Bluetooth.GenericAttributeProfile.GattDeviceService.ParentServices +P:Windows.Devices.Display.Core.DisplayTarget.DeviceInterfacePath +P:Windows.Devices.Display.DisplayMonitor.BluePrimary +P:Windows.Devices.Display.DisplayMonitor.DeviceId +P:Windows.Devices.Display.DisplayMonitor.DisplayAdapterDeviceId +P:Windows.Devices.Display.DisplayMonitor.DisplayAdapterId +P:Windows.Devices.Display.DisplayMonitor.DisplayName +P:Windows.Devices.Display.DisplayMonitor.GreenPrimary +P:Windows.Devices.Display.DisplayMonitor.PhysicalSizeInInches +P:Windows.Devices.Display.DisplayMonitor.RedPrimary +P:Windows.Devices.Display.DisplayMonitor.WhitePoint +P:Windows.Devices.Enumeration.DeviceInformation.EnclosureLocation +P:Windows.Devices.Geolocation.GeocoordinateSatelliteData.GeometricDilutionOfPrecision +P:Windows.Devices.Geolocation.GeocoordinateSatelliteData.TimeDilutionOfPrecision +P:Windows.Devices.Geolocation.Geoposition.CivicAddress +P:Windows.Devices.Geolocation.Geoposition.VenueData +P:Windows.Devices.Haptics.InputHapticsManager.CurrentHapticsController +P:Windows.Devices.PointOfService.BarcodeScannerReport.ScanDataLabel +P:Windows.Devices.PointOfService.CashDrawer.DrawerEventSource +P:Windows.Devices.PointOfService.ClaimedLineDisplay.CustomGlyphs +P:Windows.Devices.PointOfService.ClaimedPosPrinter.Journal +P:Windows.Devices.PointOfService.ClaimedPosPrinter.Receipt +P:Windows.Devices.PointOfService.ClaimedPosPrinter.Slip +P:Windows.Devices.Scanners.ImageScanner.AutoConfiguration +P:Windows.Devices.Scanners.ImageScanner.FeederConfiguration +P:Windows.Devices.Scanners.ImageScanner.FlatbedConfiguration +P:Windows.Devices.Sensors.AccelerometerReading.PerformanceCount +P:Windows.Devices.Sensors.AltimeterReading.PerformanceCount +P:Windows.Devices.Sensors.BarometerReading.PerformanceCount +P:Windows.Devices.Sensors.CompassReading.PerformanceCount +P:Windows.Devices.Sensors.Custom.CustomSensorReading.PerformanceCount +P:Windows.Devices.Sensors.GyrometerReading.PerformanceCount +P:Windows.Devices.Sensors.InclinometerReading.PerformanceCount +P:Windows.Devices.Sensors.LightSensorReading.PerformanceCount +P:Windows.Devices.Sensors.MagnetometerReading.PerformanceCount +P:Windows.Devices.Sensors.OrientationSensorReading.PerformanceCount +P:Windows.Devices.SmartCards.SmartCardCryptogramPlacementStep.Algorithm +P:Windows.Devices.Usb.UsbBulkInEndpointDescriptor.Pipe +P:Windows.Devices.WiFi.WiFiOnDemandHotspotNetworkProperties.RemainingBatteryPercent +P:Windows.Devices.WiFiDirect.Services.WiFiDirectServiceAutoAcceptSessionConnectedEventArgs.SessionInfo +P:Windows.Devices.WiFiDirect.WiFiDirectAdvertisement.InformationElements +P:Windows.Gaming.Input.RacingWheel.WheelMotor +P:Windows.Globalization.Fonts.LanguageFontGroup.DocumentAlternate1Font +P:Windows.Globalization.Fonts.LanguageFontGroup.DocumentAlternate2Font +P:Windows.Globalization.Fonts.LanguageFontGroup.FixedWidthTextFont +P:Windows.Graphics.Display.DisplayEnhancementOverride.BrightnessOverrideSettings +P:Windows.Graphics.Display.DisplayEnhancementOverride.ColorOverrideSettings +P:Windows.Graphics.Display.DisplayInformation.DiagonalSizeInInches +P:Windows.Media.Audio.AudioFileInputNode.EndTime +P:Windows.Media.Audio.AudioFileInputNode.StartTime +P:Windows.Media.Audio.AudioGraphSettings.PrimaryRenderDevice +P:Windows.Media.Audio.MediaSourceAudioInputNode.EndTime +P:Windows.Media.Audio.MediaSourceAudioInputNode.StartTime +P:Windows.Media.Capture.AdvancedCapturedPhoto.FrameBoundsRelativeToReferencePhoto +P:Windows.Media.Capture.Frames.MediaFrameFormat.VideoFormat +P:Windows.Media.Capture.Frames.MediaFrameReference.AudioMediaFrame +P:Windows.Media.Capture.Frames.MediaFrameReference.BufferMediaFrame +P:Windows.Media.Capture.Frames.MediaFrameReference.VideoMediaFrame +P:Windows.Media.Capture.Frames.MediaFrameSourceGetPropertyResult.Value +P:Windows.Media.Capture.Frames.VideoMediaFrame.SoftwareBitmap +P:Windows.Media.Capture.Frames.VideoMediaFrameFormat.DepthFormat +P:Windows.Media.Capture.MediaCaptureInitializationSettings.AudioDeviceId +P:Windows.Media.Capture.MediaCaptureInitializationSettings.VideoDeviceId +P:Windows.Media.Capture.MediaCaptureSettings.AudioDeviceId +P:Windows.Media.Capture.MediaCaptureSettings.VideoDeviceId +P:Windows.Media.Core.MediaSource.AdaptiveMediaSource +P:Windows.Media.Core.MediaSource.MediaStreamSource +P:Windows.Media.Core.MediaSource.MseStreamSource +P:Windows.Media.Core.MediaStreamSourceSampleRequest.Sample +P:Windows.Media.Core.MediaStreamSourceStartingRequest.StartPosition +P:Windows.Media.Devices.VideoDeviceControllerGetDevicePropertyResult.Value +P:Windows.Media.Effects.AudioEffect.AcousticEchoCancellationConfiguration +P:Windows.Media.MediaProperties.MediaEncodingProfile.Audio +P:Windows.Media.MediaProperties.MediaEncodingProfile.Video +P:Windows.Media.Ocr.OcrResult.TextAngle +P:Windows.Media.Protection.PlayReady.PlayReadyContentHeader.HeaderWithEmbeddedUpdates +P:Windows.Media.Protection.PlayReady.PlayReadyIndividualizationServiceRequest.ChallengeCustomData +P:Windows.Media.Protection.PlayReady.PlayReadyIndividualizationServiceRequest.ResponseCustomData +P:Windows.Media.Protection.PlayReady.PlayReadyIndividualizationServiceRequest.Uri +P:Windows.Media.Protection.PlayReady.PlayReadyRevocationServiceRequest.ChallengeCustomData +P:Windows.Media.Protection.PlayReady.PlayReadyRevocationServiceRequest.ResponseCustomData +P:Windows.Media.Protection.PlayReady.PlayReadyRevocationServiceRequest.Uri +P:Windows.Media.Protection.PlayReady.PlayReadyStatics.HardwareDRMDisabledAtTime +P:Windows.Media.Protection.PlayReady.PlayReadyStatics.HardwareDRMDisabledUntilTime +P:Windows.Media.SpeechRecognition.SpeechRecognitionResult.Constraint +P:Windows.Media.SpeechRecognition.SpeechRecognizer.SystemSpeechLanguage +P:Windows.Media.Streaming.Adaptive.AdaptiveMediaSourceCorrelatedTimes.PresentationTimeStamp +P:Windows.Media.VideoFrame.Direct3DSurface +P:Windows.Media.VideoFrame.SoftwareBitmap +P:Windows.Networking.BackgroundTransfer.BackgroundDownloader.CompletionGroup +P:Windows.Networking.BackgroundTransfer.BackgroundUploader.CompletionGroup +P:Windows.Networking.EndpointPair.LocalHostName +P:Windows.Networking.HostName.IPInformation +P:Windows.Networking.NetworkOperators.ESim.SlotIndex +P:Windows.Networking.NetworkOperators.ESimDownloadProfileMetadataResult.ProfileMetadata +P:Windows.Networking.NetworkOperators.NetworkOperatorNotificationEventDetails.SmsMessage +P:Windows.Networking.NetworkOperators.UssdReply.Message +P:Windows.Networking.Proximity.TriggeredConnectionStateChangedEventArgs.Socket +P:Windows.Networking.PushNotifications.PushNotificationReceivedEventArgs.BadgeNotification +P:Windows.Networking.PushNotifications.PushNotificationReceivedEventArgs.RawNotification +P:Windows.Networking.PushNotifications.PushNotificationReceivedEventArgs.TileNotification +P:Windows.Networking.PushNotifications.PushNotificationReceivedEventArgs.ToastNotification +P:Windows.Networking.Sockets.IWebSocketInformation.Protocol +P:Windows.Networking.Sockets.MessageWebSocketInformation.Protocol +P:Windows.Networking.Sockets.StreamSocketInformation.RemoteAddress +P:Windows.Networking.Sockets.StreamSocketInformation.SessionKey +P:Windows.Networking.Sockets.StreamWebSocketInformation.Protocol +P:Windows.Networking.Vpn.VpnChannel.CurrentRequestTransportContext +P:Windows.Perception.People.EyesPose.Gaze +P:Windows.Perception.Spatial.SpatialStageFrameOfReference.Current +P:Windows.Perception.Spatial.Surfaces.SpatialSurfaceMesh.VertexNormals +P:Windows.Security.EnterpriseData.ProtectionPolicyManager.PrimaryManagedIdentity +P:Windows.Services.Cortana.CortanaActionableInsights.User +P:Windows.Services.Cortana.CortanaActionableInsightsOptions.ContentSourceWebLink +P:Windows.Services.Cortana.CortanaActionableInsightsOptions.SurroundingText +P:Windows.Services.Maps.Guidance.GuidanceManeuver.RoadSignpost +P:Windows.Services.Maps.Guidance.GuidanceRoadSignpost.Exit +P:Windows.Services.Maps.Guidance.GuidanceRoadSignpost.ExitNumber +P:Windows.Services.Maps.MapRouteFinderResult.AlternateRoutes +P:Windows.Services.Store.StoreContext.User +P:Windows.Services.Store.StoreSku.SubscriptionInfo +P:Windows.System.RemoteSystems.RemoteSystemSessionCreationResult.Session +P:Windows.System.RemoteSystems.RemoteSystemSessionJoinResult.Session +P:Windows.UI.Composition.CompositionGeometricClip.Geometry +P:Windows.UI.Composition.RedirectVisual.Source +P:Windows.UI.Composition.ScalarNaturalMotionAnimation.FinalValue +P:Windows.UI.Composition.ScalarNaturalMotionAnimation.InitialValue +P:Windows.UI.Composition.Vector2NaturalMotionAnimation.FinalValue +P:Windows.UI.Composition.Vector2NaturalMotionAnimation.InitialValue +P:Windows.UI.Composition.Vector3NaturalMotionAnimation.FinalValue +P:Windows.UI.Composition.Vector3NaturalMotionAnimation.InitialValue +P:Windows.UI.Input.Inking.InkPresenter.StrokeContainer +P:Windows.UI.Input.PointerPointProperties.ZDistance +P:Windows.UI.Input.RadialControllerButtonClickedEventArgs.Contact +P:Windows.UI.Input.RadialControllerButtonHoldingEventArgs.Contact +P:Windows.UI.Input.RadialControllerButtonPressedEventArgs.Contact +P:Windows.UI.Input.RadialControllerButtonReleasedEventArgs.Contact +P:Windows.UI.Input.RadialControllerControlAcquiredEventArgs.Contact +P:Windows.UI.Input.RadialControllerRotationChangedEventArgs.Contact +P:Windows.UI.Input.RadialControllerScreenContactContinuedEventArgs.Contact +P:Windows.UI.Input.RadialControllerScreenContactStartedEventArgs.Contact +P:Windows.UI.Input.Spatial.SpatialInteractionController.SimpleHapticsController +P:Windows.UI.Input.Spatial.SpatialInteractionSource.Controller +P:Windows.UI.Input.Spatial.SpatialInteractionSourceLocation.SourcePointerPose +P:Windows.UI.Input.Spatial.SpatialInteractionSourceState.ControllerProperties +P:Windows.UI.Input.Spatial.SpatialPointerPose.Eyes +P:Windows.UI.Popups.UICommand.Invoked +P:Windows.UI.Shell.Tasks.AppTaskInfo.EndTime +P:Windows.UI.StartScreen.TileMixedRealityModel.BoundingBox +P:Windows.UI.Text.ContentLinkInfo.Uri +P:Windows.UI.Text.Core.CoreTextSelectionRequest.Selection +P:Windows.UI.Text.Core.CoreTextTextRequest.Text +P:Windows.UI.WebUI.WebUIAppointmentsProviderAddAppointmentActivatedEventArgs.User +P:Windows.UI.WebUI.WebUIAppointmentsProviderRemoveAppointmentActivatedEventArgs.User +P:Windows.UI.WebUI.WebUIAppointmentsProviderReplaceAppointmentActivatedEventArgs.User +P:Windows.UI.WebUI.WebUIAppointmentsProviderShowAppointmentDetailsActivatedEventArgs.User +P:Windows.UI.WebUI.WebUIAppointmentsProviderShowTimeFrameActivatedEventArgs.User +P:Windows.UI.WebUI.WebUIBackgroundTaskInstance.Current +P:Windows.UI.WebUI.WebUICachedFileUpdaterActivatedEventArgs.User +P:Windows.UI.WebUI.WebUIContactPanelActivatedEventArgs.User +P:Windows.UI.WebUI.WebUIDeviceActivatedEventArgs.User +P:Windows.UI.WebUI.WebUIDevicePairingActivatedEventArgs.User +P:Windows.UI.WebUI.WebUIDialReceiverActivatedEventArgs.User +P:Windows.UI.WebUI.WebUIFileActivatedEventArgs.User +P:Windows.UI.WebUI.WebUIFileOpenPickerActivatedEventArgs.User +P:Windows.UI.WebUI.WebUIFileOpenPickerContinuationEventArgs.User +P:Windows.UI.WebUI.WebUIFileSavePickerActivatedEventArgs.User +P:Windows.UI.WebUI.WebUIFileSavePickerContinuationEventArgs.User +P:Windows.UI.WebUI.WebUIFolderPickerContinuationEventArgs.User +P:Windows.UI.WebUI.WebUILaunchActivatedEventArgs.User +P:Windows.UI.WebUI.WebUILockScreenActivatedEventArgs.User +P:Windows.UI.WebUI.WebUIProtocolActivatedEventArgs.User +P:Windows.UI.WebUI.WebUIProtocolForResultsActivatedEventArgs.User +P:Windows.UI.WebUI.WebUIRestrictedLaunchActivatedEventArgs.User +P:Windows.UI.WebUI.WebUIShareTargetActivatedEventArgs.User +P:Windows.UI.WebUI.WebUIToastNotificationActivatedEventArgs.User +P:Windows.UI.WebUI.WebUIVoiceCommandActivatedEventArgs.User +P:Windows.UI.WebUI.WebUIWebAccountProviderActivatedEventArgs.User +P:Windows.UI.Xaml.Automation.Provider.IDragProvider.DropEffect +P:Windows.UI.Xaml.Automation.Provider.IRangeValueProvider.LargeChange +P:Windows.UI.Xaml.Automation.Provider.IRangeValueProvider.Maximum +P:Windows.UI.Xaml.Automation.Provider.IRangeValueProvider.Minimum +P:Windows.UI.Xaml.Automation.Provider.IRangeValueProvider.SmallChange +P:Windows.UI.Xaml.Automation.Provider.IRangeValueProvider.Value +P:Windows.UI.Xaml.BringIntoViewOptions.TargetRect +P:Windows.UI.Xaml.Controls.AnchorRequestedEventArgs.Anchor +P:Windows.UI.Xaml.Controls.AppBarButton.KeyboardAcceleratorTextOverride +P:Windows.UI.Xaml.Controls.AppBarToggleButton.KeyboardAcceleratorTextOverride +P:Windows.UI.Xaml.Controls.AutoSuggestBox.Description +P:Windows.UI.Xaml.Controls.AutoSuggestBox.QueryIcon +P:Windows.UI.Xaml.Controls.BitmapIcon.UriSource +P:Windows.UI.Xaml.Controls.BitmapIconSource.UriSource +P:Windows.UI.Xaml.Controls.Border.Background +P:Windows.UI.Xaml.Controls.Border.BackgroundTransition +P:Windows.UI.Xaml.Controls.Border.BorderBrush +P:Windows.UI.Xaml.Controls.Button.Flyout +P:Windows.UI.Xaml.Controls.CalendarDatePicker.Description +P:Windows.UI.Xaml.Controls.CalendarDatePicker.Header +P:Windows.UI.Xaml.Controls.CalendarDatePicker.HeaderTemplate +P:Windows.UI.Xaml.Controls.CalendarViewDayItemChangingEventArgs.Item +P:Windows.UI.Xaml.Controls.ColorPicker.PreviousColor +P:Windows.UI.Xaml.Controls.ComboBox.Description +P:Windows.UI.Xaml.Controls.ComboBox.Header +P:Windows.UI.Xaml.Controls.ComboBox.HeaderTemplate +P:Windows.UI.Xaml.Controls.CommandBar.CommandBarOverflowPresenterStyle +P:Windows.UI.Xaml.Controls.ContainerContentChangingEventArgs.Item +P:Windows.UI.Xaml.Controls.ContentControl.Content +P:Windows.UI.Xaml.Controls.ContentControl.ContentTemplateRoot +P:Windows.UI.Xaml.Controls.ContentDialog.CloseButtonCommandParameter +P:Windows.UI.Xaml.Controls.ContentDialog.CloseButtonStyle +P:Windows.UI.Xaml.Controls.ContentDialog.PrimaryButtonCommandParameter +P:Windows.UI.Xaml.Controls.ContentDialog.PrimaryButtonStyle +P:Windows.UI.Xaml.Controls.ContentDialog.PrimaryButtonText +P:Windows.UI.Xaml.Controls.ContentDialog.SecondaryButtonCommandParameter +P:Windows.UI.Xaml.Controls.ContentDialog.SecondaryButtonStyle +P:Windows.UI.Xaml.Controls.ContentDialog.SecondaryButtonText +P:Windows.UI.Xaml.Controls.ContentPresenter.Background +P:Windows.UI.Xaml.Controls.ContentPresenter.BackgroundTransition +P:Windows.UI.Xaml.Controls.ContentPresenter.BorderBrush +P:Windows.UI.Xaml.Controls.ContentPresenter.Content +P:Windows.UI.Xaml.Controls.ContentPresenter.ContentTemplate +P:Windows.UI.Xaml.Controls.ContentPresenter.Foreground +P:Windows.UI.Xaml.Controls.Control.Background +P:Windows.UI.Xaml.Controls.Control.BorderBrush +P:Windows.UI.Xaml.Controls.DatePicker.Header +P:Windows.UI.Xaml.Controls.DatePicker.HeaderTemplate +P:Windows.UI.Xaml.Controls.DatePicker.SelectedDate +P:Windows.UI.Xaml.Controls.Flyout.FlyoutPresenterStyle +P:Windows.UI.Xaml.Controls.Grid.BorderBrush +P:Windows.UI.Xaml.Controls.GroupStyle.ContainerStyle +P:Windows.UI.Xaml.Controls.GroupStyle.ContainerStyleSelector +P:Windows.UI.Xaml.Controls.GroupStyle.HeaderContainerStyle +P:Windows.UI.Xaml.Controls.GroupStyle.HeaderTemplate +P:Windows.UI.Xaml.Controls.GroupStyle.HeaderTemplateSelector +P:Windows.UI.Xaml.Controls.HandwritingView.PlacementTarget +P:Windows.UI.Xaml.Controls.Hub.Header +P:Windows.UI.Xaml.Controls.Hub.HeaderTemplate +P:Windows.UI.Xaml.Controls.Hub.SemanticZoomOwner +P:Windows.UI.Xaml.Controls.HubSection.Header +P:Windows.UI.Xaml.Controls.HubSection.HeaderTemplate +P:Windows.UI.Xaml.Controls.IScrollAnchorProvider.CurrentAnchor +P:Windows.UI.Xaml.Controls.IconElement.Foreground +P:Windows.UI.Xaml.Controls.IconSource.Foreground +P:Windows.UI.Xaml.Controls.IconSourceElement.IconSource +P:Windows.UI.Xaml.Controls.InkToolbarCustomPenButton.ConfigurationContent +P:Windows.UI.Xaml.Controls.InkToolbarCustomToolButton.ConfigurationContent +P:Windows.UI.Xaml.Controls.ItemsControl.ItemContainerStyle +P:Windows.UI.Xaml.Controls.ItemsControl.ItemTemplate +P:Windows.UI.Xaml.Controls.ItemsControl.Items +P:Windows.UI.Xaml.Controls.ItemsControl.ItemsPanelRoot +P:Windows.UI.Xaml.Controls.ItemsControl.ItemsSource +P:Windows.UI.Xaml.Controls.ItemsPresenter.Footer +P:Windows.UI.Xaml.Controls.ItemsPresenter.FooterTemplate +P:Windows.UI.Xaml.Controls.ItemsPresenter.Header +P:Windows.UI.Xaml.Controls.ItemsPresenter.HeaderTemplate +P:Windows.UI.Xaml.Controls.ListPickerFlyout.ItemTemplate +P:Windows.UI.Xaml.Controls.ListPickerFlyout.ItemsSource +P:Windows.UI.Xaml.Controls.ListPickerFlyout.SelectedItem +P:Windows.UI.Xaml.Controls.ListPickerFlyout.SelectedValue +P:Windows.UI.Xaml.Controls.ListViewBase.Footer +P:Windows.UI.Xaml.Controls.ListViewBase.FooterTemplate +P:Windows.UI.Xaml.Controls.ListViewBase.Header +P:Windows.UI.Xaml.Controls.ListViewBase.HeaderTemplate +P:Windows.UI.Xaml.Controls.ListViewBase.SemanticZoomOwner +P:Windows.UI.Xaml.Controls.Maps.MapControl.Region +P:Windows.UI.Xaml.Controls.MediaElement.AudioStreamIndex +P:Windows.UI.Xaml.Controls.MediaElement.Source +P:Windows.UI.Xaml.Controls.MediaPlayerElement.Source +P:Windows.UI.Xaml.Controls.MenuFlyoutItem.Command +P:Windows.UI.Xaml.Controls.MenuFlyoutItem.CommandParameter +P:Windows.UI.Xaml.Controls.MenuFlyoutItem.KeyboardAcceleratorTextOverride +P:Windows.UI.Xaml.Controls.MenuFlyoutSubItem.Items +P:Windows.UI.Xaml.Controls.NavigationView.MenuItemContainerStyle +P:Windows.UI.Xaml.Controls.NavigationView.MenuItemTemplate +P:Windows.UI.Xaml.Controls.NavigationView.MenuItemsSource +P:Windows.UI.Xaml.Controls.NavigationView.PaneFooter +P:Windows.UI.Xaml.Controls.NavigationView.PaneHeader +P:Windows.UI.Xaml.Controls.NavigationView.PaneToggleButtonStyle +P:Windows.UI.Xaml.Controls.NavigationView.SelectedItem +P:Windows.UI.Xaml.Controls.NavigationViewItem.Icon +P:Windows.UI.Xaml.Controls.Page.BottomAppBar +P:Windows.UI.Xaml.Controls.Page.TopAppBar +P:Windows.UI.Xaml.Controls.Panel.Background +P:Windows.UI.Xaml.Controls.Panel.BackgroundTransition +P:Windows.UI.Xaml.Controls.ParallaxView.Child +P:Windows.UI.Xaml.Controls.PasswordBox.Description +P:Windows.UI.Xaml.Controls.PasswordBox.Header +P:Windows.UI.Xaml.Controls.PasswordBox.HeaderTemplate +P:Windows.UI.Xaml.Controls.PasswordBox.InputScope +P:Windows.UI.Xaml.Controls.PasswordBox.SelectionFlyout +P:Windows.UI.Xaml.Controls.PasswordBox.SelectionHighlightColor +P:Windows.UI.Xaml.Controls.Pivot.LeftHeader +P:Windows.UI.Xaml.Controls.Pivot.RightHeader +P:Windows.UI.Xaml.Controls.Primitives.ButtonBase.Command +P:Windows.UI.Xaml.Controls.Primitives.ButtonBase.CommandParameter +P:Windows.UI.Xaml.Controls.Primitives.FlyoutBase.XamlRoot +P:Windows.UI.Xaml.Controls.Primitives.Selector.SelectedItem +P:Windows.UI.Xaml.Controls.Primitives.Selector.SelectedValue +P:Windows.UI.Xaml.Controls.RadioButton.GroupName +P:Windows.UI.Xaml.Controls.RatingControl.PlaceholderValue +P:Windows.UI.Xaml.Controls.RatingControl.Value +P:Windows.UI.Xaml.Controls.RelativePanel.BorderBrush +P:Windows.UI.Xaml.Controls.RichEditBox.Description +P:Windows.UI.Xaml.Controls.RichEditBox.Header +P:Windows.UI.Xaml.Controls.RichEditBox.HeaderTemplate +P:Windows.UI.Xaml.Controls.RichEditBox.InputScope +P:Windows.UI.Xaml.Controls.RichEditBox.SelectionFlyout +P:Windows.UI.Xaml.Controls.RichEditBox.SelectionHighlightColor +P:Windows.UI.Xaml.Controls.RichEditBox.SelectionHighlightColorWhenNotFocused +P:Windows.UI.Xaml.Controls.RichTextBlock.Foreground +P:Windows.UI.Xaml.Controls.RichTextBlock.SelectionEnd +P:Windows.UI.Xaml.Controls.RichTextBlock.SelectionFlyout +P:Windows.UI.Xaml.Controls.RichTextBlock.SelectionHighlightColor +P:Windows.UI.Xaml.Controls.RichTextBlock.SelectionStart +P:Windows.UI.Xaml.Controls.ScrollViewer.CurrentAnchor +P:Windows.UI.Xaml.Controls.SettingsFlyout.HeaderBackground +P:Windows.UI.Xaml.Controls.SettingsFlyout.HeaderForeground +P:Windows.UI.Xaml.Controls.SettingsFlyout.IconSource +P:Windows.UI.Xaml.Controls.Slider.Header +P:Windows.UI.Xaml.Controls.Slider.HeaderTemplate +P:Windows.UI.Xaml.Controls.SplitButton.Command +P:Windows.UI.Xaml.Controls.SplitButton.CommandParameter +P:Windows.UI.Xaml.Controls.SplitButton.Flyout +P:Windows.UI.Xaml.Controls.SplitView.Content +P:Windows.UI.Xaml.Controls.SplitView.Pane +P:Windows.UI.Xaml.Controls.StackPanel.BorderBrush +P:Windows.UI.Xaml.Controls.SwipeItem.Command +P:Windows.UI.Xaml.Controls.SwipeItem.CommandParameter +P:Windows.UI.Xaml.Controls.SwipeItem.IconSource +P:Windows.UI.Xaml.Controls.TextBlock.Foreground +P:Windows.UI.Xaml.Controls.TextBlock.SelectionEnd +P:Windows.UI.Xaml.Controls.TextBlock.SelectionFlyout +P:Windows.UI.Xaml.Controls.TextBlock.SelectionHighlightColor +P:Windows.UI.Xaml.Controls.TextBlock.SelectionStart +P:Windows.UI.Xaml.Controls.TextBox.Description +P:Windows.UI.Xaml.Controls.TextBox.Header +P:Windows.UI.Xaml.Controls.TextBox.HeaderTemplate +P:Windows.UI.Xaml.Controls.TextBox.InputScope +P:Windows.UI.Xaml.Controls.TextBox.SelectionFlyout +P:Windows.UI.Xaml.Controls.TextBox.SelectionHighlightColorWhenNotFocused +P:Windows.UI.Xaml.Controls.TimePicker.Header +P:Windows.UI.Xaml.Controls.TimePicker.HeaderTemplate +P:Windows.UI.Xaml.Controls.TimePicker.SelectedTime +P:Windows.UI.Xaml.Controls.ToolTip.PlacementRect +P:Windows.UI.Xaml.Controls.ToolTip.PlacementTarget +P:Windows.UI.Xaml.Controls.TreeView.ItemContainerStyle +P:Windows.UI.Xaml.Controls.TreeView.ItemTemplate +P:Windows.UI.Xaml.Controls.TreeView.ItemsSource +P:Windows.UI.Xaml.Controls.TreeViewItem.ItemsSource +P:Windows.UI.Xaml.Data.Binding.ConverterParameter +P:Windows.UI.Xaml.Data.Binding.RelativeSource +P:Windows.UI.Xaml.Data.Binding.TargetNullValue +P:Windows.UI.Xaml.Data.ICollectionView.CurrentItem +P:Windows.UI.Xaml.Documents.Glyphs.Fill +P:Windows.UI.Xaml.Documents.Glyphs.FontUri +P:Windows.UI.Xaml.Documents.Hyperlink.NavigateUri +P:Windows.UI.Xaml.Documents.TextElement.XamlRoot +P:Windows.UI.Xaml.DragEventArgs.DragUIOverride +P:Windows.UI.Xaml.ElementFactoryGetArgs.Parent +P:Windows.UI.Xaml.ElementFactoryRecycleArgs.Parent +P:Windows.UI.Xaml.FrameworkElement.Parent +P:Windows.UI.Xaml.FrameworkElement.Style +P:Windows.UI.Xaml.Input.FindNextElementOptions.SearchRoot +P:Windows.UI.Xaml.Input.FocusManagerGotFocusEventArgs.CorrelationId +P:Windows.UI.Xaml.Input.FocusManagerLostFocusEventArgs.CorrelationId +P:Windows.UI.Xaml.Input.GettingFocusEventArgs.CorrelationId +P:Windows.UI.Xaml.Input.KeyboardAccelerator.ScopeOwner +P:Windows.UI.Xaml.Input.LosingFocusEventArgs.CorrelationId +P:Windows.UI.Xaml.Markup.IXamlType.ContentProperty +P:Windows.UI.Xaml.Markup.IXamlType.ItemType +P:Windows.UI.Xaml.Markup.IXamlType.KeyType +P:Windows.UI.Xaml.Media.Animation.BeginStoryboard.Storyboard +P:Windows.UI.Xaml.Media.Animation.ColorAnimation.By +P:Windows.UI.Xaml.Media.Animation.ColorAnimation.From +P:Windows.UI.Xaml.Media.Animation.ColorAnimation.To +P:Windows.UI.Xaml.Media.Animation.ColorKeyFrame.KeyTime +P:Windows.UI.Xaml.Media.Animation.DoubleAnimation.By +P:Windows.UI.Xaml.Media.Animation.DoubleAnimation.From +P:Windows.UI.Xaml.Media.Animation.DoubleAnimation.To +P:Windows.UI.Xaml.Media.Animation.DoubleKeyFrame.KeyTime +P:Windows.UI.Xaml.Media.Animation.ObjectKeyFrame.KeyTime +P:Windows.UI.Xaml.Media.Animation.ObjectKeyFrame.Value +P:Windows.UI.Xaml.Media.Animation.PointAnimation.By +P:Windows.UI.Xaml.Media.Animation.PointAnimation.EasingFunction +P:Windows.UI.Xaml.Media.Animation.PointAnimation.From +P:Windows.UI.Xaml.Media.Animation.PointAnimation.To +P:Windows.UI.Xaml.Media.Animation.PointKeyFrame.KeyTime +P:Windows.UI.Xaml.Media.Brush.RelativeTransform +P:Windows.UI.Xaml.Media.GeneralTransform.Inverse +P:Windows.UI.Xaml.Media.PlaneProjection.ProjectionMatrix +P:Windows.UI.Xaml.Media.RectangleGeometry.Rect +P:Windows.UI.Xaml.Media.TimelineMarker.Time +P:Windows.UI.Xaml.Navigation.NavigationEventArgs.Parameter +P:Windows.UI.Xaml.Setter.Property +P:Windows.UI.Xaml.Shapes.Polygon.Points +P:Windows.UI.Xaml.Shapes.Polyline.Points +P:Windows.UI.Xaml.Shapes.Shape.Fill +P:Windows.UI.Xaml.Shapes.Shape.Stroke +P:Windows.UI.Xaml.Style.BasedOn +P:Windows.UI.Xaml.UIElement.CacheMode +P:Windows.UI.Xaml.UIElement.Clip +P:Windows.UI.Xaml.UIElement.ContextFlyout +P:Windows.UI.Xaml.UIElement.RenderTransform +P:Windows.UI.Xaml.UIElement.Transform3D +P:Windows.UI.Xaml.UIElement.XamlRoot +P:Windows.UI.Xaml.VisualStateGroup.CurrentState +P:Windows.Web.Http.Headers.HttpContentHeaderCollection.ContentDisposition +P:Windows.Web.Http.Headers.HttpContentHeaderCollection.ContentLength +P:Windows.Web.Http.Headers.HttpContentHeaderCollection.ContentLocation +P:Windows.Web.Http.Headers.HttpContentHeaderCollection.ContentMD5 +P:Windows.Web.Http.Headers.HttpContentHeaderCollection.ContentRange +P:Windows.Web.Http.Headers.HttpContentHeaderCollection.ContentType +P:Windows.Web.Http.Headers.HttpContentHeaderCollection.Expires +P:Windows.Web.Http.Headers.HttpContentHeaderCollection.LastModified +P:Windows.Web.Http.Headers.HttpRequestHeaderCollection.Authorization +P:Windows.Web.Http.Headers.HttpRequestHeaderCollection.Date +P:Windows.Web.Http.Headers.HttpRequestHeaderCollection.Host +P:Windows.Web.Http.Headers.HttpRequestHeaderCollection.IfModifiedSince +P:Windows.Web.Http.Headers.HttpRequestHeaderCollection.IfUnmodifiedSince +P:Windows.Web.Http.Headers.HttpRequestHeaderCollection.MaxForwards +P:Windows.Web.Http.Headers.HttpRequestHeaderCollection.ProxyAuthorization +P:Windows.Web.Http.Headers.HttpRequestHeaderCollection.Referer +P:Windows.Web.Http.Headers.HttpResponseHeaderCollection.Age +P:Windows.Web.Http.Headers.HttpResponseHeaderCollection.Date +P:Windows.Web.Http.Headers.HttpResponseHeaderCollection.Location +P:Windows.Web.Http.Headers.HttpResponseHeaderCollection.RetryAfter +P:Windows.Web.Http.HttpCookie.Expires +P:Windows.Web.Http.HttpGetInputStreamResult.ExtendedError +P:Windows.Web.Http.HttpGetStringResult.ExtendedError +P:Windows.Web.Http.HttpRequestResult.ExtendedError +P:Windows.Web.Syndication.SyndicationFeed.FirstUri +P:Windows.Web.Syndication.SyndicationFeed.IconUri +P:Windows.Web.Syndication.SyndicationFeed.LastUri +P:Windows.Web.Syndication.SyndicationFeed.NextUri +P:Windows.Web.Syndication.SyndicationFeed.PreviousUri +P:Windows.Web.Syndication.SyndicationItem.EditMediaUri +P:Windows.Web.Syndication.SyndicationItem.EditUri diff --git a/tools/dynwinrt-codegen/scripts/extract-null-results.py b/tools/dynwinrt-codegen/scripts/extract-null-results.py new file mode 100644 index 00000000..987a305d --- /dev/null +++ b/tools/dynwinrt-codegen/scripts/extract-null-results.py @@ -0,0 +1,250 @@ +#!/usr/bin/env python3 +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +"""Extract the Windows SDK members whose documented result can be null. + +WinRT metadata carries no nullability, so dynwinrt-codegen types most outputs +as non-null and keeps `| None` for members whose documentation says that the +result can be null. This script derives that list from MicrosoftDocs/winrt-api +at a pinned commit and writes only doc comment IDs (api-ids), never +documentation text, to api-docs/windows-null-results.txt. + +A method (`M:`) or property (`P:`) is listed when +- a sentence of its `## -returns` or `## -property-value` section says that + the result can be null (for an asynchronous method: its completed result), or +- a sentence of its `## -remarks` section says that the member itself + ("this method", "this property", "it", or the member's name) returns or is + null. +Sentences that negate null, that describe null arguments, or that describe an +object holding a null value are ignored, and so are Boolean results, attached +properties, constructors and members of generic types. Reviewed corrections +from api-docs/windows-null-results.overrides.txt are applied last. + +The documentation is read from git objects, without a working tree: + + python extract-null-results.py # fetch the pinned commit + python extract-null-results.py --repo DIR # reuse a local clone + python extract-null-results.py --commit SHA # move the pin +""" + +from __future__ import annotations + +import argparse +import fnmatch +import re +import subprocess +import sys +import tempfile +from collections import Counter +from collections.abc import Iterator +from pathlib import Path + +REPOSITORY = "https://github.com/MicrosoftDocs/winrt-api" +COMMIT = "8448d5eecfbc2ed903f659f350841dcb4888bc8b" +CODEGEN = Path(__file__).resolve().parent.parent +OUTPUT = CODEGEN / "api-docs" / "windows-null-results.txt" +OVERRIDES = CODEGEN / "api-docs" / "windows-null-results.overrides.txt" + +NULL = re.compile(r"\bnull(?:ptr)?\b(?![- ](?:terminat|character|char\b))", re.I) +NEGATION = re.compile( + r"\b(?:never|not|cannot|can't|won't|doesn't|does not|isn't|is not|will not|must not|may not)" + r"\s+(?:be\s+|is\s+|return\s+|returns\s+)?(?:a\s+)?null\b|\bnon-?null\b", + re.I, +) +ARGUMENT = re.compile( + r"\bnull\s+(?:was|is|were|are)\s+passed\b" + r"|\bpass(?:es|ed|ing)?\s+(?:in\s+)?(?:a\s+)?null\b" + r"|\bother than\s+null\b" + r"|\b(?:argument|parameter)\s+(?:is|was|are)\s+null\b" + r"|\bnull\s+(?:for|as)\s+(?:the\s+)?\w+\s+(?:argument|parameter)\b" + r"|\bexception\b[^.]*\bset\s+to\s+null\b|\bset\s+to\s+null\b[^.]*\bexception\b", + re.I, +) +HOLDER = re.compile( + r"\b(?:with|holds?|holding|contains?|containing|supports?|has|have)\s+(?:a|an)\s+" + r"(?:json\s+)?null\s+value\b", + re.I, +) +BOOLEAN = re.compile(r"\A\W*(?:true|false)\b", re.I) +REMARKS_GAP = r"(?:(?!\b(?:when|if|unless|called|until|whether|and|but)\b)[^.;,]){0,40}?" +REMARKS_VERB = ( + r"\b(?:returns?|is|will\s+be|may\s+be|can\s+be|could\s+be|might\s+be|is\s+set\s+to)" + r"\s+(?:a\s+|an\s+|the\s+)?null(?:ptr)?\b" +) + + +def git(repo: Path, *args: str) -> str: + return subprocess.run( + ["git", "-C", str(repo), *args], check=True, capture_output=True, text=True + ).stdout + + +def ensure_commit(repo: Path, commit: str) -> None: + if not (repo / ".git").exists(): + repo.mkdir(parents=True, exist_ok=True) + git(repo, "init", "--quiet") + present = subprocess.run( + ["git", "-C", str(repo), "cat-file", "-e", f"{commit}^{{commit}}"], + capture_output=True, + ) + if present.returncode != 0: + git(repo, "fetch", "--quiet", "--depth", "1", REPOSITORY, commit) + + +def documents(repo: Path, commit: str) -> Iterator[str]: + listing = git(repo, "ls-tree", "-r", "-z", commit) + blobs = [] + for entry in listing.split("\0"): + if not entry: + continue + info, path = entry.split("\t", 1) + _, kind, sha = info.split() + if kind == "blob" and path.endswith(".md"): + blobs.append(sha) + with subprocess.Popen( + ["git", "-C", str(repo), "cat-file", "--batch"], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + ) as batch: + assert batch.stdin is not None and batch.stdout is not None + for sha in blobs: + batch.stdin.write(sha.encode() + b"\n") + batch.stdin.flush() + size = int(batch.stdout.readline().split()[2]) + data = batch.stdout.read(size) + batch.stdout.read(1) + yield data.decode("utf-8", "replace").replace("\r\n", "\n") + batch.stdin.close() + + +def front_matter(text: str) -> dict[str, str]: + match = re.match(r"\A---[ \t]*\n(.*?)\n---", text, re.S) + fields = {} + for line in match.group(1).splitlines() if match else []: + key, _, value = line.strip().partition(":") + if key.startswith("-"): + fields[key[1:]] = value.strip() + return fields + + +def section(text: str, name: str) -> str: + match = re.search(rf"^## -{re.escape(name)}[ \t]*\n(.*?)(?=^## -|\Z)", text, re.M | re.S) + return plain(match.group(1)) if match else "" + + +def plain(markdown: str) -> str: + text = re.sub(r"", " ", markdown, flags=re.S) + text = re.sub(r"!?\[([^\]]*)\]\([^)]*\)", r"\1", text) + text = re.sub(r"\[!(?:NOTE|IMPORTANT|TIP|WARNING|CAUTION)\]", " ", text) + text = re.sub(r"(?m)^\s*>\s?", " ", text) + text = re.sub(r"[*_`]", "", text) + return " ".join(text.split()) + + +def sentences(text: str) -> list[str]: + return [sentence for sentence in re.split(r"(?<=[.!?])\s+", text) if NULL.search(sentence)] + + +def states_null(sentence: str) -> bool: + return not (NEGATION.search(sentence) or ARGUMENT.search(sentence) or HOLDER.search(sentence)) + + +def result_is_nullable(result: str) -> bool: + if not result or BOOLEAN.match(result): + return False + return any(states_null(sentence) for sentence in sentences(result)) + + +def remarks_say_null(remarks: str, member: str) -> bool: + subject = ( + r"(?:\bthis\s+(?:method|property|function|call|operation)" + r"|\bthe\s+(?:method|property|call|operation)" + rf"|\bit|\b{re.escape(member)})\b" + ) + claim = re.compile(subject + REMARKS_GAP + REMARKS_VERB, re.I) + returns = re.compile(rf"\bif\s+(?:this|it|{re.escape(member)})\s+returns\s+null\b", re.I) + return any( + (claim.search(sentence) or returns.search(sentence)) and states_null(sentence) + for sentence in sentences(remarks) + ) + + +def member_name(api_id: str) -> str: + return api_id[2:].split("(", 1)[0].rsplit(".", 1)[-1] + + +def extract(repo: Path, commit: str) -> tuple[set[str], set[str], Counter[str]]: + documented: set[str] = set() + nullable: set[str] = set() + sources: Counter[str] = Counter() + for text in documents(repo, commit): + fields = front_matter(text) + api_id = fields.get("api-id", "") + api_type = fields.get("api-type", "") + if not api_id.startswith(("M:", "P:")) or api_type in { + "winrt attachedproperty", + "winrt constructor", + }: + continue + if "#ctor" in api_id or "`" in api_id.split("(", 1)[0]: + continue + documented.add(api_id) + result = section(text, "returns" if api_id.startswith("M:") else "property-value") + if result_is_nullable(result): + sources["returns" if api_id.startswith("M:") else "property-value"] += 1 + nullable.add(api_id) + elif remarks_say_null(section(text, "remarks"), member_name(api_id)): + sources["remarks"] += 1 + nullable.add(api_id) + return documented, nullable, sources + + +def apply_overrides(documented: set[str], nullable: set[str], sources: Counter[str]) -> set[str]: + result = set(nullable) + for number, raw in enumerate(OVERRIDES.read_text(encoding="utf-8").splitlines(), 1): + entry = raw.split("#", 1)[0].strip() + if not entry: + continue + sign, pattern = entry[0], entry[1:].strip() + if sign not in "+-" or not pattern: + sys.exit(f"{OVERRIDES.name}:{number}: expected '+api-id' or '-api-id'") + matches = {api_id for api_id in documented if fnmatch.fnmatchcase(api_id, pattern)} + if not matches: + sys.exit(f"{OVERRIDES.name}:{number}: {pattern} matches no documented member") + if sign == "+": + sources["override additions"] += len(matches - result) + result |= matches + else: + sources["override removals"] += len(matches & result) + result -= matches + return result + + +def main() -> None: + parser = argparse.ArgumentParser(description=__doc__.split("\n\n", 1)[0]) + parser.add_argument("--commit", default=COMMIT, help="winrt-api commit to read") + parser.add_argument("--repo", type=Path, help="local winrt-api clone to reuse") + arguments = parser.parse_args() + + with tempfile.TemporaryDirectory(prefix="winrt-api-") as scratch: + repo = arguments.repo or Path(scratch) + ensure_commit(repo, arguments.commit) + documented, nullable, sources = extract(repo, arguments.commit) + members = sorted(apply_overrides(documented, nullable, sources)) + + header = [ + "# Windows SDK members whose documented result can be null: a method's", + "# return value (for asynchronous methods, the completed result) or a", + "# property's value. dynwinrt-codegen keeps `| None` on these outputs.", + f"# Source: {REPOSITORY} at commit {arguments.commit}", + "# Generated by scripts/extract-null-results.py; do not edit. Reviewed", + "# corrections belong in windows-null-results.overrides.txt.", + ] + OUTPUT.write_text("\n".join(header + members) + "\n", encoding="utf-8", newline="\n") + print(f"{len(members)} members from {len(documented)} documented methods and properties") + for source, count in sources.most_common(): + print(f" {source}: {count}") + + +if __name__ == "__main__": + main() diff --git a/tools/dynwinrt-codegen/src/codegen/winrt/python/method.rs b/tools/dynwinrt-codegen/src/codegen/winrt/python/method.rs index caf2071b..52484e00 100644 --- a/tools/dynwinrt-codegen/src/codegen/winrt/python/method.rs +++ b/tools/dynwinrt-codegen/src/codegen/winrt/python/method.rs @@ -786,7 +786,7 @@ pub(crate) fn generate_method_body( if method.is_property_getter && in_params.is_empty() { let prop_name = to_snake_case(method.name.strip_prefix("get_").unwrap_or(&method.name)); let py_return = return_type - .map(|typ| py_property_type(typ, AnnotationSurface::Runtime, context)) + .map(|typ| py_property_type(method, typ, AnnotationSurface::Runtime, context)) .unwrap_or_else(|| "None".to_string()); out.push_str(" @_property\n"); out.push_str(&format!(" def {}(self) -> {}:\n", prop_name, py_return)); diff --git a/tools/dynwinrt-codegen/src/codegen/winrt/python/nullability.rs b/tools/dynwinrt-codegen/src/codegen/winrt/python/nullability.rs index 7077b9a0..6a9de1fc 100644 --- a/tools/dynwinrt-codegen/src/codegen/winrt/python/nullability.rs +++ b/tools/dynwinrt-codegen/src/codegen/winrt/python/nullability.rs @@ -12,9 +12,10 @@ //! `| None` is appended. use crate::codegen::winrt::shared::imports::ireference_inner_type; -use crate::meta::MethodMeta; +use crate::meta::{ElementAccess, MethodMeta}; use crate::types::TypeMeta; +use super::collections::CollectionKind; use super::naming::PythonProjectionContext; /// The generated artifact an annotation is rendered into. @@ -50,6 +51,52 @@ pub(crate) enum OutputPosition { Activation, } +/// The collection holding an element. Whether a reference-type element admits +/// `None` depends only on this; see [`element_admits_none`]. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub(crate) enum ElementContainer { + /// `IIterable`, `IIterator`, `IVectorView`, `IMapView` or `IKeyValuePair`. + View, + /// `IVector`, `IMap` or their observable forms. + Mutable, + /// An array returned by a member that does not read collection elements. + Array, +} + +impl ElementContainer { + pub(crate) fn of(kind: CollectionKind) -> Self { + match kind { + CollectionKind::MutableSequence | CollectionKind::MutableMapping => Self::Mutable, + CollectionKind::Iterable + | CollectionKind::Iterator + | CollectionKind::Sequence + | CollectionKind::Mapping + | CollectionKind::KeyValuePair => Self::View, + } + } +} + +impl From for ElementContainer { + fn from(access: ElementAccess) -> Self { + match access { + ElementAccess::ReadOnly => Self::View, + ElementAccess::Mutable => Self::Mutable, + } + } +} + +/// The collection element rule: anyone can store null in a mutable collection, +/// so its reference-type elements admit `None`. Views, iterators and arrays +/// are typed like other outputs. Positions that read elements inherit the +/// rule of their owning collection; a view obtained from a mutable collection +/// follows the view rule. +pub(crate) fn element_admits_none(container: ElementContainer) -> bool { + match container { + ElementContainer::Mutable => true, + ElementContainer::View | ElementContainer::Array => false, + } +} + /// A position plus the facts about the member producing the value. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub(crate) struct OutputSite { @@ -57,6 +104,12 @@ pub(crate) struct OutputSite { /// The member follows the `Try*` pattern, so a null result is part of /// its contract ("not found", "could not parse"). pub(crate) try_method: bool, + /// The Windows SDK documentation says the member's result can be null. + pub(crate) documented_null: bool, + /// The collection holding the value: set on collection elements, and on + /// the results of members that read elements of the collection declaring + /// them (`get_at`, `lookup`, `current`, ...). + pub(crate) container: Option, } impl OutputSite { @@ -64,6 +117,8 @@ impl OutputSite { Self { position, try_method: false, + documented_null: false, + container: None, } } @@ -71,15 +126,30 @@ impl OutputSite { Self { position, try_method: is_try_method(method), + documented_null: method.documented_null_result, + container: method.element_access.map(ElementContainer::from), } } - /// The site of a value nested in this one, such as an async result or a - /// collection element. Member facts carry over; the policy decides where - /// they apply. + /// An element read from a collection of the given kind. + pub(crate) fn element_of(container: ElementContainer) -> Self { + Self::of(OutputPosition::CollectionElement).element_in(container) + } + + /// The site of a value nested in this one, such as an async result. + /// Member facts carry over; the policy decides where they apply. pub(crate) fn nested(self, position: OutputPosition) -> Self { Self { position, ..self } } + + /// The site of an element held by `container`, nested in this value. + pub(crate) fn element_in(self, container: ElementContainer) -> Self { + Self { + position: OutputPosition::CollectionElement, + container: Some(container), + ..self + } + } } /// `TryParse`, `TryGetItemAsync`, ...: the CLR name is `Try` followed by an @@ -136,7 +206,9 @@ fn stub_output_admits_none( site: OutputSite, context: &PythonProjectionContext, ) -> bool { - use OutputPosition::{Activation, AsyncResult, CallbackParam, OutParam, Return}; + use OutputPosition::{ + Activation, AsyncResult, CallbackParam, CollectionElement, OutParam, Property, Return, + }; // `Object` positions are frequently null, e.g. the arguments of a // `TypedEventHandler`. @@ -147,8 +219,19 @@ fn stub_output_admits_none( if context.is_delegate_type(typ) { return site.position != CallbackParam; } - // `Try*` members report "not found" through a null result. - site.try_method && matches!(site.position, Return | OutParam | AsyncResult | Activation) + // `Try*` members report "not found" through a null result, and the + // Windows SDK documentation names the other members that return null. + let member_result = matches!( + site.position, + Return | OutParam | Property | AsyncResult | Activation + ); + if member_result && (site.try_method || site.documented_null) { + return true; + } + // Collection elements, including the results of `get_at`, `lookup` and + // `current`, follow the collection holding them. + matches!(site.position, CollectionElement | Return | Property) + && site.container.is_some_and(element_admits_none) } #[cfg(test)] @@ -181,6 +264,17 @@ mod tests { OutputSite { position: OutputPosition::AsyncResult, try_method: true, + documented_null: false, + container: None, + } + ); + assert_eq!( + site.element_in(ElementContainer::Array), + OutputSite { + position: OutputPosition::CollectionElement, + try_method: true, + documented_null: false, + container: Some(ElementContainer::Array), } ); } @@ -268,24 +362,25 @@ mod tests { } } + const MEMBER_RESULTS: [OutputPosition; 5] = [ + OutputPosition::Return, + OutputPosition::OutParam, + OutputPosition::Property, + OutputPosition::AsyncResult, + OutputPosition::Activation, + ]; + #[test] fn try_members_keep_none_on_their_results_only() { let try_get = method("TryGetItemAsync"); for position in POSITIONS { - let expected = matches!( - position, - OutputPosition::Return - | OutputPosition::OutParam - | OutputPosition::AsyncResult - | OutputPosition::Activation - ); assert_eq!( admits( &widget(), OutputSite::for_method(&try_get, position), AnnotationSurface::Stub ), - expected, + MEMBER_RESULTS.contains(&position), "{position:?}" ); } @@ -295,4 +390,75 @@ mod tests { AnnotationSurface::Stub )); } + + #[test] + fn documented_null_members_keep_none_on_their_results_only() { + let get_default = MethodMeta { + documented_null_result: true, + ..method("GetDefault") + }; + for position in POSITIONS { + assert_eq!( + admits( + &widget(), + OutputSite::for_method(&get_default, position), + AnnotationSurface::Stub + ), + MEMBER_RESULTS.contains(&position), + "{position:?}" + ); + } + let site = OutputSite::for_method(&get_default, OutputPosition::Return); + assert!(!admits( + &widget(), + site.element_in(ElementContainer::View), + AnnotationSurface::Stub + )); + } + + #[test] + fn collection_elements_follow_the_mutability_of_their_collection() { + let stub = AnnotationSurface::Stub; + assert!(admits( + &widget(), + OutputSite::element_of(ElementContainer::Mutable), + stub + )); + for container in [ElementContainer::View, ElementContainer::Array] { + let site = OutputSite::element_of(container); + assert!(!admits(&widget(), site, stub), "{container:?}"); + assert!(admits(&TypeMeta::Object, site, stub)); + assert!(admits(&nullable_u32(), site, stub)); + assert!(!admits(&TypeMeta::String, site, stub)); + } + for (access, expected) in [ + (ElementAccess::Mutable, true), + (ElementAccess::ReadOnly, false), + ] { + let get_at = MethodMeta { + element_access: Some(access), + ..method("GetAt") + }; + for position in [OutputPosition::Return, OutputPosition::Property] { + assert_eq!( + admits(&widget(), OutputSite::for_method(&get_at, position), stub), + expected, + "{access:?} at {position:?}" + ); + } + assert!(!admits( + &widget(), + OutputSite::for_method(&get_at, OutputPosition::AsyncProgress), + stub + )); + } + assert_eq!( + ElementContainer::of(CollectionKind::MutableSequence), + ElementContainer::Mutable + ); + assert_eq!( + ElementContainer::of(CollectionKind::KeyValuePair), + ElementContainer::View + ); + } } diff --git a/tools/dynwinrt-codegen/src/codegen/winrt/python/stub_helpers.rs b/tools/dynwinrt-codegen/src/codegen/winrt/python/stub_helpers.rs index 853b36d5..37ba7e3f 100644 --- a/tools/dynwinrt-codegen/src/codegen/winrt/python/stub_helpers.rs +++ b/tools/dynwinrt-codegen/src/codegen/winrt/python/stub_helpers.rs @@ -9,7 +9,7 @@ use crate::types::{FieldMeta, TypeMeta}; use super::naming::{PythonProjectionContext, PythonSymbol, STRUCT_SYMBOLS, to_snake_case}; use super::native_types::{FoundationType, foundation_type}; -use super::nullability::AnnotationSurface; +use super::nullability::{AnnotationSurface, ElementContainer}; use super::structs::{py_struct_field_read_type, py_struct_field_type}; use super::type_helpers::{ method_pydoc_with_indent, py_collection_item_type, py_delegate_callable_type, @@ -276,7 +276,7 @@ pub(super) fn emit_method_stub_named( if method.is_property_getter && in_params.is_empty() { let prop_name = to_snake_case(method.name.strip_prefix("get_").unwrap_or(&method.name)); let py_return = return_type - .map(|typ| py_property_type(typ, AnnotationSurface::Stub, context)) + .map(|typ| py_property_type(method, typ, AnnotationSurface::Stub, context)) .unwrap_or_else(|| "None".to_string()); out.push_str(&format!("{indent}@builtins.property\n")); emit_documented_stub( @@ -328,16 +328,21 @@ pub(super) fn emit_method_stub_named( } else { format!("self, {}", py_params) }; - // `append` takes the projected input annotation, which can differ from - // the element annotation of the MutableSequence base: WinRT vectors - // may reject null on mutation, while `Object` elements are read back - // as `DynWinRTValue | None`. Empty structural protocols can make mypy - // consider the override compatible. + // `append` takes the projected input annotation, while the + // MutableSequence base reads elements back as `T | None`: WinRT + // vectors may reject null on mutation, yet anyone can store null in + // them. Empty structural protocols can make mypy consider the + // override compatible. let override_ignore = if overrides_mutable_sequence && method_name == "append" && in_params.first().is_some_and(|param| { py_param_type_safe(¶m.typ, context) - != py_collection_item_type(¶m.typ, AnnotationSurface::Stub, context) + != py_collection_item_type( + ¶m.typ, + ElementContainer::Mutable, + AnnotationSurface::Stub, + context, + ) }) { " # type: ignore[override, unused-ignore]" } else { diff --git a/tools/dynwinrt-codegen/src/codegen/winrt/python/stubs.rs b/tools/dynwinrt-codegen/src/codegen/winrt/python/stubs.rs index 15baf2ea..3dcd6d24 100644 --- a/tools/dynwinrt-codegen/src/codegen/winrt/python/stubs.rs +++ b/tools/dynwinrt-codegen/src/codegen/winrt/python/stubs.rs @@ -25,11 +25,11 @@ use crate::codegen::winrt::shared::structs::{ }; use super::collections::{ - CollectionKind, abc_name, class_interface, interface_kind, observable_vector_identity, + CollectionKind, class_interface, interface_kind, observable_vector_identity, }; use super::naming::{PythonProjectionContext, PythonSupportSymbol, is_py_reserved, to_snake_case}; use super::native_types::foundation_type; -use super::nullability::AnnotationSurface; +use super::nullability::{AnnotationSurface, ElementContainer}; use super::shared::reorder_getters_before_setters; use super::signature::py_dynwinrt_type; use super::stub_helpers::{ @@ -452,9 +452,9 @@ pub fn generate_interface_stub(context: &PythonProjectionContext, iface: &Interf } } - let collection_base = collection_kind.and_then(abc_name).and_then(|abc| { + let collection_base = collection_kind.and_then(|kind| { super::type_helpers::py_collection_base_type( - abc, + kind, &iface.generic_args, AnnotationSurface::Stub, context, @@ -856,10 +856,10 @@ pub fn generate_class_stub( } let collection_base = collection_iface - .zip(collection_kind.and_then(abc_name)) - .and_then(|(iface, abc)| { + .zip(collection_kind) + .and_then(|(iface, kind)| { super::type_helpers::py_collection_base_type( - abc, + kind, &iface.generic_args, AnnotationSurface::Stub, context, @@ -1045,16 +1045,14 @@ pub fn generate_class_stub( continue; } out.push('\n'); - let required_base = interface_kind(req_iface) - .and_then(abc_name) - .and_then(|abc| { - super::type_helpers::py_collection_base_type( - abc, - &req_iface.generic_args, - AnnotationSurface::Stub, - context, - ) - }); + let required_base = interface_kind(req_iface).and_then(|kind| { + super::type_helpers::py_collection_base_type( + kind, + &req_iface.generic_args, + AnnotationSurface::Stub, + context, + ) + }); if let Some(base) = required_base { out.push_str(&format!("\nclass {symbol}({base}):\n")); } else { @@ -1275,11 +1273,18 @@ fn collection_protocol_stubs( return String::new(); }; let indent = " ".repeat(indent_spaces); + // Item positions inherit the element rule of the collection that owns them. + let container = ElementContainer::of(kind); let item_type = iface .generic_args .first() .map(|typ| { - super::type_helpers::py_collection_item_type(typ, AnnotationSurface::Stub, context) + super::type_helpers::py_collection_item_type( + typ, + container, + AnnotationSurface::Stub, + context, + ) }) .unwrap_or_else(|| "object".to_string()); let item_input = iface @@ -1326,6 +1331,7 @@ fn collection_protocol_stubs( .map(|typ| { super::type_helpers::py_collection_item_type( typ, + container, AnnotationSurface::Stub, context, ) diff --git a/tools/dynwinrt-codegen/src/codegen/winrt/python/type_helpers.rs b/tools/dynwinrt-codegen/src/codegen/winrt/python/type_helpers.rs index 1b75501a..46c9ba5c 100644 --- a/tools/dynwinrt-codegen/src/codegen/winrt/python/type_helpers.rs +++ b/tools/dynwinrt-codegen/src/codegen/winrt/python/type_helpers.rs @@ -16,7 +16,8 @@ use super::naming::to_snake_case; use super::naming::{PythonProjectionContext, PythonSupportSymbol, PythonSymbol}; use super::native_types::{FoundationType, foundation_type}; use super::nullability::{ - AnnotationSurface, OutputPosition, OutputSite, may_project_none, output_admits_none, + AnnotationSurface, ElementContainer, OutputPosition, OutputSite, may_project_none, + output_admits_none, }; /// Build the Python docstring for a method body. Uses snake_case param display @@ -245,9 +246,16 @@ fn spell_member( ) }; match typ { - TypeMeta::Array(inner) if context.is_delegate_type(inner) => { - format!("list[{}]", nested(inner, OutputPosition::CollectionElement)) - } + TypeMeta::Array(inner) if context.is_delegate_type(inner) => format!( + "list[{}]", + render_output( + inner, + Spelling::Member, + array_element(site), + surface, + context + ) + ), TypeMeta::AsyncOperation(result) => { format!( "WinRTCoroutine[{}]", @@ -306,7 +314,13 @@ fn spell_value( TypeMeta::Array(inner) if matches!(inner.as_ref(), TypeMeta::U8) => "bytes".to_string(), TypeMeta::Array(inner) => format!( "list[{}]", - nested(inner, Spelling::Element, OutputPosition::CollectionElement) + render_output( + inner, + Spelling::Element, + array_element(site), + surface, + context + ) ), TypeMeta::String | TypeMeta::Char16 => "str".to_string(), TypeMeta::Guid => "UUID".to_string(), @@ -343,22 +357,22 @@ fn spell_collection( let TypeMeta::Parameterized { args, .. } = typ else { return None; }; - let abc = type_kind(typ).and_then(abc_name)?; + let kind = type_kind(typ)?; + let abc = abc_name(kind)?; + let element = site.element_in(ElementContainer::of(kind)); let elements = args .iter() - .map(|arg| { - render_output( - arg, - Spelling::Element, - site.nested(OutputPosition::CollectionElement), - surface, - context, - ) - }) + .map(|arg| render_output(arg, Spelling::Element, element, surface, context)) .collect::>(); Some(format!("{abc}[{}]", elements.join(", "))) } +/// The elements of an array filled by a member that reads collection +/// elements (`get_many`) follow that collection; other arrays are snapshots. +fn array_element(site: OutputSite) -> OutputSite { + site.element_in(site.container.unwrap_or(ElementContainer::Array)) +} + /// Pessimistic rendering for callers outside the output policy (callback /// parameters and the `IReference` input arm): every value that may /// project as `None` admits it, as on the runtime surface. @@ -378,42 +392,40 @@ pub(crate) fn py_return_type_safe( .unwrap_or_else(|| "None".to_string()) } -/// Annotation of a property getter's value. +/// Annotation of the value read by a property getter. pub(super) fn py_property_type( + getter: &MethodMeta, typ: &TypeMeta, surface: AnnotationSurface, context: &PythonProjectionContext, ) -> String { py_output_annotation( typ, - OutputSite::of(OutputPosition::Property), + OutputSite::for_method(getter, OutputPosition::Property), surface, context, ) } -/// Item, key or value type of a projected collection class. +/// Item, key or value type of a projected collection held by `container`. pub(super) fn py_collection_item_type( typ: &TypeMeta, + container: ElementContainer, surface: AnnotationSurface, context: &PythonProjectionContext, ) -> String { - py_output_annotation( - typ, - OutputSite::of(OutputPosition::CollectionElement), - surface, - context, - ) + py_output_annotation(typ, OutputSite::element_of(container), surface, context) } -/// `Sequence[T]` / `Mapping[K, V]` base of a projected collection class. +/// `Sequence[T]` / `Mapping[K, V]` base of a projected collection of `kind`. pub(super) fn py_collection_base_type( - abc: &str, + kind: CollectionKind, args: &[TypeMeta], surface: AnnotationSurface, context: &PythonProjectionContext, ) -> Option { - let item = |typ| py_collection_item_type(typ, surface, context); + let abc = abc_name(kind)?; + let item = |typ| py_collection_item_type(typ, ElementContainer::of(kind), surface, context); match args { [element] => Some(format!("{abc}[{}]", item(element))), [key, value] => Some(format!("{abc}[{}, {}]", item(key), item(value))), @@ -1002,12 +1014,71 @@ mod tests { returned(&TypeMeta::Object, stub, &context), "DynWinRTValue | None" ); - assert_eq!(py_property_type(&widget, stub, &context), "Widget"); - assert_eq!(py_collection_item_type(&widget, stub, &context), "Widget"); + let getter = method("get_Widget", vec![], widget.clone()); + assert_eq!(py_property_type(&getter, &widget, stub, &context), "Widget"); + let documented_getter = MethodMeta { + documented_null_result: true, + ..getter.clone() + }; assert_eq!( - py_collection_base_type("Sequence", std::slice::from_ref(&widget), stub, &context), + py_property_type(&documented_getter, &widget, stub, &context), + "Widget | None" + ); + assert_eq!( + py_collection_item_type(&widget, ElementContainer::View, stub, &context), + "Widget" + ); + assert_eq!( + py_collection_item_type(&widget, ElementContainer::Mutable, stub, &context), + "Widget | None" + ); + assert_eq!( + py_collection_base_type( + CollectionKind::Sequence, + std::slice::from_ref(&widget), + stub, + &context + ), Some("Sequence[Widget]".to_string()) ); + assert_eq!( + py_collection_base_type( + CollectionKind::MutableMapping, + &[TypeMeta::String, widget.clone()], + stub, + &context + ), + Some("MutableMapping[str, Widget | None]".to_string()) + ); + let vector = |piid: &str| TypeMeta::Parameterized { + namespace: "Windows.Foundation.Collections".into(), + name: "IVector`1".into(), + piid: piid.into(), + args: vec![widget.clone()], + }; + for piid in [ + crate::codegen::winrt::python::collections::IVECTOR_PIID, + crate::codegen::winrt::python::collections::IOBSERVABLE_VECTOR_PIID, + ] { + assert_eq!( + returned(&vector(piid), stub, &context), + "MutableSequence[Widget | None]" + ); + } + let get_many = MethodMeta { + params: vec![ParamMeta { + name: "items".into(), + typ: TypeMeta::Array(Box::new(widget.clone())), + direction: ParamDirection::OutFill, + }], + return_type: Some(TypeMeta::U32), + element_access: Some(crate::meta::ElementAccess::Mutable), + ..method("GetMany", vec![], TypeMeta::U32) + }; + assert_eq!( + py_method_return_type(&get_many, stub, &context), + "list[Widget | None]" + ); let get_item = method("GetItemAsync", vec![], async_of(&widget)); let try_get_item = method("TryGetItemAsync", vec![], async_of(&widget)); diff --git a/tools/dynwinrt-codegen/src/documented_nulls.rs b/tools/dynwinrt-codegen/src/documented_nulls.rs new file mode 100644 index 00000000..83117a13 --- /dev/null +++ b/tools/dynwinrt-codegen/src/documented_nulls.rs @@ -0,0 +1,324 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +//! Windows SDK members whose documented result can be null. +//! +//! WinRT metadata carries no nullability. `api-docs/windows-null-results.txt` +//! lists the doc comment IDs (`M:`/`P:` api-ids) of the Windows SDK methods +//! and properties whose documentation says the result can be null; +//! `scripts/extract-null-results.py` derives it from MicrosoftDocs/winrt-api. +//! A member is looked up by the type its documentation lists it under, its +//! CLR name, and its parameter types. + +use std::collections::HashSet; +use std::sync::LazyLock; + +use crate::meta::MethodMeta; +use crate::types::TypeMeta; + +const TABLE: &str = include_str!("../api-docs/windows-null-results.txt"); + +struct Table { + members: HashSet, + owners: HashSet, +} + +static DOCUMENTED: LazyLock = LazyLock::new(|| { + let members = entries().map(normalize_api_id).collect::>(); + let owners = members.iter().filter_map(|id| owner_of(id)).collect(); + Table { members, owners } +}); + +/// The api-ids listed in the table, verbatim. +pub(crate) fn entries() -> impl Iterator { + TABLE + .lines() + .map(str::trim) + .filter(|line| !line.is_empty() && !line.starts_with('#')) +} + +/// Whether the documentation of `method`, listed under the type named +/// `owner` (`Namespace.Type`), says its result can be null. +pub(crate) fn documents_null_result(owner: &str, method: &MethodMeta) -> bool { + DOCUMENTED.owners.contains(owner) + && member_id(owner, method).is_some_and(|id| DOCUMENTED.members.contains(&id)) +} + +/// The normalized doc comment ID of `method` listed under `owner`. Only +/// methods and property getters produce results. +pub(crate) fn member_id(owner: &str, method: &MethodMeta) -> Option { + if method.is_property_getter { + let property = method.raw_name.strip_prefix("get_")?; + return Some(format!("P:{owner}.{property}")); + } + if method.is_property_setter || method.is_event_add || method.is_event_remove { + return None; + } + let parameters = method + .params + .iter() + .map(|parameter| doc_type_name(¶meter.typ)) + .collect::>() + .join(","); + Some(format!("M:{owner}.{}({parameters})", method.raw_name)) +} + +/// Normalizes an api-id so that metadata can reproduce it: methods always +/// carry a parameter list, and parameter types lose by-reference markers, +/// modifiers and generic arguments. +pub(crate) fn normalize_api_id(api_id: &str) -> String { + let Some(open) = api_id.find('(') else { + return if api_id.starts_with("M:") { + format!("{api_id}()") + } else { + api_id.to_string() + }; + }; + let parameters = split_parameters(api_id[open + 1..].trim_end_matches(')')) + .into_iter() + .map(normalize_doc_type) + .collect::>() + .join(","); + format!("{}({parameters})", &api_id[..open]) +} + +fn owner_of(id: &str) -> Option { + let member = id.get(2..)?.split('(').next()?; + member.rsplit_once('.').map(|(owner, _)| owner.to_string()) +} + +fn split_parameters(list: &str) -> Vec<&str> { + let mut parameters = Vec::new(); + let (mut depth, mut start) = (0usize, 0usize); + for (index, character) in list.char_indices() { + match character { + '{' => depth += 1, + '}' => depth = depth.saturating_sub(1), + ',' if depth == 0 => { + parameters.push(&list[start..index]); + start = index + 1; + } + _ => {} + } + } + if !list.is_empty() { + parameters.push(&list[start..]); + } + parameters +} + +fn normalize_doc_type(name: &str) -> String { + let name = name.split('!').next().unwrap_or(name).trim_end_matches('@'); + let mut result = String::with_capacity(name.len()); + let mut depth = 0usize; + for character in name.chars() { + match character { + '{' => depth += 1, + '}' => depth = depth.saturating_sub(1), + _ if depth == 0 => result.push(character), + _ => {} + } + } + match result.split_once('`') { + Some((definition, _)) => definition.to_string(), + // Some pages spell this struct with its .NET projection. + None if result == "System.Type" => "Windows.UI.Xaml.Interop.TypeName".to_string(), + None => result, + } +} + +/// The doc comment ID name of a parameter type, after normalization. +fn doc_type_name(typ: &TypeMeta) -> String { + let name = match typ { + TypeMeta::Bool => "System.Boolean", + TypeMeta::I8 => "System.SByte", + TypeMeta::U8 => "System.Byte", + TypeMeta::I16 => "System.Int16", + TypeMeta::U16 => "System.UInt16", + TypeMeta::I32 => "System.Int32", + TypeMeta::U32 => "System.UInt32", + TypeMeta::I64 => "System.Int64", + TypeMeta::U64 => "System.UInt64", + TypeMeta::F32 => "System.Single", + TypeMeta::F64 => "System.Double", + TypeMeta::Char16 => "System.Char", + TypeMeta::String => "System.String", + TypeMeta::Guid => "System.Guid", + TypeMeta::Object => "System.Object", + TypeMeta::AsyncAction => "Windows.Foundation.IAsyncAction", + TypeMeta::AsyncActionWithProgress(_) => "Windows.Foundation.IAsyncActionWithProgress", + TypeMeta::AsyncOperation(_) => "Windows.Foundation.IAsyncOperation", + TypeMeta::AsyncOperationWithProgress(..) => { + "Windows.Foundation.IAsyncOperationWithProgress" + } + TypeMeta::Array(inner) => return format!("{}[]", doc_type_name(inner)), + TypeMeta::Interface { + namespace, name, .. + } + | TypeMeta::RuntimeClass { + namespace, name, .. + } + | TypeMeta::Delegate { + namespace, name, .. + } + | TypeMeta::Struct { + namespace, name, .. + } + | TypeMeta::Enum { + namespace, name, .. + } + | TypeMeta::Parameterized { + namespace, name, .. + } => { + let definition = name.split('`').next().unwrap_or(name); + return format!("{namespace}.{definition}"); + } + }; + name.to_string() +} + +#[cfg(test)] +mod tests { + use std::collections::{BTreeMap, BTreeSet}; + use std::path::Path; + + use super::*; + use crate::meta::{ParamDirection, ParamMeta}; + + const WINDOWS_WINMD: &str = + r"C:\Program Files (x86)\Windows Kits\10\UnionMetadata\10.0.26100.0\Windows.winmd"; + + #[test] + fn api_ids_normalize_to_metadata_reproducible_keys() { + assert_eq!( + normalize_api_id("M:Windows.Devices.Sensors.Compass.GetDefault"), + "M:Windows.Devices.Sensors.Compass.GetDefault()" + ); + assert_eq!( + normalize_api_id( + "M:N.T.Find(Windows.Foundation.Collections.IMap{System.String,Windows.Foundation.Collections.IVector{System.String}},System.Byte[]@,System.Guid@!System.Runtime.CompilerServices.IsConst)" + ), + "M:N.T.Find(Windows.Foundation.Collections.IMap,System.Byte[],System.Guid)" + ); + assert_eq!(normalize_api_id("P:N.T.Value"), "P:N.T.Value"); + assert_eq!( + normalize_api_id("M:N.T.Get(System.Type)"), + normalize_api_id("M:N.T.Get(Windows.UI.Xaml.Interop.TypeName)") + ); + assert_eq!(owner_of("M:N.T.Find()").as_deref(), Some("N.T")); + } + + #[test] + fn member_ids_use_clr_names_and_parameter_types() { + let method = MethodMeta { + name: "GetDefaultWithAccelerometerReadingType".into(), + raw_name: "GetDefault".into(), + params: vec![ + ParamMeta { + name: "readingType".into(), + typ: TypeMeta::Enum { + namespace: "Windows.Devices.Sensors".into(), + name: "AccelerometerReadingType".into(), + underlying: Box::new(TypeMeta::I32), + members: vec![], + is_flags: false, + doc: None, + deprecated: None, + }, + direction: ParamDirection::In, + }, + ParamMeta { + name: "values".into(), + typ: TypeMeta::Array(Box::new(TypeMeta::Parameterized { + namespace: "Windows.Foundation.Collections".into(), + name: "IIterable`1".into(), + piid: String::new(), + args: vec![TypeMeta::String], + })), + direction: ParamDirection::Out, + }, + ], + ..Default::default() + }; + assert_eq!( + member_id("Windows.Devices.Sensors.Accelerometer", &method).as_deref(), + Some( + "M:Windows.Devices.Sensors.Accelerometer.GetDefault(Windows.Devices.Sensors.AccelerometerReadingType,Windows.Foundation.Collections.IIterable[])" + ) + ); + let getter = MethodMeta { + name: "get_Parent".into(), + raw_name: "get_Parent".into(), + is_property_getter: true, + ..Default::default() + }; + assert_eq!( + member_id("Windows.UI.Xaml.FrameworkElement", &getter).as_deref(), + Some("P:Windows.UI.Xaml.FrameworkElement.Parent") + ); + let setter = MethodMeta { + name: "put_Parent".into(), + raw_name: "put_Parent".into(), + is_property_setter: true, + ..Default::default() + }; + assert_eq!(member_id("N.T", &setter), None); + } + + /// Every entry names a member of the Windows SDK metadata whose parsed + /// method carries the documented-null fact. This guards the table against + /// typos and drift, and the key derivation against metadata changes. + #[test] + fn every_entry_resolves_to_a_flagged_windows_sdk_member() { + // Documented, but absent from the 10.0.26100 SDK metadata: newer APIs, + // and AllJoyn, which the SDK dropped. + const ABSENT_FROM_TEST_SDK: [&str; 3] = [ + "M:Windows.Devices.AllJoyn.AllJoynServiceInfo.FromIdAsync(System.String)", + "M:Windows.Gaming.UI.GameMonitor.GetDefault()", + "M:Windows.System.User.GetUserAgeRangeAsync()", + ]; + if !Path::new(WINDOWS_WINMD).is_file() { + eprintln!("Skipping: Windows.winmd not found"); + return; + } + let index = crate::meta::load_index(WINDOWS_WINMD).expect("Windows.winmd index"); + let mut by_owner = BTreeMap::>::new(); + for entry in entries() { + let id = normalize_api_id(entry); + let owner = owner_of(&id).expect("owner"); + by_owner.entry(owner).or_default().insert(id); + } + let mut unresolved = Vec::new(); + let mut unflagged = Vec::new(); + for (owner, ids) in &by_owner { + let (namespace, name) = owner.rsplit_once('.').expect("qualified owner"); + let methods = crate::meta::documented_owner_methods(&index, namespace, name); + for id in ids { + let matches = methods + .iter() + .filter(|method| member_id(owner, method).as_deref() == Some(id)) + .collect::>(); + if ABSENT_FROM_TEST_SDK.contains(&id.as_str()) { + let member = id[2..] + .split('(') + .next() + .unwrap() + .rsplit('.') + .next() + .unwrap(); + assert!( + !methods.iter().any(|method| method.raw_name == member), + "{id} is present in the test SDK; drop it from ABSENT_FROM_TEST_SDK" + ); + } else if matches.is_empty() { + unresolved.push(id.clone()); + } else if !matches.iter().any(|method| method.documented_null_result) { + unflagged.push(id.clone()); + } + } + } + assert!(unresolved.is_empty(), "unresolved entries: {unresolved:#?}"); + assert!(unflagged.is_empty(), "entries not applied: {unflagged:#?}"); + assert!(by_owner.values().map(BTreeSet::len).sum::() > 900); + } +} diff --git a/tools/dynwinrt-codegen/src/lib.rs b/tools/dynwinrt-codegen/src/lib.rs index ef0c3b86..09db795e 100644 --- a/tools/dynwinrt-codegen/src/lib.rs +++ b/tools/dynwinrt-codegen/src/lib.rs @@ -5,6 +5,7 @@ pub mod codegen; mod com_activation_registry; pub mod com_metadata; mod contract_registry; +mod documented_nulls; pub mod meta; pub mod types; mod win32_contracts; diff --git a/tools/dynwinrt-codegen/src/meta.rs b/tools/dynwinrt-codegen/src/meta.rs index 93e51ee8..729b504c 100644 --- a/tools/dynwinrt-codegen/src/meta.rs +++ b/tools/dynwinrt-codegen/src/meta.rs @@ -57,6 +57,46 @@ pub struct MethodMeta { pub param_docs: std::collections::HashMap, /// XML `` text. pub returns_doc: Option, + /// The Windows SDK documentation says the result can be null: a method's + /// return value (for asynchronous methods, the completed result) or a + /// property's value. See `documented_nulls`. + pub documented_null_result: bool, + /// Set on the members that read elements of the + /// `Windows.Foundation.Collections` interface declaring them. + pub element_access: Option, +} + +/// How a member reads the elements of the `Windows.Foundation.Collections` +/// interface declaring it: `GetAt`, `GetMany`, `Lookup`, `Current`, `Key` and +/// `Value`. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum ElementAccess { + /// `IIterator`, `IVectorView`, `IMapView` and `IKeyValuePair`. + ReadOnly, + /// `IVector` and `IMap`, in which anyone can store null. + Mutable, +} + +fn collection_element_access( + namespace: &str, + definition: &str, + member: &str, +) -> Option { + if namespace != WINDOWS_FOUNDATION_COLLECTIONS_NAMESPACE { + return None; + } + let access = match definition { + "IIterator`1" | "IVectorView`1" | "IMapView`2" | "IKeyValuePair`2" => { + ElementAccess::ReadOnly + } + "IVector`1" | "IMap`2" => ElementAccess::Mutable, + _ => return None, + }; + matches!( + member, + "GetAt" | "GetMany" | "Lookup" | "get_Current" | "get_Key" | "get_Value" + ) + .then_some(access) } /// A WinRT interface with its methods. @@ -1224,6 +1264,8 @@ fn parse_class_from_index(index: &reader::Index, namespace: &str, name: &str) -> let mut ancestor_key: Option<(String, String)> = def .extends() .map(|e| (e.namespace().to_string(), e.name().to_string())); + // The documentation lists inherited members under the class declaring them. + let mut documentation_owners = vec![full_name.clone()]; while let Some((ext_ns, ext_name)) = ancestor_key.take() { if ext_ns == "System" && ext_name == "Object" { break; @@ -1232,6 +1274,7 @@ fn parse_class_from_index(index: &reader::Index, namespace: &str, name: &str) -> Some(d) => d, None => break, }; + documentation_owners.push(format!("{ext_ns}.{ext_name}")); for iface_impl in parent_def.interface_impls() { let iface_ty = iface_impl.interface(&[]); if iface_impl.has_attribute("OverridableAttribute") { @@ -1378,6 +1421,19 @@ fn parse_class_from_index(index: &reader::Index, namespace: &str, name: &str) -> } } + for interface in default_interface + .iter_mut() + .chain(required_interfaces.iter_mut()) + .chain(factory_interfaces.iter_mut()) + .chain(static_interfaces.iter_mut()) + { + for method in &mut interface.methods { + method.documented_null_result |= documentation_owners + .iter() + .any(|owner| crate::documented_nulls::documents_null_result(owner, method)); + } + } + Some(ClassMeta { name: name.to_string(), namespace: namespace.to_string(), @@ -1428,6 +1484,36 @@ fn parse_interface(index: &reader::Index, namespace: &str, name: &str) -> Option parse_interface_methods(index, &def, name, namespace, &iid, &[]) } +/// The parsed methods that the documentation lists under `namespace.name`: +/// the members of a class's interfaces, or an interface's own members. +#[cfg(test)] +pub(crate) fn documented_owner_methods( + index: &reader::Index, + namespace: &str, + name: &str, +) -> Vec { + let Some(def) = index.get(namespace, name).next() else { + return Vec::new(); + }; + if def.extends().is_none() { + return parse_interface(index, namespace, name) + .map(|interface| interface.methods) + .unwrap_or_default(); + } + parse_class_from_index(index, namespace, name) + .map(|class| { + class + .default_interface + .into_iter() + .chain(class.required_interfaces) + .chain(class.factory_interfaces) + .chain(class.static_interfaces) + .flat_map(|interface| interface.methods) + .collect() + }) + .unwrap_or_default() +} + fn parse_interface_type( index: &reader::Index, interface_type: &windows_metadata::Type, @@ -1582,6 +1668,9 @@ fn parse_interface_methods( ) -> Option { let winmd_generics: Vec = generic_args.iter().map(type_meta_to_winmd_type).collect(); + // The documentation lists interface members under the interface + // definition, e.g. `Windows.Foundation.Collections.IVector`1`. + let documentation_owner = format!("{namespace}.{}", def.name()); let mut methods = Vec::new(); let mut implementation_metadata = InterfaceImplementationMetadata { @@ -1741,7 +1830,8 @@ fn parse_interface_methods( format!("({})", clr_sig_types.join(",")) }; - methods.push(MethodMeta { + let element_access = collection_element_access(namespace, def.name(), &raw_name); + let mut method_meta = MethodMeta { name: method_name.clone(), vtable_index, params, @@ -1756,7 +1846,12 @@ fn parse_interface_methods( deprecated: None, param_docs: std::collections::HashMap::new(), returns_doc: None, - }); + documented_null_result: false, + element_access, + }; + method_meta.documented_null_result = + crate::documented_nulls::documents_null_result(&documentation_owner, &method_meta); + methods.push(method_meta); } let (generic_piid, generic_args_vec) = if !generic_args.is_empty() { diff --git a/tools/dynwinrt-codegen/tests/python_consumer_typing_test.rs b/tools/dynwinrt-codegen/tests/python_consumer_typing_test.rs index 04191b2c..ba16d503 100644 --- a/tools/dynwinrt-codegen/tests/python_consumer_typing_test.rs +++ b/tools/dynwinrt-codegen/tests/python_consumer_typing_test.rs @@ -421,7 +421,7 @@ def collections(resource: Resource, derived: OtherDerived, raw: DynWinRTValue, mapping[resource] = derived assert_type(vector[0], DynWinRTValue | None) assert_type(vector[:], list[DynWinRTValue | None]) - assert_type(resources[0], Resource) + assert_type(resources[0], Resource | None) assert_type(mapping[resource], DynWinRTValue | None) del mapping[resource] "#, @@ -814,7 +814,7 @@ print("collection-subscript-native-ok", flush=True) } #[test] -fn natural_sdk_consumers_need_no_none_guards() { +fn natural_sdk_consumers_guard_only_nullable_results() { let winmd = Path::new( r"C:\Program Files (x86)\Windows Kits\10\UnionMetadata\10.0.26100.0\Windows.winmd", ); @@ -834,7 +834,8 @@ fn natural_sdk_consumers_need_no_none_guards() { Windows.Security.Cryptography.Core.HashAlgorithmProvider,\ Windows.Storage.StorageFolder,Windows.Storage.FileIO,\ Windows.Storage.Streams.DataReader,Windows.Storage.Streams.DataWriter,\ - Windows.Storage.Streams.InMemoryRandomAccessStream", + Windows.Storage.Streams.InMemoryRandomAccessStream,\ + Windows.Devices.Sensors.Accelerometer", "--lang", "py", "--output", @@ -846,7 +847,8 @@ fn natural_sdk_consumers_need_no_none_guards() { let imports = r#"from collections.abc import Sequence from typing import assert_type from dynwinrt import DynWinRTValue, WinRTCoroutine -from sdk.windows.data.json import JsonObject +from sdk.windows.data.json import IJsonValue, JsonObject +from sdk.windows.devices.sensors import Accelerometer from sdk.windows.foundation import Uri from sdk.windows.foundation.collections import PropertySet from sdk.windows.globalization import Calendar @@ -868,7 +870,15 @@ def uri_demo() -> str: def json_demo() -> list[str]: parsed = JsonObject.parse('{{"tags": ["a", "b"]}}') assert_type(JsonObject.try_parse("{{}}"), tuple[JsonObject | None, bool]) - return [value.get_string() for value in parsed.get_named_array("tags")] + tags = parsed.get_named_array("tags") + assert_type(tags[0], IJsonValue | None) + return [value.get_string() for value in tags if value is not None] + +def sensor_demo() -> float | None: + accelerometer = Accelerometer.get_default() + if accelerometer is None: + return None + return accelerometer.get_current_reading().acceleration_x def calendar_demo(calendar: Calendar) -> str: languages: Sequence[str] = calendar.languages @@ -897,6 +907,7 @@ async def storage_demo(path: str) -> list[str]: await FileIO.write_text_async(file, "first line") assert_type(folder.create_file_async("a.txt"), WinRTCoroutine[StorageFile]) assert_type(folder.try_get_item_async("notes.txt"), WinRTCoroutine[IStorageItem | None]) + assert_type(folder.get_parent_async(), WinRTCoroutine[StorageFolder | None]) return [item.name for item in await folder.get_files_async()] "# ), diff --git a/tools/dynwinrt-codegen/tests/python_stub_nullability_test.rs b/tools/dynwinrt-codegen/tests/python_stub_nullability_test.rs index 552988fe..a0dba5e7 100644 --- a/tools/dynwinrt-codegen/tests/python_stub_nullability_test.rs +++ b/tools/dynwinrt-codegen/tests/python_stub_nullability_test.rs @@ -3,9 +3,10 @@ //! The stub nullability policy on real Windows SDK metadata. Values received //! from the projection are non-null in `.pyi` stubs by default, while -//! `IReference`, `Try*` results, `Object` and delegate values keep -//! `| None`. Inputs, implementation protocols and the runtime `.py` -//! annotations are unchanged. +//! `IReference`, `Try*` results, members documented to return null, +//! `Object` and delegate values keep `| None`. Collection elements follow the +//! mutability of the collection holding them. Inputs, implementation +//! protocols and the runtime `.py` annotations are unchanged. use std::fs; use std::path::{Path, PathBuf}; @@ -23,7 +24,7 @@ impl Drop for Generated { } impl Generated { - fn new(classes: &str) -> Option { + fn new(label: &str, classes: &str) -> Option { if !Path::new(WINDOWS_WINMD).is_file() { eprintln!("Skipping: Windows.winmd not found"); return None; @@ -34,7 +35,7 @@ impl Generated { .parent() .unwrap() .join("target") - .join(format!("pn{}", std::process::id())); + .join(format!("pn{label}{}", std::process::id())); let _ = fs::remove_dir_all(&root); let output = Command::new(env!("CARGO_BIN_EXE_dynwinrt-codegen")) .args([ @@ -69,8 +70,11 @@ fn assert_contains(text: &str, expected: &str) { #[test] fn stub_outputs_follow_the_nullability_policy() { let Some(generated) = Generated::new( + "out", "Windows.Storage.StorageFolder,Windows.Web.Http.Headers.HttpContentHeaderCollection,\ - Windows.Foundation.Collections.PropertySet,Windows.Data.Json.JsonObject", + Windows.Foundation.Collections.PropertySet,Windows.Data.Json.JsonObject,\ + Windows.Devices.Sensors.Accelerometer,Windows.Devices.Sensors.Compass,\ + Windows.System.DispatcherQueue,Windows.Data.Xml.Dom.XmlDocument", ) else { return; }; @@ -81,6 +85,10 @@ fn stub_outputs_follow_the_nullability_policy() { generated.module("windows__web__http__headers__http_content_header_collection.pyi"); let properties = generated.module("windows__foundation__collections__property_set.pyi"); let json = generated.module("windows__data__json__json_object.pyi"); + let accelerometer = generated.module("windows__devices__sensors__accelerometer.pyi"); + let compass = generated.module("windows__devices__sensors__compass.pyi"); + let dispatcher = generated.module("windows__system__dispatcher_queue.pyi"); + let xml = generated.module("windows__data__xml__dom__xml_document.pyi"); // Method, async, collection and property outputs are non-null by default. assert_contains( @@ -115,11 +123,40 @@ fn stub_outputs_follow_the_nullability_policy() { "def try_parse(input: str) -> tuple[JsonObject | None, bool]: ...", ); - // IReference values and Object values keep None. + // Members the Windows SDK documentation says can return null keep None, + // including through overloads, interfaces and async results. + assert_contains( + &accelerometer, + "def get_default() -> Accelerometer | None: ...", + ); + assert_contains( + &accelerometer, + "def get_default_with_accelerometer_reading_type(reading_type: 'AccelerometerReadingType') -> Accelerometer | None: ...", + ); + assert_contains(&compass, "def get_default() -> Compass | None: ..."); + assert_contains( + &dispatcher, + "def get_for_current_thread() -> DispatcherQueue | None: ...", + ); + assert_contains( + &dispatcher, + "def create_timer(self) -> DispatcherQueueTimer: ...", + ); + assert_contains( + &folder, + "def get_parent_async(self) -> WinRTCoroutine[StorageFolder | None]: ...", + ); + assert_contains( + &xml, + "def select_single_node(self, xpath: str) -> IXmlNode | None: ...", + ); + + // IReference values and Object values keep None, and so do properties + // whose documentation says a null value means "absent". assert_contains(&headers, "def content_length(self) -> int | None: ..."); assert_contains( &headers, - "def content_type(self) -> HttpMediaTypeHeaderValue: ...", + "def content_type(self) -> HttpMediaTypeHeaderValue | None: ...", ); assert_contains( &properties, @@ -161,3 +198,78 @@ fn stub_outputs_follow_the_nullability_policy() { "def properties(self) -> StorageItemContentProperties | None:", ); } + +#[test] +fn collection_elements_follow_the_mutability_of_their_collection() { + let Some(generated) = Generated::new( + "elements", + "Windows.Storage.StorageFolder,Windows.Data.Json.JsonObject,\ + Windows.ApplicationModel.Resources.Core.ResourceMap,Windows.Media.Playback.MediaPlaybackList", + ) else { + return; + }; + let files = + generated.module("windows__foundation__collections__i_vector_view_storage_file.pyi"); + let array = generated.module("windows__data__json__json_array.pyi"); + let object = generated.module("windows__data__json__json_object.pyi"); + let resources = + generated.module("windows__application_model__resources__core__resource_map.pyi"); + let playlist = generated.module("windows__media__playback__media_playback_list.pyi"); + let observable = generated + .module("windows__foundation__collections__i_observable_vector_media_playback_item.pyi"); + + // Views keep non-null elements, including their item positions. + assert_contains(&files, "def get_at(self, index: int) -> StorageFile: ..."); + assert_contains( + &files, + "def __getitem__(self, index: int) -> StorageFile: ...", + ); + assert_contains( + &resources, + "class ResourceMap(_ResourceMapIdentity, Mapping[str, NamedResource], _DynWinRTRuntimeClass):", + ); + assert_contains( + &resources, + "def lookup(self, key: str) -> NamedResource: ...", + ); + + // Anyone can store null in a mutable collection, so its elements, item + // positions and element-reading members keep None. + assert_contains( + &array, + "class JsonArray(_JsonArrayIdentity, MutableSequence[IJsonValue | None], _DynWinRTRuntimeClass):", + ); + assert_contains( + &array, + "def get_at(self, index: int) -> IJsonValue | None: ...", + ); + assert_contains( + &array, + "def __getitem__(self, index: int) -> IJsonValue | None: ...", + ); + assert_contains( + &array, + "def get_object_at(self, index: int) -> JsonObject: ...", + ); + assert_contains( + &object, + "class JsonObject(_JsonObjectIdentity, MutableMapping[str, IJsonValue | None], _DynWinRTRuntimeClass):", + ); + assert_contains( + &object, + "def lookup(self, key: str) -> IJsonValue | None: ...", + ); + assert_contains( + &object, + "def get_named_array(self, name: str) -> JsonArray: ...", + ); + assert_contains( + &playlist, + "def items(self) -> MutableSequence[MediaPlaybackItem | None]: ...", + ); + assert_contains(&observable, "MutableSequence[MediaPlaybackItem | None]):"); + assert_contains( + &observable, + "def __getitem__(self, index: int) -> MediaPlaybackItem | None: ...", + ); +} From 557eba097b4ec5b7677ed246bf302a674afef00b Mon Sep 17 00:00:00 2001 From: Leilei Zhang Date: Thu, 24 Sep 2026 17:21:04 +0800 Subject: [PATCH 5/5] Test that mutable collection mutators accept None Inherited MutableSequence and MutableMapping mutators (append, extend, update, setdefault) take the element type of the collection base, so they accept None only while mutable collection bases keep `| None`. Lock that in with a strict mypy consumer of an observable vector and a JSON object. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../tests/python_consumer_typing_test.rs | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/tools/dynwinrt-codegen/tests/python_consumer_typing_test.rs b/tools/dynwinrt-codegen/tests/python_consumer_typing_test.rs index ba16d503..34c7b778 100644 --- a/tools/dynwinrt-codegen/tests/python_consumer_typing_test.rs +++ b/tools/dynwinrt-codegen/tests/python_consumer_typing_test.rs @@ -915,6 +915,58 @@ async def storage_demo(path: str) -> list[str]: ); } +#[test] +fn mutable_collection_mutators_accept_none() { + let winmd = Path::new( + r"C:\Program Files (x86)\Windows Kits\10\UnionMetadata\10.0.26100.0\Windows.winmd", + ); + if !winmd.is_file() || !has_mypy() { + eprintln!("Skipping mutable collection mutators: Windows.winmd or mypy unavailable."); + return; + } + let fixture = Fixture::new(); + let output = Command::new(env!("CARGO_BIN_EXE_dynwinrt-codegen")) + .args(["generate", "--winmd"]) + .arg(winmd) + .args([ + "--class-name", + "Windows.Storage.StorageLibrary,Windows.Data.Json.JsonObject", + "--lang", + "py", + "--output", + ]) + .arg(fixture.0.join("sdk")) + .output() + .unwrap(); + assert!(output.status.success(), "{}", diagnostics(&output)); + // Inherited MutableSequence and MutableMapping mutators take the element + // type of the collection base, which keeps `| None` for mutable + // collections, like the generated item setters. + typecheck( + &fixture, + &["sdk"], + r#"from typing import assert_type +from sdk.windows.data.json import IJsonValue, JsonObject +from sdk.windows.foundation.collections import IObservableVector_StorageFolder +from sdk.windows.storage import StorageFolder + +def vector(folders: IObservableVector_StorageFolder) -> None: + folders.append(None) + folders.extend([None]) + folders.insert(0, None) + folders[0] = None + assert_type(folders[0], StorageFolder | None) + +def mapping(values: JsonObject) -> None: + values.update({"k": None}) + values.setdefault("k", None) + values["k"] = None + assert_type(values["k"], IJsonValue | None) +"#, + &[], + ); +} + #[test] fn native_object_inputs_keep_projection_factories_and_context_lifetimes() { if !has_implementation_runtime() {