How to deal with Code Contracts warning CC1036 when using string.IsNullOrWhiteSpace?
Asked Answered
P

5

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?

Pteranodon answered 5/1, 2016 at 13:1 Comment(10)
IsNullOrWhiteSpace is [Pure] already. Perhaps it is not in your framework version, which is ... ?Thirzia
@Thirzia 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?Pteranodon
Looks like a bug.Thirzia
@Thirzia This bug refers to 4.5, I'm using 4.6.1.Pteranodon
@Thirzia Strange - my sources (from metadata) look different.Pteranodon
The method is marked as Pure for .NET 4.6.1 in the .NET Framework source code: referencesource.microsoft.com/#mscorlib/system/…Zigzagger
@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.Pteranodon
@Jaco I'll be happy to improve my question if you think that something is wrong or some important information is missing.Pteranodon
[Pure] shouldn't even be necessary on a System.String method. According to the documentation (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."Ciaphus
@MarkWaterman Thanks! Very valuable information!Pteranodon
C
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();
}
Ciaphus answered 21/1, 2016 at 18:36 Comment(3)
I ended up creating a helper public static class CodeContractsBugFix containing the predicate you have proposed. Thanks!Pteranodon
Anyone have any idea why this doesn't work anymore (as of 1.9.10714.2)? Either the version shown in this answer, or @Pteranodon helper class? I did both of these and the analyzer is still complaining.Gadoid
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.Gadoid
M
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.

Myriammyriameter answered 21/1, 2016 at 12:23 Comment(1)
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 msdn.microsoft.com/en-us/library/hh925568(v=vs.110).aspxZigzagger
C
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();
}
Ciaphus answered 21/1, 2016 at 18:36 Comment(3)
I ended up creating a helper public static class CodeContractsBugFix containing the predicate you have proposed. Thanks!Pteranodon
Anyone have any idea why this doesn't work anymore (as of 1.9.10714.2)? Either the version shown in this answer, or @Pteranodon helper class? I did both of these and the analyzer is still complaining.Gadoid
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.Gadoid
Z
5

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

Zigzagger answered 5/1, 2016 at 13:8 Comment(0)
W
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.

Wormhole answered 21/1, 2016 at 11:47 Comment(1)
Looks like this is just an issue with Code Contracts using the wrong reference assemblies for .NET 4.6.1.Wormhole
G
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.)

Gadoid answered 20/2, 2016 at 5:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.