CA2020: IntPtr/UIntPtr の組み込み演算子によって発生する動作変更を防止する

プロパティ
ルール ID CA2020
Title IntPtr/UIntPtr の組み込み演算子によって発生する動作変更を防止する
[カテゴリ] 信頼性
修正が中断ありか中断なしか なし
.NET 8 では既定で有効 提案として

原因

この規則は、.NET 6 と .NET 7 の間で、新しい組み込み演算子 IntPtrUIntPtr がもたらす動作変更が検出された場合に適用されます。

規則の説明

数値 IntPtr 機能を使い、IntPtrUIntPtr では変換、単項演算、二項演算の組み込み演算子を取得しました。 .NET 6 以前のバージョンにおける以前のユーザー定義演算子と比較して、これらの演算子は、checked コンテキスト内でオーバーフローしたときにスローしたり、unchecked コンテキストでスローしなかったりする場合があります。 .NET 7 にアップグレードする場合に、この動作変更が発生する可能性があります。

影響を受ける API の一覧

演算子 Context .NET 7 の場合 .NET 6 以前の場合
演算子 +(IntPtr, int) checked オーバーフロー時にスローする オーバーフロー時にスローしない checked(intPtrVariable + 2);
演算子 -(IntPtr, int) checked オーバーフロー時にスローする オーバーフロー時にスローしない checked(intPtrVariable - 2);
explicit 演算子 IntPtr(long) unchecked オーバーフロー時にスローしない 32 ビット コンテキストでスローする場合がある (IntPtr)longVariable;
explicit 演算子 void*(IntPtr) checked オーバーフロー時にスローする オーバーフロー時にスローしない checked((void*)intPtrVariable);
explicit 演算子 IntPtr(void*) checked オーバーフロー時にスローする オーバーフロー時にスローしない checked((IntPtr)voidPtrVariable);
explicit 演算子 int(IntPtr) unchecked オーバーフロー時にスローしない 64 ビット コンテキストでスローする場合がある (int)intPtrVariable;
演算子 +(UIntPtr, int) checked オーバーフロー時にスローする オーバーフロー時にスローしない checked(uintPtrVariable + 2);
演算子 -(UIntPtr, int) checked オーバーフロー時にスローする オーバーフロー時にスローしない checked(uintPtrVariable - 2);
explicit 演算子 UIntPtr(ulong) unchecked オーバーフロー時にスローしない 32 ビット コンテキストでスローする場合がある (UIntPtr)uLongVariable
explicit 演算子 uint(UIntPtr) unchecked オーバーフロー時にスローしない 64 ビット コンテキストでスローする場合がある (uint)uintPtrVariable

違反の修正方法

コードを調べて、フラグが設定された式によって動作変更が発生する可能性があるかどうかを判断し、次のオプションから診断内容を修正する適切な方法を選択します。

修正オプション:

  • 式によって動作変更が発生しない場合:
    • IntPtr または UIntPtr 型がネイティブの int または uint として使用されている場合は、型を nint または nuint に変更します。
    • IntPtr または UIntPtr 型がネイティブ ポインターとして使用されている場合は、型を対応するネイティブ ポインター型に変更します。
    • 変数の型を変更できない場合は、警告を抑制します。
  • 式によって動作変更が発生する可能性がある場合は、それを checked または unchecked ステートメントでラップし、以前の動作を保持します。

違反:

using System;

public unsafe class IntPtrTest
{
    IntPtr intPtrVariable;
    long longVariable;

    void Test ()
    {
        checked
        {
            IntPtr result = intPtrVariable + 2; // Warns: Starting with .NET 7 the operator '+' will throw when overflowing in a checked context. Wrap the expression with an 'unchecked' statement to restore the .NET 6 behavior.

            result = intPtrVariable - 2; // Starting with .NET 7 the operator '-' will throw when overflowing in a checked context. Wrap the expression with an 'unchecked' statement to restore the .NET 6 behavior.

            void* voidPtrVariable = (void*)intPtrVariable; // Starting with .NET 7 the explicit conversion '(void*)IntPtr' will throw when overflowing in a checked context. Wrap the expression with an 'unchecked' statement to restore the .NET 6 behavior.

            result = (IntPtr)voidPtrVariable; // Starting with .NET 7 the explicit conversion '(IntPtr)void*' will throw when overflowing in a checked context. Wrap the expression with an 'unchecked' statement to restore the .NET 6 behavior.
        }

        intPtrVariable = (IntPtr)longVariable; // Starting with .NET 7 the explicit conversion '(IntPtr)Int64' will not throw when overflowing in an unchecked context. Wrap the expression with a 'checked' statement to restore the .NET 6 behavior.

        int a = (int)intPtrVariable; // Starting with .NET 7 the explicit conversion '(Int32)IntPtr' will not throw when overflowing in an unchecked context. Wrap the expression with a 'checked' statement to restore the .NET 6 behavior.
    }
}

修正:

  • 式によって動作変更が発生せず、IntPtr または UIntPtr 型がネイティブの int または uint として使用されている場合は、型を nint または nuint に変更します。
using System;

public unsafe class IntPtrTest
{
    nint intPtrVariable; // type changed to nint
    long longVariable;

    void Test ()
    {
        checked
        {
            nint result = intPtrVariable + 2; // no warning

            result = intPtrVariable - 2;

            void* voidPtrVariable = (void*)intPtrVariable;

            result = (nint)voidPtrVariable;
        }

        intPtrVariable = (nint)longVariable;

        int a = (int)intPtrVariable;
    }
}
  • 式によって動作変更が発生する可能性がある場合は、それを checked または unchecked ステートメントでラップし、以前の動作を保持します。
using System;

public unsafe class IntPtrTest
{
    IntPtr intPtrVariable;
    long longVariable;

    void Test ()
    {
        checked
        {
            IntPtr result = unchecked(intPtrVariable + 2); // wrap with unchecked

            result = unchecked(intPtrVariable - 2);

            void* voidPtrVariable = unchecked((void*)intPtrVariable);

            result = unchecked((IntPtr)voidPtrVariable);
        }

        intPtrVariable = checked((IntPtr)longVariable); // wrap with checked

        int a = checked((int)intPtrVariable);
    }
}

どのようなときに警告を抑制するか

式によって動作変更が発生しない場合は、この規則からの警告を抑制しても安全です。

関連項目