Подтвердить что ты не робот

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

Есть ли способ указать ReSharper, что нулевая ссылка не будет возникать из-за конструирования по контракту Требует проверки? Например, следующий код поднимет предупреждение (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, и сообщение исчезнет!
4b9b3361

Ответ 1

Примечание: с текущей RAP 8.0 EAP эта функциональность включена.


Здесь решение для текущей (то есть .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>

Ответ 2

Я хотел бы добавить, что для людей, которые пишут собственные методы утверждения и т.д., вы можете включить эти атрибуты без внешнего 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 равно null. В отличие от метода ExternalAnnotations, это будет работать для тех, кто использует ваши методы, без какой-либо дополнительной работы с их стороны.

Update

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

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

Ответ 3

Я думаю, вы можете, но это не тривиально. Взгляните на Resharper online help для аннотации кода

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

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

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

Ответ 4

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

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

Ответ 5

Я взял XML Porges и добавил аннотации для методов 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>

Ответ 6

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

Вам нужен другой файл. Новое местоположение (я думаю, только для приложений Metro): "C:\Program Files (x86)\JetBrains\ReSharper\v7.1\Bin\ExternalAnnotes \.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>

Ответ 7

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

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

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

#ifdef DEBUG
    Console.WriteLine("I'm in DEBUG mode, so this is probably a Debug build.");
#else
    Console.WriteLine("Let 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 Хеннинга Краузе.


(источник: infinitec.de)

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