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=>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=>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=>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=>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=>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=>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=>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=>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=>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=>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.
Команда Resharper была уведомлена; Анализ кода не учитывает настройки на вкладке свойств проекта "Контракты кода", Поддержка контрактов Microsoft Code.