28

I have the following code contract:

public void F(string x)
{
    Contract.Requires(!string.IsNullOrWhiteSpace(x));

    throw new NotImplementedException();
}

When compiling, I get the following warning:

warning CC1036: Detected call to method 'System.String.IsNullOrWhiteSpace(System.String)' without [Pure] in contracts of method [...]

How to deal with it?

What's odd, is that I'm also using string.IsNullOrEmpty, which isn't marked as [Pure] as well, in other contracts and the rewriter does not have a problem with that.

My Contract Rewriter's Version is 1.9.10714.2.

This is the relevant part from the implementation of String class I'm using (retrieved from metadata):

#region Assembly mscorlib.dll, v4.0.0.0
// C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.6.1\mscorlib.dll
#endregion

using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Reflection;
using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices;
using System.Security;
using System.Text;

namespace System
{
    // Summary:
    //     Represents text as a series of Unicode characters.To browse the .NET Framework
    //     source code for this type, see the Reference Source.
    [Serializable]
    [ComVisible(true)]
    public sealed class String : IComparable, ICloneable, IConvertible, IEnumerable, IComparable<string>, IEnumerable<char>, IEquatable<string>
    {

    // [...]

        //
        // Summary:
        // [...]
        public static bool IsNullOrEmpty(string value);
        //
        // Summary:
        // [...]
        public static bool IsNullOrWhiteSpace(string value);

Why is the [Pure] attribute missing?

leppie
  • 115,091
  • 17
  • 196
  • 297
BartoszKP
  • 34,786
  • 15
  • 102
  • 130
  • [`IsNullOrWhiteSpace`](http://referencesource.microsoft.com/#mscorlib/system/string.cs,55e241b6143365ef,references) **is** `[Pure]` already. Perhaps it is not in your framework version, which is ... ? – Sinatr Jan 05 '16 at 13:38
  • @Sinatr Interesting - as mentioned in the tags, my "Target framework" is set to 4.6.1. Is it possible that the rewriter works in a different context? – BartoszKP Jan 05 '16 at 13:39
  • Looks like a [bug](https://social.msdn.microsoft.com/Forums/en-US/1d3c3bca-66fb-4e94-8eb1-f93763875793/bug-report-cc-156091110-pure-missing-from-stringisnullorwhitespace-for-net-451?forum=codecontracts). – Sinatr Jan 05 '16 at 13:44
  • @Sinatr This bug refers to 4.5, I'm using 4.6.1. – BartoszKP Jan 05 '16 at 13:47
  • @Sinatr Strange - my sources (from metadata) look different. – BartoszKP Jan 05 '16 at 13:56
  • The method is marked as Pure for .NET 4.6.1 in the .NET Framework source code: http://referencesource.microsoft.com/#mscorlib/system/string.cs,8281103e6f23cb5c – Alex Jan 19 '16 at 13:11
  • @Jaco Yes, Sinatr already have pointed this out in the very first comment. That's why I've pasted what my VS displays when showing `string`'s interface from metadata. – BartoszKP Jan 19 '16 at 14:31
  • @Jaco I'll be happy to improve my question if you think that something is wrong or some important information is missing. – BartoszKP Jan 19 '16 at 14:51
  • 3
    `[Pure]` shouldn't even be necessary on a System.String method. According to the [documentation](http://research.microsoft.com/en-us/projects/contracts/userdoc.pdf) (section 5.4) the tools automatically assume purity for "Any method whose fully qualified name begins with System.Diagnostics.Contracts.Contract, System.String, System.IO.Path, or System.Type." – Mark Waterman Jan 21 '16 at 02:03
  • @MarkWaterman Thanks! Very valuable information! – BartoszKP Jan 21 '16 at 12:22

5 Answers5

8

Here we have two points:

1. Why is the [Pure] attribute missing in string class for IsNullorWhiteSpace function?

2. How to resolve the CC1030 warning issue?

I will try to discuss both.

1. Why is the [Pure] attribute missing? It's not missing, metadata does not seem to be showing this.

This may not be marked as Pure in previous version of .NET FX as, they were saying:

Yes, we need to make our checker sensitive to the disable pragma...

Sigh.

We currently don't have that implemented, but I've added it to our work list.

Refer to the 5 year old discussion here.

But this has been marked as Pure in latest FX (4.6.1), Refer to .NET Framework 4.6.1, the new string class code.

[Pure]
public static bool IsNullOrWhiteSpace(String value) {
    if (value == null) return true;

    for(int i = 0; i < value.Length; i++) {
        if(!Char.IsWhiteSpace(value[i])) return false;
    }

    return true;
}

Then Why CC1036?

This warning "CC1036" is from CodeContracts, developers have open this issue yesterday only (refer here).

Now why metadata is not spitting up Pure attributes, this is a different question, like for Equals method, Pure is added but only SecuritySafeCritical is displayed in metadata code.

[SecuritySafeCritical]
public static bool Equals(String a, String b, StringComparison comparisonType);

The same problem applies to Invariant(). Given the following code, the same warnings are displayed:

private string testString = "test";

[ContractInvariantMethod]
private void TestInvariant()
{
     Contract.Invariant(!string.IsNullOrWhiteSpace(testString));
}

How to resolve?

As others are also suggesting, create another method, mark it as Pure and call this in your contract condition.

BartoszKP
  • 34,786
  • 15
  • 102
  • 130
Anil
  • 3,722
  • 2
  • 24
  • 49
  • What exact version of .NET 4.6.1 are you using ? For example, for .NET 4.5.1 there are multiple versions with versions ranging from 4.0.30319.18401 to 4.0.30319.34000, see https://msdn.microsoft.com/en-us/library/hh925568(v=vs.110).aspx – Alex Jan 21 '16 at 19:05
7

Going through a pure delegate will make the warning go away. Predicate<T> is already marked pure, so you can just use that to work around the bug:

// Workaround for https://github.com/Microsoft/CodeContracts/issues/339
public Predicate<string> IsNullOrWhiteSpace = string.IsNullOrWhiteSpace;

public void F(string x)
{
    Contract.Requires(!IsNullOrWhiteSpace(x));

    throw new NotImplementedException();
}
Mark Waterman
  • 961
  • 7
  • 16
  • I ended up creating a helper `public static class CodeContractsBugFix` containing the predicate you have proposed. Thanks! – BartoszKP Jan 23 '16 at 19:41
  • Anyone have any idea why this doesn't work anymore (as of 1.9.10714.2)? Either the version shown in this answer, or @BartoszKP helper class? I did both of these and the analyzer is still complaining. – fourpastmidnight Feb 13 '16 at 00:38
  • This no longer worked for me. However, I did note in my answer a way to tell Code Contracts which reference .NET Assemblies to use for v4.6+ of the .NET Framework that resolves the OP's issue. – fourpastmidnight Feb 20 '16 at 17:55
5

Although kind of ugly, you can wrap the function string.IsNullOrWhiteSpace with an extension method and mark this new function as Pure.

BartoszKP
  • 34,786
  • 15
  • 102
  • 130
Alex
  • 21,273
  • 10
  • 61
  • 73
3

I just encountered the exact same problem. I'm using VS2015, so it does not seem to be related to VS version. I also tested the exact same code on .NET 4.0, 4.5.1 and 4.6, without getting the warning.

Like others have commented before me, the IsNullOrWhiteSpace is marked as [Pure] in .NET 4.6.1, and additionally should by default be considered pure by Code Contracts because it is in the System.String namespace. This makes it look like a bug, so I have submitted an issue to Code Contracts about this, so with some luck we will see an official answer soon.

While we wait for an answer, it is possible (like @Jaco suggests) to wrap it in an extension method and mark it as Pure yourself. Optionally, you can suppress the warning for that particular method like this:

[SuppressMessage("Microsoft.Contracts", "CC1036", Justification = "string.IsNullOrWhiteSpace is Pure")]

... but note that this will also suppress this warning from other Contract definitions in the same method.

BartoszKP
  • 34,786
  • 15
  • 102
  • 130
Desarc
  • 121
  • 5
  • Looks like this is just an issue with [Code Contracts using the wrong reference assemblies for .NET 4.6.1](https://github.com/Microsoft/CodeContracts/issues/339). – Desarc Feb 03 '16 at 08:56
3

Actually, this is a problem with the way .NET 4.6+ is compiled. See this GitHub pull request.

I was able to work around this by modifying the following file(s):

  • For Visual Studio 2013:
    • C:\Program Files (x86)\Microsoft\Contracts\MsBuild\v12.0\Microsoft.CodeContracts.Targets
  • For Visual Studio 2015:
    • C:\Progarm Files (x86)\Microsoft\Contracts\MsBuild\v14.0\Microsoft.CodeContracts.Targets

In both files, ensure the <Otherwise> child element of the first <Choose> element has the following content shown below:

...
<Choose>
  <When Condition="'$(TargetFrameworkIdentifier)' == 'Silverlight'">
     ...
  </When>
  <Otherwise>
    <Choose>
      <When Condition="'$(TargetFrameworkVersion)' == 'v4.0">
        <PropertyGroup>
          <CodeContractsReferenceAssemblyLibPath>$(CodeContractsInstallDir)Contracts\.NETFramework\v4.0</CodeContractsReferenceAssemblyLibPath>
        </PropertyGroup>
      </When>
      <When Condition="'$(TargetFrameworkVersion)' == 'v4.5'">
        <PropertyGroup>
          <CodeContractsReferenceAssemblyLibPath>$(CodeContractsInstallDir)Contracts\.NETFramework\v4.5</CodeContractsReferenceAssemblyLibPath>
        </PropertyGroup>
      </When>
      <When Condition="'$(TargetFrameworkVersion)' == 'v4.5.1'">
        <PropertyGroup>
          <CodeContractsReferenceAssemblyLibPath>$(CodeContractsInstallDir)Contracts\.NETFramework\v4.5</CodeContractsReferenceAssemblyLibPath>
        </PropertyGroup>
      </When>
      <When Condition="'$(TargetFrameworkVersion)' == 'v4.5.2'">
        <PropertyGroup>
          <CodeContractsReferenceAssemblyLibPath>$(CodeContractsInstallDir)Contracts\.NETFramework\v4.5</CodeContractsReferenceAssemblyLibPath>
        </PropertyGroup>
      </When>
      <When Condition="'$(TargetFrameworkVersion)' == 'v4.6'">
        <PropertyGroup>
          <CodeContractsReferenceAssemblyLibPath>$(CodeContractsInstallDir)Contracts\.NETFramework\v4.5</CodeContractsReferenceAssemblyLibPath>
        </PropertyGroup>
      </When>
      <When Condition="'$(TargetFrameworkVersion)' == 'v4.6.1'">
        <PropertyGroup>
          <CodeContractsReferenceAssemblyLibPath>$(CodeContractsInstallDir)Contracts\.NETFramework\v4.5</CodeContractsReferenceAssemblyLibPath>
        </PropertyGroup>
      </When>
      <Otherwise>
        <PropertyGroup>
          <CodeContractsReferenceAssemblyLibPath>$(CodeContractsInstallDir)Contracts\.NETFramework\v3.5</CodeContractsReferenceAssemblyLibPath>
        </PropertyGroup>
      </Otherwise>
    </Choose>
  </Otherwise>
</Chose>
...

After making these changes to these files (per the GitHub pull request referenced above), I no longer received Code Contracts static analysis warnings for the use of String.IsNullOrWhiteSpace.

It should be noted that the referenced pull request has been merged into the main code for Code Contracts up on GitHub; they just haven't made a new release containing these changes yet.

Also, for those concerned about changing "system files", don't be. When the next version of Code Contracts is released, it will install updated versions of these files--and hopefully the changes will be included, and all will be right with the world. (Unless, of course, the changes aren't included--in which case, you'll be coming back here to reference this post to make those changes again ;) lol.)

fourpastmidnight
  • 4,032
  • 1
  • 35
  • 48