ReSharper - возможное нулевое назначение при использовании Microsoft.Contracts

Есть ли способ указать ReSharper, что нулевая ссылка не будет возникать из-за того, что Design-by-Contract требует проверки? Например, следующий код вызовет предупреждение (Possible 'null' assignment to entity marked with 'NotNull' attribute) в ReSharper по строкам 7 и 8:

private Dictionary<string, string> _Lookup = new Dictionary<string, string>();

public void Foo(string s)
{
    Contract.Requires(!String.IsNullOrEmpty(s));

    if (_Lookup.ContainsKey(s))
        _Lookup.Remove(s);
}

Что действительно странно, так это то, что если вы удалите Contract.Requires(...) сообщение ReSharper исчезает.

Обновить

Я нашел решение с помощью ExternalAnnotations, которое Майк также упомянул ниже. Вот пример того, как сделать это для функции в Microsoft.Contracts:

  • Создайте каталог с именем Microsoft.Contracts под ExternalAnnotations ReSharper каталог.
  • Далее создайте файл с именем Microsoft.Contracts.xml и заселить вот так:

<assembly name="Microsoft.Contracts">
    <member name="M:System.Diagnostics.Contracts.Contract.Requires(System.Boolean)">
        <attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/>
        <parameter name="condition">
            <attribute ctor="M:JetBrains.Annotations.AssertionConditionAttribute.#ctor(JetBrains.Annotations.AssertionConditionType)">
                <argument>0</argument>
            </attribute>
        </parameter>
    </member>
</assembly>

  • Перезапустите Visual Studio, и сообщение исчезнет!

7 ответов

Решение

Примечание. Начиная с текущей версии EAP версии R# 8.0 эта функция включена.


Вот решение для текущей (то есть.NET 4.0) версии контрактов кода:

внутри ...\ExternalAnnotations\mscorlib\Contracts.xmlдобавьте следующее:

<assembly name="mscorlib">
    <member name="M:System.Diagnostics.Contracts.Contract.Assert(System.Boolean)">
        <attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/>
        <parameter name="condition">
            <attribute ctor="M:JetBrains.Annotations.AssertionConditionAttribute.#ctor(JetBrains.Annotations.AssertionConditionType)">
                <argument>0</argument>
            </attribute>
        </parameter>
    </member>
    <member name="M:System.Diagnostics.Contracts.Contract.Assert(System.Boolean, System.String)">
        <attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/>
        <parameter name="condition">
            <attribute ctor="M:JetBrains.Annotations.AssertionConditionAttribute.#ctor(JetBrains.Annotations.AssertionConditionType)">
                <argument>0</argument>
            </attribute>
        </parameter>
    </member>
    <member name="M:System.Diagnostics.Contracts.Contract.Assume(System.Boolean)">
        <attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/>
        <parameter name="condition">
            <attribute ctor="M:JetBrains.Annotations.AssertionConditionAttribute.#ctor(JetBrains.Annotations.AssertionConditionType)">
                <argument>0</argument>
            </attribute>
        </parameter>
    </member>
    <member name="M:System.Diagnostics.Contracts.Contract.Assume(System.Boolean, System.String)">
        <attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/>
        <parameter name="condition">
            <attribute ctor="M:JetBrains.Annotations.AssertionConditionAttribute.#ctor(JetBrains.Annotations.AssertionConditionType)">
                <argument>0</argument>
            </attribute>
        </parameter>
    </member>
    <member name="M:System.Diagnostics.Contracts.Contract.Requires(System.Boolean)">
        <attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/>
        <parameter name="condition">
            <attribute ctor="M:JetBrains.Annotations.AssertionConditionAttribute.#ctor(JetBrains.Annotations.AssertionConditionType)">
                <argument>0</argument>
            </attribute>
        </parameter>
    </member>
    <member name="M:System.Diagnostics.Contracts.Contract.Requires``1(System.Boolean)">
        <attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/>
        <parameter name="condition">
            <attribute ctor="M:JetBrains.Annotations.AssertionConditionAttribute.#ctor(JetBrains.Annotations.AssertionConditionType)">
                <argument>0</argument>
            </attribute>
        </parameter>
    </member>
    <member name="M:System.Diagnostics.Contracts.Contract.Requires(System.Boolean,System.String)">
        <attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/>
        <parameter name="condition">
            <attribute ctor="M:JetBrains.Annotations.AssertionConditionAttribute.#ctor(JetBrains.Annotations.AssertionConditionType)">
                <argument>0</argument>
            </attribute>
        </parameter>
    </member>
    <member name="M:System.Diagnostics.Contracts.Contract.Requires``1(System.Boolean,System.String)">
        <attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/>
        <parameter name="condition">
            <attribute ctor="M:JetBrains.Annotations.AssertionConditionAttribute.#ctor(JetBrains.Annotations.AssertionConditionType)">
                <argument>0</argument>
            </attribute>
        </parameter>
    </member>
    <member name="M:System.Diagnostics.Contracts.Contract.Invariant(System.Boolean)">
        <attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/>
        <parameter name="condition">
            <attribute ctor="M:JetBrains.Annotations.AssertionConditionAttribute.#ctor(JetBrains.Annotations.AssertionConditionType)">
                <argument>0</argument>
            </attribute>
        </parameter>
    </member>
    <member name="M:System.Diagnostics.Contracts.Contract.Invariant(System.Boolean,System.String)">
        <attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/>
        <parameter name="condition">
            <attribute ctor="M:JetBrains.Annotations.AssertionConditionAttribute.#ctor(JetBrains.Annotations.AssertionConditionType)">
                <argument>0</argument>
            </attribute>
        </parameter>
    </member>
</assembly>

Я хотел бы добавить, что для людей, пишущих свои собственные методы утверждений и тому подобное, вы можете включить эти атрибуты без внешнего XML-файла. В Visual Studio перейдите в ReSharper > Options > Code Annotations и нажмите Copy default implementation to clipboard кнопка. Затем создайте новый файл (где угодно в своем решении) и вставьте код из буфера обмена. Теперь вы можете создавать такие методы:

public class Require
{
    [AssertionMethod]
    public static void That(
        [AssertionCondition(AssertionConditionType.IS_TRUE)] 
        bool requiredCondition,
        string message = null)
    {
        ...
    }
...
}

Теперь любой вызов Require.That(a != null) покажет ReSharper, что вы не можете пройти эту линию, если a нулевой. В отличие от метода ExternalAnnotations, он будет работать для всех, кто использует ваши методы, без какой-либо дополнительной работы с их стороны.

Обновить

Resharper изменил свою модель аннотации контракта с версии 7. Вот как будет выглядеть вышеупомянутый метод сейчас:

public class Require
{
    [ContractAnnotation("requiredCondition:false => halt")]
    public static void That(
        bool requiredCondition,
        string message = null)
    {
        ...
    }
...
}

Я думаю, что вы можете, но это не тривиально. Посмотрите онлайн-справку Resharper для аннотации кода

Они аннотировали классы BCL и инфраструктуру NUnit (и многое другое) для расширения возможностей проверки кода Resharpers.

Например, с NUnit утверждает, что они аннотированы AssertionMethodAttribute. Это говорит проверке кода Resharpers, что если вы прошли Assert.IsNotNull(foo); тогда foo не должен быть нулевым и больше не будет выдавать предупреждение "Возможное нулевое назначение...".

Вы можете создать xml-файл, аннотирующий метод Contracts.Requires, чтобы указать, что он похож на Assert.

Причина, по которой сообщение исчезает при удалении утверждения, заключается в том, что R# по умолчанию работает в "оптимистическом" режиме. Предполагается, что все не является нулевым, пока вы не сделаете что-то, что указывает на то, что оно может быть фактически нулевым Вот что происходит, когда вы добавляете вызов String.IsNullOrEmpty, Вы утверждаете, что s на самом деле может быть нулевым. Он просто не знает, что Contract.Requires Метод остановит выполнение, если это так, но вы решили с аннотацией.

В R# 5.0 вы можете перейти в пессимистический режим, который предполагает худшее на каждом углу.

Я взял XML Поргеса и добавил аннотации для методов Assert и Assume. Я напишу этот ответ на случай, если другие люди захотят добавить больше методов.

<assembly name="mscorlib">
    <member name="M:System.Diagnostics.Contracts.Contract.Assert(System.Boolean)">
        <attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/>
        <parameter name="condition">
            <attribute ctor="M:JetBrains.Annotations.AssertionConditionAttribute.#ctor(JetBrains.Annotations.AssertionConditionType)">
                <argument>0</argument>
            </attribute>
        </parameter>
    </member>
    <member name="M:System.Diagnostics.Contracts.Contract.Assert(System.Boolean, System.String)">
        <attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/>
        <parameter name="condition">
            <attribute ctor="M:JetBrains.Annotations.AssertionConditionAttribute.#ctor(JetBrains.Annotations.AssertionConditionType)">
                <argument>0</argument>
            </attribute>
        </parameter>
    </member>
    <member name="M:System.Diagnostics.Contracts.Contract.Assume(System.Boolean)">
        <attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/>
        <parameter name="condition">
            <attribute ctor="M:JetBrains.Annotations.AssertionConditionAttribute.#ctor(JetBrains.Annotations.AssertionConditionType)">
                <argument>0</argument>
            </attribute>
        </parameter>
    </member>
    <member name="M:System.Diagnostics.Contracts.Contract.Assume(System.Boolean, System.String)">
        <attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/>
        <parameter name="condition">
            <attribute ctor="M:JetBrains.Annotations.AssertionConditionAttribute.#ctor(JetBrains.Annotations.AssertionConditionType)">
                <argument>0</argument>
            </attribute>
        </parameter>
    </member>
    <member name="M:System.Diagnostics.Contracts.Contract.Requires(System.Boolean)">
        <attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/>
        <parameter name="condition">
            <attribute ctor="M:JetBrains.Annotations.AssertionConditionAttribute.#ctor(JetBrains.Annotations.AssertionConditionType)">
                <argument>0</argument>
            </attribute>
        </parameter>
    </member>
    <member name="M:System.Diagnostics.Contracts.Contract.Requires``1(System.Boolean)">
        <attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/>
        <parameter name="condition">
            <attribute ctor="M:JetBrains.Annotations.AssertionConditionAttribute.#ctor(JetBrains.Annotations.AssertionConditionType)">
                <argument>0</argument>
            </attribute>
        </parameter>
    </member>
    <member name="M:System.Diagnostics.Contracts.Contract.Requires(System.Boolean,System.String)">
        <attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/>
        <parameter name="condition">
            <attribute ctor="M:JetBrains.Annotations.AssertionConditionAttribute.#ctor(JetBrains.Annotations.AssertionConditionType)">
                <argument>0</argument>
            </attribute>
        </parameter>
    </member>
    <member name="M:System.Diagnostics.Contracts.Contract.Requires``1(System.Boolean,System.String)">
        <attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/>
        <parameter name="condition">
            <attribute ctor="M:JetBrains.Annotations.AssertionConditionAttribute.#ctor(JetBrains.Annotations.AssertionConditionType)">
                <argument>0</argument>
            </attribute>
        </parameter>
    </member>
    <member name="M:System.Diagnostics.Contracts.Contract.Invariant(System.Boolean)">
        <attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/>
        <parameter name="condition">
            <attribute ctor="M:JetBrains.Annotations.AssertionConditionAttribute.#ctor(JetBrains.Annotations.AssertionConditionType)">
                <argument>0</argument>
            </attribute>
        </parameter>
    </member>
    <member name="M:System.Diagnostics.Contracts.Contract.Invariant(System.Boolean,System.String)">
        <attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/>
        <parameter name="condition">
            <attribute ctor="M:JetBrains.Annotations.AssertionConditionAttribute.#ctor(JetBrains.Annotations.AssertionConditionType)">
                <argument>0</argument>
            </attribute>
        </parameter>
    </member>
</assembly>

Resharper изменил свою модель аннотации контракта с версии 7.

Вам нужен другой файл. Новое местоположение (я думаю, только для приложений Metro): "C:\Program Files (x86)\JetBrains\ReSharper\v7.1\Bin\ExternalAnnotations\.NETCore\System.Diagnostics.Contracts\Contracts.xml"

Я использую Visual Studio 2012 и.Net 4.5 и Resharper 7.1.

Содержание:

<assembly name="System.Diagnostics.Contracts">
    <member name="M:System.Diagnostics.Contracts.Contract.Assert(System.Boolean)">
        <attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/>
        <attribute ctor="M:JetBrains.Annotations.ContractAnnotationAttribute.#ctor(System.String)">
            <argument>condition:false=&gt;halt</argument>
        </attribute>
    </member>
    <member name="M:System.Diagnostics.Contracts.Contract.Assert(System.Boolean, System.String)">
        <attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/>
        <attribute ctor="M:JetBrains.Annotations.ContractAnnotationAttribute.#ctor(System.String)">
            <argument>condition:false=&gt;halt</argument>
        </attribute>
    </member>
    <member name="M:System.Diagnostics.Contracts.Contract.Assume(System.Boolean)">
        <attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/>
        <attribute ctor="M:JetBrains.Annotations.ContractAnnotationAttribute.#ctor(System.String)">
            <argument>condition:false=&gt;halt</argument>
        </attribute>
    </member>
    <member name="M:System.Diagnostics.Contracts.Contract.Assume(System.Boolean, System.String)">
        <attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/>
        <attribute ctor="M:JetBrains.Annotations.ContractAnnotationAttribute.#ctor(System.String)">
            <argument>condition:false=&gt;halt</argument>
        </attribute>
    </member>
    <member name="M:System.Diagnostics.Contracts.Contract.Requires(System.Boolean)">
        <attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/>
        <attribute ctor="M:JetBrains.Annotations.ContractAnnotationAttribute.#ctor(System.String)">
            <argument>condition:false=&gt;halt</argument>
        </attribute>
    </member>
    <member name="M:System.Diagnostics.Contracts.Contract.Requires``1(System.Boolean)">
        <attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/>
        <attribute ctor="M:JetBrains.Annotations.ContractAnnotationAttribute.#ctor(System.String)">
            <argument>condition:false=&gt;halt</argument>
        </attribute>
    </member>
    <member name="M:System.Diagnostics.Contracts.Contract.Requires(System.Boolean,System.String)">
        <attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/>
        <attribute ctor="M:JetBrains.Annotations.ContractAnnotationAttribute.#ctor(System.String)">
            <argument>condition:false=&gt;halt</argument>
        </attribute>
    </member>
    <member name="M:System.Diagnostics.Contracts.Contract.Requires``1(System.Boolean,System.String)">
        <attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/>
        <attribute ctor="M:JetBrains.Annotations.ContractAnnotationAttribute.#ctor(System.String)">
            <argument>condition:false=&gt;halt</argument>
        </attribute>
    </member>
    <member name="M:System.Diagnostics.Contracts.Contract.Invariant(System.Boolean)">
        <attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/>
        <attribute ctor="M:JetBrains.Annotations.ContractAnnotationAttribute.#ctor(System.String)">
            <argument>condition:false=&gt;halt</argument>
        </attribute>
    </member>
    <member name="M:System.Diagnostics.Contracts.Contract.Invariant(System.Boolean,System.String)">
        <attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/>
        <attribute ctor="M:JetBrains.Annotations.ContractAnnotationAttribute.#ctor(System.String)">
            <argument>condition:false=&gt;halt</argument>
        </attribute>
    </member>
</assembly>

TL;DR - добавить условный символ компиляции CONTRACTS_FULLк вашему проекту.

Contract.Requires(...)Метод пуст и отключен, если только вы не включите и не воспользуетесь программой переписки кода. Запустив программу перезаписи вручную или (обычно) включив ее через свойства проекта Visual Studio, вы сохранитеContract.Requires(...)код в ваших скомпилированных и переписанных двоичных файлах. Вы знаете, что код будет работать, и, игнорируя предупреждение Resharper, вы можете запустить его и протестировать.

В чем проблема тогда? Решарпер не знает, что контракты кода выполняются, так как они действительно вводятся только во время (после) компиляции. В глазах Resharper он отключен так же, какDEBUGСимвол препроцессора работает и как Visual Studio выделяет области вашего кода, которые не будут частью ваших скомпилированных двоичных файлов.

#ifdef DEBUG
    Console.WriteLine("I'm in DEBUG mode, so this is probably a Debug build.");
#else
    Console.WriteLine("Let's assume this is a Release build.");
#endif

В соответствии с руководством пользователя Code Contracts (глава 2, первый абзац) и исходным кодом в ContractExtensions.cs(входит в папку установки Code Contracts),CONTRACTS_FULL должен быть установлен перед компиляцией. Контрактные методы фактически реализуются с[ConditionalAttribute("CONTRACTS_FULL")] и игнорируется (не включается во время компиляции), если флаг не установлен. Resharper уважает этот флаг и предполагает, что функция не будет работать, если она не установлена.

[ConditionalAttribute("CONTRACTS_FULL")]
public static void Requires(bool condition) { ... }

Решение: Добавьте условный символ компиляции CONTRACTS_FULL к вашему проекту. См. Использование кодовых контрактов Visual Studio и Resharper от Henning Krause.

http://www.infinitec.de/image.axd?picture=Windows-Live-Writer/Using-CodeContracts-with-Resharper/05970F39/ConditionalSymbol.png

Команда Resharper была уведомлена; Анализ кода не учитывает настройки на вкладке свойств проекта "Контракты кода", Поддержка контрактов Microsoft Code.

Другие вопросы по тегам