syghの新フラグメント置き場

プログラミングTipsやコード断片の保管場所です。お絵描きもときどき載せます。

Visual Studio 2015のコード整形機能のバグ

Visual Studioのコードエディターでは、C#コードを入力する際、ペースト時や閉じ中カッコ0x7Dあるいはセミコロン0x3Bを入力したタイミングなどで自動的にフォーマット(コード整形)が発動し、タブや空白などが自動調整されます。以前のバージョンのVisual Studioでは、C++コードでは明示的なフォーマット機能(ショートカット:Ctrl+K, Ctrl+F)のみをサポートしていましたが、Visual Studio 2015では、C++でもC#に近いオートフォーマットがデフォルトで発動するようになりました。
ただし、VS2015のソースコードエディターは、Update 3時点でもオートフォーマットのバグをいくつか抱えています。大半はVS2013時点では存在していなかったリグレッションです。VS2017は試していないので、不具合が修正されているかどうか、それとも依然として存在するかどうかは分かりません。なお、たとえMS Connectに不具合報告しても、VS2015では修正されず、どうせ新しいバージョンでしか修正されないことが分かり切っているので、報告するつもりはありません。また、せっかくユーザーが報告した不具合のチケットを、あろうことか勝手に削除することがあるようなので、MS Connectにはもう報告しないつもりです。傲岸不遜で恥知らずなMSにはもう協力したくありません。

フォーマット機能は、コーディング規約にそぐわないコードを整形するときは便利なのですが、意図しない形に勝手に自動整形されてしまうとイライラの原因になるだけです。また、明示的なフォーマットと、自動フォーマットとで整形結果が異なるときもあります。特にC#ラムダ式まわりで違いが具現化するのでイライラします。オートフォーマットが嫌いな人は、Visual Studioの設定で無効化しておくのが無難だと思います。

例1 (C++)

OpenGLDirect3Dで頂点バッファの初期データを定義するときによくある例です。

namespace
{
    struct MyVertex { float X, Y, Z; float Color[4]; };
    // 以下の行をカット&ペーストすると、Z の後ろのコンマと Color の間の空白がなくなる。
    const MyVertex Vertex1 = { 0.0f, +0.5f, 0.0f, { 1.0f, 0.0f, 0.0f, 1.0f } };
}

例2 (C++)

MFCが自動生成するコードとしてよく見られるものを、抽象化した例です。

class CMyDialog
{
public:
    CMyDialog() {} // 標準コンストラクター

// ダイアログ データ
    enum { IDD = 1 };
};

上記コードに対して明示的なフォーマットを実行したときに期待される動作は「// ダイアログ データの行が1レベルだけインデントされること」ですが、実際にフォーマットを実行しても何も起こりません。一方、カット&ペーストすると、// ダイアログ データ// 標準コンストラクターの開始桁位置と揃うように勝手にタブ文字および空白が挿入されてしまいます。

例3 (C++, C#)

void MyFunc()
{
    const int MyConst = 0; // 定数。
    // 処理。
    if (false) {}
}

上記コードに対して明示的なフォーマットを実行したときに期待される動作は「何も変更されないこと」ですが、C#の場合は、// 処理。// 定数。の開始桁位置と揃うように勝手にタブ文字および空白が挿入されてしまいます。また、C++においても、カット&ペーストするとC#同様に勝手な整形が実行されてしまいます。
行末尾のコメントを禁止するコーディング規約を採用している場合はこの問題に遭遇することはないと思いますが、勝手に桁位置を揃えられてしまうとイライラしますね。

例4 (C#)

class MyClass
{
    // ここで
    // #if false
    // #
    // と入力すると、既存の #region および #endregion のインデントが消失する。
    #region Fields
    int field;
    #endregion
}

通常、#regionおよび#endregionは対象となるコードブロックのインデントレベルに合わせてインデントされますが、このバグが発動するとインデントが消失します。

他にも、

    {
        ...
    }

というブロックを含むC++ソースファイルをフォーマットすると、なぜか

    {
        ...
}

というようにブロックのインデントが消失することもあります。このバグは発動条件が不明で、まったく同じソースなのに現象が出たり出なかったりと不安定です。確かVS2012か2013あたりまでは、少なくともこういった不安定な挙動を示すことはなかったように記憶しています。