Broken serializable types

Rule ND1504 API Breaking Changes

Runs only in diff mode (baseline defined). Warns about types tagged SerializableAttribute whose serializable instance fields were added or removed (fields tagged NonSerializedAttribute are ignored).

From http://msdn.microsoft.com/library/system.serializableattribute.aspx : all fields of a SerializableAttribute type are serialized by default, unless the type implements ISerializable or fields are marked NonSerializedAttribute.

How to fix

Keep the serialization process of serializable types stable, now and in the future; else deal with Version Tolerant Serialization: https://msdn.microsoft.com/en-us/library/ms229752(v=vs.110).aspx

Severity is High: an API Breaking change can provoke significant friction with API consumers.

CQLinq source

This rule is written in CQLinq, C# LINQ over the NDepend code model. It ships with every new NDepend project: you can read it, adapt it, or use it as a starting point for your own custom rules.

// <Name>Broken serializable types</Name>
// <Id>ND1504:BrokenSerializableTypes</Id>

warnif count > 0

from t in Application.Types where

  // Collect types tagged with SerializableAttribute
  t.HasAttribute("System.SerializableAttribute".AllowNoMatch())  && 
 !t.IsDelegate &&
  t.IsPresentInBothBuilds()

  // Find newer and older versions of NonSerializedAttribute
  let newNonSerializedAttribute = ThirdParty.Types.WithFullName("System.NonSerializedAttribute").SingleOrDefault()
  let oldNonSerializedAttribute = newNonSerializedAttribute == null ? null : newNonSerializedAttribute.OlderVersion()

  // Find added or removed fields not marked with NonSerializedAttribute
  let addedInstanceField = from f in t.InstanceFields where 
                             f.WasAdded() && 
                             (newNonSerializedAttribute == null || !f.HasAttribute(newNonSerializedAttribute))
                           select f
  let removedInstanceField = from f in t.OlderVersion().InstanceFields where 
                             f.WasRemoved() && 
                             (oldNonSerializedAttribute == null || !f.HasAttribute(oldNonSerializedAttribute))
                           select f
  where addedInstanceField.Count() > 0 || removedInstanceField.Count() > 0

select new { 
   t, 
   addedInstanceField, 
   removedInstanceField,
   Debt = 20.ToMinutes().ToDebt(),
   Severity = Severity.Critical
}


//<Expl>
// The field set of the serializable *{0}* changed since the baseline.
// This can break client code using it.
// New field{1?:s}: *{1}* Removed field{2?:s}: *{2}*
//</Expl>

//<Desc>
// Runs only in *diff mode* (baseline defined). Warns about types tagged
// *SerializableAttribute* whose serializable instance fields were added or
// removed (fields tagged *NonSerializedAttribute* are ignored).
//
// From http://msdn.microsoft.com/library/system.serializableattribute.aspx :
// all fields of a *SerializableAttribute* type are serialized by default,
// unless the type implements *ISerializable* or fields are marked
// *NonSerializedAttribute*.
//</Desc>

//<HowToFix>
// Keep the serialization process of serializable types stable, now and in
// the future; else deal with **Version Tolerant Serialization**:
// https://msdn.microsoft.com/en-us/library/ms229752(v=vs.110).aspx
//
// Severity is **High**: an API Breaking change can provoke significant
// friction with API consumers.
//</HowToFix>

API Breaking Changes: all 9 items