📜 ⬆️ ⬇️

Tip: Using ReSharper with Microsoft CodeContracts

I decided to write a short note after a couple of hours of proceedings - the answers are not immediately in the network, in English as well.

About Microsoft CodeContracts on Habré have already been written , it is a library and toolkit for Visual Studio, allowing to use elements of "contract programming" in C #.

We began to use CodeContracts (hereinafter - simply “contracts”) in our projects relatively recently, and, in general, we are satisfied, although we received an extra few seconds of waiting for the compile time.
')
Well, and, of course, we use ReSharper, which does not need additional presentation.

But there are a couple of nuances, which consist in the fact that in order to work effectively, these two tools need a little bit to make friends with each other.

Nuance 1. Method invocation is skipped

First of all, you will notice that by default ReSharper thinks that some calls of contracts like the following will be excluded from the assembly, and mercilessly “suspect” them:

ReSharper thinks the compiler will exclude contract checking

In fact, these calls are not excluded from the assembly. Some contract methods, such as Contract.Requires and Contract.Ensures , are marked with the [Conditional("CONTRACTS_FULL")] attribute, but the CONTRACTS_FULL flag is set by the contract engine before compilation, already at the project assembly stage, and therefore not visible at the ReSharper editing stage u, which looks at the list of given compilation symbols in the settings.

After the usual compilation, the contract mechanisms instrument the resulting IL code, and replace these calls with others from the __ConstractRuntime class. You can easily see it with Reflector (hurry while it is still free).

But Contract.Assert calls are present in the code from the very beginning, and they also work at run time, without rewriting. They are labeled not only as [Conditional("CONTRACTS_FULL")] , but also as Conditional("DEBUG") . As a result, for the debug configuration (where the DEBUG symbol is defined) ReSharper and he himself understands that this call is real.

So how do you explain to ReSharper that Contract.Requires is also an “honest” call and should not be “checked” in the editor? It is necessary just for the project configuration in which you have included checks for runtime contracts (options of the project “Code Contracts”> “Runtime Checking”) and also manually set the conditional compilation symbol CONSTRACTS_FULL on the “Build” tab.

ReSharper will immediately “see” the missing methods! Of course, it is not very convenient to repeat the value of the flags that are already set up on one tab, and also on another tab, but at least you can do this once per project configuration.

JetBrains is already aware of this problem, and, hopefully, in future versions, contracts will be supported by ReSharper out of the box.

Nuance 2. Possible NullReferenceException

Another setting that needs to be done to maximize the use of ReSharper with contracts is to set up value analysis (value analysis). In particular, ReSharper is able to detect the presence / absence of checks on method parameters to null and, accordingly, warns about a possible throw of a NullReferenceException exception when accessing methods of an argument object in the absence of such a check:

ReSharper does not understand that contracts check the argument to null

At the same time, ReSharper cannot guess by default that adding a contract precondition to Contract.Requires(visitor != null) provides the necessary null check.

Fortunately, the so-called mechanism is provided in ReSharper. External annotations, which allows you to mark up classes and methods from third-party libraries with additional meta-information for ReSharper. Specifically, in order for ReSharper to “recognize” contract checks, it needs to add a special xml file with a meta description of the Contract class methods to the folder "\ Bin \ ExternalAnnotations \ mscorlib \":

 <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> 

After that, everything falls into place, and again you can rely on the prompts of your favorite tool, without thinking about when he is mistaken about using CodeContracts!

Source: https://habr.com/ru/post/114067/


All Articles