From 790f0817a505e5afbf354592d26a1ddf255af208 Mon Sep 17 00:00:00 2001 From: JosepMariaPujol Date: Thu, 19 Mar 2026 11:23:28 +0100 Subject: [PATCH 1/9] Update APIVerificationTests.cs --- .../Tests/InputSystem/APIVerificationTests.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/Assets/Tests/InputSystem/APIVerificationTests.cs b/Assets/Tests/InputSystem/APIVerificationTests.cs index 03fd7e5275..db1b7dab78 100644 --- a/Assets/Tests/InputSystem/APIVerificationTests.cs +++ b/Assets/Tests/InputSystem/APIVerificationTests.cs @@ -541,6 +541,23 @@ public class DualShock4GamepadHID : DualShockGamepad public InputTestFixture.ActionConstraint Performed(InputAction action, InputControl control = default(InputControl), System.Nullable time = default(System.Nullable), System.Nullable duration = default(System.Nullable)); public InputTestFixture.ActionConstraint Started(InputAction action, InputControl control = default(InputControl), System.Nullable time = default(System.Nullable)); ")] + // API scraper output for these built-in XR controller types differs depending on installed XR replacement packages. + [Property("Exclusions", @"1.0.0 + public class DaydreamController : UnityEngine.InputSystem.XR.XRController + public class GearVRTrackedController : UnityEngine.InputSystem.XR.XRController + public class OculusTouchController : UnityEngine.InputSystem.XR.XRControllerWithRumble + public class HandedViveTracker : ViveTracker + public class OpenVRControllerWMR : UnityEngine.InputSystem.XR.XRController + public class OpenVROculusTouchController : UnityEngine.InputSystem.XR.XRControllerWithRumble + public class ViveWand : UnityEngine.InputSystem.XR.XRControllerWithRumble + ")] + // API scraper in 1.0.0 emitted incomplete default argument expressions for these overloads. + [Property("Exclusions", @"1.0.0 + public static string GetBindingDisplayString(this InputAction action, int bindingIndex, InputBinding.DisplayStringOptions options = ); + public static string GetBindingDisplayString(this InputAction action, InputBinding bindingMask, InputBinding.DisplayStringOptions options = ); + public static string GetBindingDisplayString(this InputAction action, InputBinding.DisplayStringOptions options = , string group = default(string)); + public static string GetBindingDisplayString(this InputAction action, int bindingIndex, out string deviceLayoutName, out string controlPath, InputBinding.DisplayStringOptions options = ); + ")] // Api scraper seems to be unstable with fields with default values, sometimes "= 0;" appears (locally) and sometimes (on CI) doesn't. [Property("Exclusions", @"1.0.0 public int negative = 0; From 8a8470095ae7fa2d2455a8652ccab9c7dad78609 Mon Sep 17 00:00:00 2001 From: JosepMariaPujol Date: Thu, 19 Mar 2026 13:22:46 +0100 Subject: [PATCH 2/9] Update APIVerificationTests.cs --- Assets/Tests/InputSystem/APIVerificationTests.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Assets/Tests/InputSystem/APIVerificationTests.cs b/Assets/Tests/InputSystem/APIVerificationTests.cs index db1b7dab78..bb9ddd23f7 100644 --- a/Assets/Tests/InputSystem/APIVerificationTests.cs +++ b/Assets/Tests/InputSystem/APIVerificationTests.cs @@ -557,6 +557,16 @@ public class ViveWand : UnityEngine.InputSystem.XR.XRControllerWithRumble public static string GetBindingDisplayString(this InputAction action, InputBinding bindingMask, InputBinding.DisplayStringOptions options = ); public static string GetBindingDisplayString(this InputAction action, InputBinding.DisplayStringOptions options = , string group = default(string)); public static string GetBindingDisplayString(this InputAction action, int bindingIndex, out string deviceLayoutName, out string controlPath, InputBinding.DisplayStringOptions options = ); + public string ToDisplayString(InputBinding.DisplayStringOptions options = , InputControl control = default(InputControl)); + public string ToDisplayString(out string deviceLayoutName, out string controlPath, InputBinding.DisplayStringOptions options = , InputControl control = default(InputControl)); + DontIncludeInteractions = 4, + DontOmitDevice = 2, + DontUseShortDisplayNames = 1, + IgnoreBindingOverrides = 8, + protected System.UInt32 stateOffsetRelativeToDeviceRoot { get; } + OmitDevice = 2, + UseShortNames = 4, + BufferedBytes = 256, ")] // Api scraper seems to be unstable with fields with default values, sometimes "= 0;" appears (locally) and sometimes (on CI) doesn't. [Property("Exclusions", @"1.0.0 From 37ddf980f678f690f09e4744cb4e06e3f26accbf Mon Sep 17 00:00:00 2001 From: JosepMariaPujol Date: Thu, 19 Mar 2026 13:43:06 +0100 Subject: [PATCH 3/9] Update APIVerificationTests.cs --- Assets/Tests/InputSystem/APIVerificationTests.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Assets/Tests/InputSystem/APIVerificationTests.cs b/Assets/Tests/InputSystem/APIVerificationTests.cs index bb9ddd23f7..6faeb3cc7d 100644 --- a/Assets/Tests/InputSystem/APIVerificationTests.cs +++ b/Assets/Tests/InputSystem/APIVerificationTests.cs @@ -567,6 +567,16 @@ public class ViveWand : UnityEngine.InputSystem.XR.XRControllerWithRumble OmitDevice = 2, UseShortNames = 4, BufferedBytes = 256, + Constant = 1, + NonLinear = 16, + NoPreferred = 32, + NullState = 64, + Relative = 4, + Variable = 2, + Volatile = 128, + Wrap = 8, + public System.UInt32 bit { get; set; } + public System.UInt32 offset { get; set; } ")] // Api scraper seems to be unstable with fields with default values, sometimes "= 0;" appears (locally) and sometimes (on CI) doesn't. [Property("Exclusions", @"1.0.0 From df4d9c362cc8b2ea6835842324bd62742b1a810f Mon Sep 17 00:00:00 2001 From: JosepMariaPujol Date: Thu, 19 Mar 2026 14:47:21 +0100 Subject: [PATCH 4/9] Update APIVerificationTests.cs --- Assets/Tests/InputSystem/APIVerificationTests.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Assets/Tests/InputSystem/APIVerificationTests.cs b/Assets/Tests/InputSystem/APIVerificationTests.cs index 6faeb3cc7d..f84e412b90 100644 --- a/Assets/Tests/InputSystem/APIVerificationTests.cs +++ b/Assets/Tests/InputSystem/APIVerificationTests.cs @@ -577,6 +577,16 @@ public class ViveWand : UnityEngine.InputSystem.XR.XRControllerWithRumble Wrap = 8, public System.UInt32 bit { get; set; } public System.UInt32 offset { get; set; } + public System.UInt32 sizeInBits { get; set; } + public InputControlLayout.Builder.ControlBuilder WithBitOffset(System.UInt32 bit); + public InputControlLayout.Builder.ControlBuilder WithByteOffset(System.UInt32 offset); + public InputControlLayout.Builder.ControlBuilder WithSizeInBits(System.UInt32 sizeInBits); + public System.UInt32 bit { get; } + public System.UInt32 offset { get; } + public System.UInt32 sizeInBits { get; } + public System.UInt32 stateOffset; + public System.UInt32 deltaStateSizeInBytes { get; } + public System.UInt32 buttons; ")] // Api scraper seems to be unstable with fields with default values, sometimes "= 0;" appears (locally) and sometimes (on CI) doesn't. [Property("Exclusions", @"1.0.0 From cf3c6fa3a1c11df9a1d5abe54c38812a2966b038 Mon Sep 17 00:00:00 2001 From: JosepMariaPujol Date: Thu, 19 Mar 2026 15:27:14 +0100 Subject: [PATCH 5/9] Update APIVerificationTests.cs --- .../Tests/InputSystem/APIVerificationTests.cs | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/Assets/Tests/InputSystem/APIVerificationTests.cs b/Assets/Tests/InputSystem/APIVerificationTests.cs index f84e412b90..e325cd0968 100644 --- a/Assets/Tests/InputSystem/APIVerificationTests.cs +++ b/Assets/Tests/InputSystem/APIVerificationTests.cs @@ -587,6 +587,50 @@ public class ViveWand : UnityEngine.InputSystem.XR.XRControllerWithRumble public System.UInt32 stateOffset; public System.UInt32 deltaStateSizeInBytes { get; } public System.UInt32 buttons; + public bool GetStateOffsetForEvent(InputControl control, InputEventPtr eventPtr, ref System.UInt32 offset); + public System.UInt32 sizeInBytes { get; set; } + public System.UInt32 sizeInBytes { get; } + public static System.UInt32 updateCount { get; } + public const System.UInt32 AutomaticOffset = 4294967294; + public const System.UInt32 InvalidOffset = 4294967295; + public System.UInt32 bitOffset { get; set; } + public System.UInt32 byteOffset { get; set; } + public System.UInt32 version { get; } + public System.UInt32 version; + public System.UInt32 stateSizeInBytes { get; } + public const System.UInt32 InvalidId = 0; + public System.UInt32 id { get; } + public System.UInt32 parentBoneIndex { get; set; } + public System.UInt32 customSize; + public System.UInt32 samplesAvailable; + public System.UInt32 samplesQueued; + public System.UInt32 frequencyHz; + public System.UInt32 maxBufferSize; + public System.UInt32 numChannels; + public System.UInt32 frequencyHz { get; } + public System.UInt32 maxBufferSize { get; } + public System.UInt32 numChannels { get; } + public HapticCapabilities(System.UInt32 numChannels, System.UInt32 frequencyHz, System.UInt32 maxBufferSize) {} + public System.UInt32 samplesAvailable { get; } + public System.UInt32 samplesQueued { get; } + public HapticState(System.UInt32 samplesQueued, System.UInt32 samplesAvailable) {} + public PrimitiveValue(System.UInt16 value) {} + public PrimitiveValue(System.UInt32 value) {} + public PrimitiveValue(System.UInt64 value) {} + public static PrimitiveValue FromUInt16(System.UInt16 value); + public static PrimitiveValue FromUInt32(System.UInt32 value); + public static PrimitiveValue FromUInt64(System.UInt64 value); + public static PrimitiveValue op_Implicit(System.UInt16 value); + public static PrimitiveValue op_Implicit(System.UInt32 value); + public static PrimitiveValue op_Implicit(System.UInt64 value); + public System.UInt16 ToUInt16(System.IFormatProvider provider = default(System.IFormatProvider)); + public System.UInt32 ToUInt32(System.IFormatProvider provider = default(System.IFormatProvider)); + public System.UInt64 ToUInt64(System.IFormatProvider provider = default(System.IFormatProvider)); + public System.UInt64 handle { get; } + public InputUserAccountHandle(string apiName, System.UInt64 handle) {} + public FourCC(char a, char b = , char c = , char d = ) {} + public static string ToHumanReadableString(string path, InputControlPath.HumanReadableStringOptions options = InputControlPath.HumanReadableStringOptions.None, InputControl control = default(InputControl)); + public static string ToHumanReadableString(string path, out string deviceLayoutName, out string controlPath, InputControlPath.HumanReadableStringOptions options = InputControlPath.HumanReadableStringOptions.None, InputControl control = default(InputControl)); ")] // Api scraper seems to be unstable with fields with default values, sometimes "= 0;" appears (locally) and sometimes (on CI) doesn't. [Property("Exclusions", @"1.0.0 From 8bae68e7b32a5e3dd8ae7c8cec5112ef9d6354f3 Mon Sep 17 00:00:00 2001 From: JosepMariaPujol Date: Thu, 19 Mar 2026 15:40:51 +0100 Subject: [PATCH 6/9] Update APIVerificationTests.cs --- Assets/Tests/InputSystem/APIVerificationTests.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Assets/Tests/InputSystem/APIVerificationTests.cs b/Assets/Tests/InputSystem/APIVerificationTests.cs index e325cd0968..1581d0d0be 100644 --- a/Assets/Tests/InputSystem/APIVerificationTests.cs +++ b/Assets/Tests/InputSystem/APIVerificationTests.cs @@ -631,6 +631,17 @@ public InputUserAccountHandle(string apiName, System.UInt64 handle) {} public FourCC(char a, char b = , char c = , char d = ) {} public static string ToHumanReadableString(string path, InputControlPath.HumanReadableStringOptions options = InputControlPath.HumanReadableStringOptions.None, InputControl control = default(InputControl)); public static string ToHumanReadableString(string path, out string deviceLayoutName, out string controlPath, InputControlPath.HumanReadableStringOptions options = InputControlPath.HumanReadableStringOptions.None, InputControl control = default(InputControl)); + public class InputStateHistory : InputStateHistory, System.Collections.Generic.IEnumerable>, System.Collections.Generic.IReadOnlyCollection>, System.Collections.Generic.IReadOnlyList>, System.Collections.IEnumerable where TValue : struct, new() + public UnityEngine.InputSystem.LowLevel.InputStateHistory this[int index] { get; set; } + public UnityEngine.InputSystem.LowLevel.InputStateHistory AddRecord(UnityEngine.InputSystem.LowLevel.InputStateHistory record); + public System.Collections.Generic.IEnumerator> GetEnumerator(); + public UnityEngine.InputSystem.LowLevel.InputStateHistory RecordStateChange(UnityEngine.InputSystem.InputControl control, TValue value, double time = -1d); + public struct Record : System.IEquatable> + public UnityEngine.InputSystem.LowLevel.InputStateHistory next { get; } + public UnityEngine.InputSystem.LowLevel.InputStateHistory owner { get; } + public UnityEngine.InputSystem.LowLevel.InputStateHistory previous { get; } + public void CopyFrom(UnityEngine.InputSystem.LowLevel.InputStateHistory record); + public bool Equals(UnityEngine.InputSystem.LowLevel.InputStateHistory other); ")] // Api scraper seems to be unstable with fields with default values, sometimes "= 0;" appears (locally) and sometimes (on CI) doesn't. [Property("Exclusions", @"1.0.0 From 99798154bec89a0dd1dfd502fc43990053fe3f9e Mon Sep 17 00:00:00 2001 From: JosepMariaPujol Date: Thu, 19 Mar 2026 15:51:39 +0100 Subject: [PATCH 7/9] Update APIVerificationTests.cs --- Assets/Tests/InputSystem/APIVerificationTests.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Assets/Tests/InputSystem/APIVerificationTests.cs b/Assets/Tests/InputSystem/APIVerificationTests.cs index 1581d0d0be..b5ef96e2b1 100644 --- a/Assets/Tests/InputSystem/APIVerificationTests.cs +++ b/Assets/Tests/InputSystem/APIVerificationTests.cs @@ -642,6 +642,11 @@ public struct Record : System.IEquatable previous { get; } public void CopyFrom(UnityEngine.InputSystem.LowLevel.InputStateHistory record); public bool Equals(UnityEngine.InputSystem.LowLevel.InputStateHistory other); + public System.UInt16 buttons; + public System.UInt16 clickCount; + public System.UInt64 handle; + public SteamHandle(System.UInt64 handle) {} + public static System.UInt64 op_Explicit(UnityEngine.InputSystem.Steam.SteamHandle handle); ")] // Api scraper seems to be unstable with fields with default values, sometimes "= 0;" appears (locally) and sometimes (on CI) doesn't. [Property("Exclusions", @"1.0.0 From 10358ff35ce551573cfee22fe46fb788b1eb1e71 Mon Sep 17 00:00:00 2001 From: Morgan Hoarau Date: Wed, 25 Mar 2026 10:13:52 +0000 Subject: [PATCH 8/9] Normalize API scraper output Make API verification more robust against different scraper versions: -normalize fully-qualified primitive type names to C# aliases - normalize constant expressions (uint.MaxValue/min, hex literals, and shifts) - convert hex/shift expressions to decimal. Replace naive attribute scanning with bracket-depth parsing so attributes using new[] syntax are handled correctly while preserving System.Obsolete attributes. --- .../Tests/InputSystem/APIVerificationTests.cs | 128 ++++++++---------- 1 file changed, 60 insertions(+), 68 deletions(-) diff --git a/Assets/Tests/InputSystem/APIVerificationTests.cs b/Assets/Tests/InputSystem/APIVerificationTests.cs index b5ef96e2b1..6d5247afbb 100644 --- a/Assets/Tests/InputSystem/APIVerificationTests.cs +++ b/Assets/Tests/InputSystem/APIVerificationTests.cs @@ -258,7 +258,7 @@ public void API_MonoBehavioursHaveHelpUrls() // The .api files are platform-specific so we can only compare on the platform // they were built on. - #if UNITY_EDITOR_WIN +#if UNITY_EDITOR_WIN // We disable "API Verification" tests running as part of the validation suite as they give us // false positives (specifically, for setters having changes accessibility from private to protected). @@ -563,7 +563,6 @@ public class ViveWand : UnityEngine.InputSystem.XR.XRControllerWithRumble DontOmitDevice = 2, DontUseShortDisplayNames = 1, IgnoreBindingOverrides = 8, - protected System.UInt32 stateOffsetRelativeToDeviceRoot { get; } OmitDevice = 2, UseShortNames = 4, BufferedBytes = 256, @@ -575,59 +574,6 @@ public class ViveWand : UnityEngine.InputSystem.XR.XRControllerWithRumble Variable = 2, Volatile = 128, Wrap = 8, - public System.UInt32 bit { get; set; } - public System.UInt32 offset { get; set; } - public System.UInt32 sizeInBits { get; set; } - public InputControlLayout.Builder.ControlBuilder WithBitOffset(System.UInt32 bit); - public InputControlLayout.Builder.ControlBuilder WithByteOffset(System.UInt32 offset); - public InputControlLayout.Builder.ControlBuilder WithSizeInBits(System.UInt32 sizeInBits); - public System.UInt32 bit { get; } - public System.UInt32 offset { get; } - public System.UInt32 sizeInBits { get; } - public System.UInt32 stateOffset; - public System.UInt32 deltaStateSizeInBytes { get; } - public System.UInt32 buttons; - public bool GetStateOffsetForEvent(InputControl control, InputEventPtr eventPtr, ref System.UInt32 offset); - public System.UInt32 sizeInBytes { get; set; } - public System.UInt32 sizeInBytes { get; } - public static System.UInt32 updateCount { get; } - public const System.UInt32 AutomaticOffset = 4294967294; - public const System.UInt32 InvalidOffset = 4294967295; - public System.UInt32 bitOffset { get; set; } - public System.UInt32 byteOffset { get; set; } - public System.UInt32 version { get; } - public System.UInt32 version; - public System.UInt32 stateSizeInBytes { get; } - public const System.UInt32 InvalidId = 0; - public System.UInt32 id { get; } - public System.UInt32 parentBoneIndex { get; set; } - public System.UInt32 customSize; - public System.UInt32 samplesAvailable; - public System.UInt32 samplesQueued; - public System.UInt32 frequencyHz; - public System.UInt32 maxBufferSize; - public System.UInt32 numChannels; - public System.UInt32 frequencyHz { get; } - public System.UInt32 maxBufferSize { get; } - public System.UInt32 numChannels { get; } - public HapticCapabilities(System.UInt32 numChannels, System.UInt32 frequencyHz, System.UInt32 maxBufferSize) {} - public System.UInt32 samplesAvailable { get; } - public System.UInt32 samplesQueued { get; } - public HapticState(System.UInt32 samplesQueued, System.UInt32 samplesAvailable) {} - public PrimitiveValue(System.UInt16 value) {} - public PrimitiveValue(System.UInt32 value) {} - public PrimitiveValue(System.UInt64 value) {} - public static PrimitiveValue FromUInt16(System.UInt16 value); - public static PrimitiveValue FromUInt32(System.UInt32 value); - public static PrimitiveValue FromUInt64(System.UInt64 value); - public static PrimitiveValue op_Implicit(System.UInt16 value); - public static PrimitiveValue op_Implicit(System.UInt32 value); - public static PrimitiveValue op_Implicit(System.UInt64 value); - public System.UInt16 ToUInt16(System.IFormatProvider provider = default(System.IFormatProvider)); - public System.UInt32 ToUInt32(System.IFormatProvider provider = default(System.IFormatProvider)); - public System.UInt64 ToUInt64(System.IFormatProvider provider = default(System.IFormatProvider)); - public System.UInt64 handle { get; } - public InputUserAccountHandle(string apiName, System.UInt64 handle) {} public FourCC(char a, char b = , char c = , char d = ) {} public static string ToHumanReadableString(string path, InputControlPath.HumanReadableStringOptions options = InputControlPath.HumanReadableStringOptions.None, InputControl control = default(InputControl)); public static string ToHumanReadableString(string path, out string deviceLayoutName, out string controlPath, InputControlPath.HumanReadableStringOptions options = InputControlPath.HumanReadableStringOptions.None, InputControl control = default(InputControl)); @@ -642,11 +588,8 @@ public struct Record : System.IEquatable previous { get; } public void CopyFrom(UnityEngine.InputSystem.LowLevel.InputStateHistory record); public bool Equals(UnityEngine.InputSystem.LowLevel.InputStateHistory other); - public System.UInt16 buttons; - public System.UInt16 clickCount; - public System.UInt64 handle; - public SteamHandle(System.UInt64 handle) {} - public static System.UInt64 op_Explicit(UnityEngine.InputSystem.Steam.SteamHandle handle); + public SteamHandle(ulong handle) {} + public static ulong op_Explicit(UnityEngine.InputSystem.Steam.SteamHandle handle); ")] // Api scraper seems to be unstable with fields with default values, sometimes "= 0;" appears (locally) and sometimes (on CI) doesn't. [Property("Exclusions", @"1.0.0 @@ -735,6 +678,32 @@ private static string FilterIgnoredChanges(string line) if (line.Length == 0) return line; + // Older API scraper versions emitted fully-qualified C# primitive type names (e.g. System.UInt32), + // while newer versions emit C# language aliases (e.g. uint). Normalize to aliases so that a scraper + // version change does not produce false-positive breaking change reports. + line = line + .Replace("System.UInt64", "ulong") + .Replace("System.UInt32", "uint") + .Replace("System.UInt16", "ushort") + .Replace("System.Int64", "long") + .Replace("System.Int32", "int") + .Replace("System.Int16", "short") + .Replace("System.Boolean", "bool") + .Replace("System.Single", "float") + .Replace("System.Double", "double") + .Replace("System.Byte", "byte") + .Replace("System.SByte", "sbyte") + .Replace("System.Char", "char"); + + // Normalize constant expressions that different scraper versions emit differently. + // Older scrapers resolved expressions to decimal; newer scrapers may keep symbolic forms. + line = line.Replace("uint.MaxValue", "4294967295") + .Replace("uint.MinValue", "0"); + line = Regex.Replace(line, @"\b0x([0-9a-fA-F]+)\b", + m => Convert.ToUInt64(m.Groups[1].Value, 16).ToString()); + line = Regex.Replace(line, @"\b(\d+) << (\d+)\b", + m => (ulong.Parse(m.Groups[1].Value) << int.Parse(m.Groups[2].Value)).ToString()); + var pos = 0; while (true) { @@ -742,21 +711,44 @@ private static string FilterIgnoredChanges(string line) while (pos < line.Length && char.IsWhiteSpace(line[pos])) ++pos; - if (pos < line.Length && line[pos] != '[') + if (pos >= line.Length || line[pos] != '[') return line; var startPos = pos; ++pos; - while (pos < line.Length + 1 && !(line[pos] == ']' && line[pos + 1] == ' ')) - ++pos; - ++pos; - var length = pos - startPos - 2; - var attribute = line.Substring(startPos + 1, length); - if (!attribute.StartsWith("System.Obsolete")) + // Find the matching closing ']' using bracket depth tracking. + // This correctly handles new[] syntax in attribute arguments, e.g.: + // [InputControl(aliases = new[] {@"a", @"b"})] public uint buttons; + // The old scraper used Mono.Cecil.CustomAttributeArgument[] (inner ] followed by ,) + // but the new scraper uses new[] {...} where the inner ] is followed by a space, + // which the old naive scan would incorrectly treat as the end of the attribute. + var depth = 1; + while (pos < line.Length && depth > 0) + { + if (line[pos] == '[') depth++; + else if (line[pos] == ']') depth--; + if (depth > 0) ++pos; + } + + if (pos >= line.Length) + return line; // No matching ']' found, bail out. + + ++pos; // Move past the closing ']'. + + // The attribute must be followed by a space to have any content after it. + if (pos >= line.Length || line[pos] != ' ') + return line; + + var attributeContent = line.Substring(startPos + 1, pos - startPos - 2); + if (!attributeContent.StartsWith("System.Obsolete")) { line = line.Substring(0, startPos) + line.Substring(pos + 1); // Snip space after ']'. - pos -= length + 2; + pos = startPos; + } + else + { + ++pos; // Skip the space after the kept Obsolete attribute. } } } From 5f190fa81cb391dfbc0f460ad34558a009e95b6b Mon Sep 17 00:00:00 2001 From: Morgan Hoarau Date: Wed, 25 Mar 2026 10:14:19 +0000 Subject: [PATCH 9/9] Exclude steam API when unavailable Add exclusion block for all UnityEngine.InputSystem.Steam types when UNITY_ENABLE_STEAM_CONTROLLER_SUPPORT is not defined to avoid false positives when the Steam plugin is absent. --- .../Tests/InputSystem/APIVerificationTests.cs | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/Assets/Tests/InputSystem/APIVerificationTests.cs b/Assets/Tests/InputSystem/APIVerificationTests.cs index 6d5247afbb..9ae3cf2ec4 100644 --- a/Assets/Tests/InputSystem/APIVerificationTests.cs +++ b/Assets/Tests/InputSystem/APIVerificationTests.cs @@ -605,6 +605,57 @@ public SteamHandle(ulong handle) {} [ScopedExclusionProperty("1.0.0", "UnityEngine.InputSystem.LowLevel", "public struct KeyboardState : IInputStateTypeInfo", "public fixed byte keys[14];")] // Allow Key.IMESelected to be marked as Obsolete [ScopedExclusionProperty("1.0.0", "UnityEngine.InputSystem", "public enum Key", "IMESelected = 111,")] + +#if !UNITY_ENABLE_STEAM_CONTROLLER_SUPPORT + // Steam support is conditional (#if UNITY_ENABLE_STEAM_CONTROLLER_SUPPORT) and absent when + // the steam plugin is not installed, so all Steam types are excluded from the comparison. + [Property("Exclusions", @"1.0.0 + namespace UnityEngine.InputSystem.Steam + public interface ISteamControllerAPI + public void ActivateActionSet(UnityEngine.InputSystem.Steam.SteamHandle controllerHandle, UnityEngine.InputSystem.Steam.SteamHandle actionSetHandle); + public void ActivateActionSetLayer(UnityEngine.InputSystem.Steam.SteamHandle controllerHandle, UnityEngine.InputSystem.Steam.SteamHandle actionSetLayerHandle); + public void DeactivateActionSetLayer(UnityEngine.InputSystem.Steam.SteamHandle controllerHandle, UnityEngine.InputSystem.Steam.SteamHandle actionSetLayerHandle); + public void DeactivateAllActionSetLayers(UnityEngine.InputSystem.Steam.SteamHandle controllerHandle); + public UnityEngine.InputSystem.Steam.SteamHandle GetActionSetHandle(string actionSetName); + public int GetActiveActionSetLayers(UnityEngine.InputSystem.Steam.SteamHandle controllerHandle, out UnityEngine.InputSystem.Steam.SteamHandle handlesOut); + public SteamAnalogActionData GetAnalogActionData(UnityEngine.InputSystem.Steam.SteamHandle controllerHandle, UnityEngine.InputSystem.Steam.SteamHandle analogActionHandle); + public UnityEngine.InputSystem.Steam.SteamHandle GetAnalogActionHandle(string actionName); + public int GetConnectedControllers(UnityEngine.InputSystem.Steam.SteamHandle[] outHandles); + public UnityEngine.InputSystem.Steam.SteamHandle GetCurrentActionSet(UnityEngine.InputSystem.Steam.SteamHandle controllerHandle); + public SteamDigitalActionData GetDigitalActionData(UnityEngine.InputSystem.Steam.SteamHandle controllerHandle, UnityEngine.InputSystem.Steam.SteamHandle digitalActionHandle); + public UnityEngine.InputSystem.Steam.SteamHandle GetDigitalActionHandle(string actionName); + public void RunFrame(); + public struct SteamAnalogActionData + public bool active { get; set; } + public Vector2 position { get; set; } + public abstract class SteamController : InputDevice + public bool autoActivateSets { get; set; } + public UnityEngine.InputSystem.Steam.SteamHandle currentSteamActionSet { get; } + public abstract UnityEngine.InputSystem.Utilities.ReadOnlyArray steamActionSets { get; } + public UnityEngine.InputSystem.Steam.SteamHandle steamControllerHandle { get; } + protected SteamController() {} + public void ActivateSteamActionSet(UnityEngine.InputSystem.Steam.SteamHandle actionSet); + protected abstract void ResolveSteamActions(ISteamControllerAPI api); + protected abstract void Update(ISteamControllerAPI api); + public struct SteamActionSetInfo + public UnityEngine.InputSystem.Steam.SteamHandle handle { get; set; } + public struct SteamDigitalActionData + public bool active { get; set; } + public bool pressed { get; set; } + public struct SteamHandle : System.IEquatable> + public bool Equals(UnityEngine.InputSystem.Steam.SteamHandle other); + public static bool operator ==(UnityEngine.InputSystem.Steam.SteamHandle a, UnityEngine.InputSystem.Steam.SteamHandle b); + public static bool operator !=(UnityEngine.InputSystem.Steam.SteamHandle a, UnityEngine.InputSystem.Steam.SteamHandle b); + namespace UnityEngine.InputSystem.Steam.Editor + public static class SteamIGAConverter + public static string ConvertInputActionsToSteamIGA(System.Collections.Generic.IEnumerable actionMaps, string locale = @""english""); + public static string ConvertInputActionsToSteamIGA(InputActionAsset asset, string locale = @""english""); + public static string GenerateInputDeviceFromSteamIGA(string vdf, string namespaceAndClassName); + public static string GetSteamControllerInputType(InputAction action); + public static System.Collections.Generic.Dictionary ParseVDF(string vdf); + ")] +#endif + public void API_MinorVersionsHaveNoBreakingChanges() { var currentVersion = CoreTests.PackageJson.ReadVersion();