<|start_filename|>tests/D2L.CodeStyle.Analyzers.Test/DiagnosticsTests.cs<|end_filename|>
﻿using System.Linq;
using System.Reflection;
using Microsoft.CodeAnalysis;
using NUnit.Framework;

namespace D2L.CodeStyle.Analyzers {
	[TestFixture]
	internal sealed class DiagnosticsTests {

		[Test]
		public void DiagnosticIDsMustBeUnique() {
			var diagnostics = typeof( Diagnostics ).GetFields(
				BindingFlags.Public | BindingFlags.Static
			).Select( field => field.GetValue( null ) )
			.Cast<DiagnosticDescriptor>();

			var ids = diagnostics.Select( d => d.Id ).ToArray();

			CollectionAssert.AllItemsAreUnique( ids, "Found duplicate diagnostic ID." );
		}

	}
}


<|start_filename|>tests/D2L.CodeStyle.Analyzers.Test/ApiUsage/RpcAnalyzerTests.cs<|end_filename|>
﻿using D2L.CodeStyle.Analyzers.Test.Verifiers;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using NUnit.Framework;

namespace D2L.CodeStyle.Analyzers.ApiUsage {
	[TestFixture]
	internal sealed class RpcDependencyAnalyzerTests : DiagnosticVerifier {
		private const string PREAMBLE = @"
namespace D2L.Web {
	interface IRpcContext {}
	interface IRpcPostContext {}
	class RpcAttribute : System.Attribute {}
}

namespace D2L.Web.RequestContext {
	interface IRpcPostContextBase {}
}

namespace D2L.LP.Extensibility.Activation.Domain {
	class DependencyAttribute : System.Attribute {}
}";

		[Test]
		public void NormalMethod_NoDiag() {
			const string test = PREAMBLE + @"
namespace Test {
	class Test {
		public void Test( int x ) {}
	}
}";
			AssertNoDiagnostic( test );
		}

		[Test]
		public void NormalMethodWithUnknownAttribute_NoDiag() {
			const string test = PREAMBLE + @"
namespace Test {
	class Test {
		[SomethingWeird]
		public void Test( int x ) {}
	}
}";
			AssertNoDiagnostic( test );
		}

		[Test]
		public void MethodWithUnrelatedButAnnoyinglyNamedRpcAttribute_NoDiag() {
			const string test = PREAMBLE + @"
namespace Test {
	class RpcAttribute : System.Attribute {} // shadow the parent one
	class Test {
		[Rpc]
		public void Test() {}
	}
}";
			AssertNoDiagnostic( test );
		}

		[Test]
		public void MethodWithRpcAttributeButNoArguments_RpcContextDiag() {
			const string test = PREAMBLE + @"
namespace Test {
	using D2L.Web;

	class Test {
		[Rpc]
		public void Test() {}
	}
}";
			AssertSingleDiagnostic( Diagnostics.RpcContextFirstArgument, test, 20, 19 );
		}

		[Test]
		public void MethodWithRpcAttributeAndIntFirstArgument_RpcContextDiag() {
			const string test = PREAMBLE + @"
namespace Test {
	using D2L.Web;

	class Test {
		[Rpc]
		public void Test( int x ) {}
	}
}";
			AssertSingleDiagnostic( Diagnostics.RpcContextFirstArgument, test, 20, 21 );
		}

		[Test]
		public void RpcWithFirstArgumentOfUnknownType_Diag() {
			// This test ensures that we don't throw a NullReferenceException due to the first argument being of unknown type
			const string test = PREAMBLE + @"
namespace Test {
	using D2L.Web;
	using D2L.LP.Extensibility.Activation.Domain;
	class Test {
		[Rpc]
		public void Test( SomethingWeird x ) {}
	}
}";
			AssertSingleDiagnostic( Diagnostics.RpcContextFirstArgument, test, 20, 21 );
		}

		[Test]
		public void MethodWithRpcAttributeAndIRpcContextFirstArg_NoDiag() {
			const string test = PREAMBLE + @"
namespace Test {
	using D2L.Web;

	class Test {
		[Rpc]
		public void Test( IRpcContext x ) {}
	}
}";
			AssertNoDiagnostic( test );
		}

		[Test]
		public void MethodWithRpcAttributeAndIRpcPostContextFirstArg_NoDiag() {
			const string test = PREAMBLE + @"
namespace Test {
	using D2L.Web;

	class Test {
		[Rpc]
		public void Test( IRpcPostContext x ) {}
	}
}";
			AssertNoDiagnostic( test );
		}

		[Test]
		public void MethodWithRpcAttributeAndIRpcPostContextBaseFirstArg_NoDiag() {
			const string test = PREAMBLE + @"
namespace Test {
	using D2L.Web;
	using D2L.Web.RequestContext;

	class Test {
		[Rpc]
		public void Test( IRpcPostContextBase x ) {}
	}
}";
			AssertNoDiagnostic( test );
		}

		[Test]
		public void RpcWithoutDependencyArgs_NoDiag() {
			const string test = PREAMBLE + @"
namespace Test {
	using D2L.Web;
	using D2L.LP.Extensibility.Activation.Domain;
	class Test {
		[Rpc]
		public void Test( IRpcContext x, int x, int y ) {}
	}
}";
			AssertNoDiagnostic( test );
		}

		[Test]
		public void RpcWithParameterThatHasBadAttributeType_NoDiag() {
			const string test = PREAMBLE + @"
namespace Test {
	using D2L.Web;
	using D2L.LP.Extensibility.Activation.Domain;
	class Test {
		[Rpc]
		public void Test( IRpcContext x, [SomethingWeird] int x, int y ) {}
	}
}";
			AssertNoDiagnostic( test );
		}

		[Test]
		public void RpcWithSingleDependencyNoParams_NoDiag() {
			const string test = PREAMBLE + @"
namespace Test {
	using D2L.Web;
	using D2L.LP.Extensibility.Activation.Domain;
	class Test {
		[Rpc]
		public void Test( IRpcContext x, [Dependency] int x ) {}
	}
}";
			AssertNoDiagnostic( test );
		}

		[Test]
		public void RpcWithTwoDependencyNoParams_NoDiag() {
			const string test = PREAMBLE + @"
namespace Test {
	using D2L.Web;
	using D2L.LP.Extensibility.Activation.Domain;
	class Test {
		[Rpc]
		public void Test( IRpcContext x, [Dependency] int x, [Dependency] int y ) {}
	}
}";
			AssertNoDiagnostic( test );
		}

		[Test]
		public void RpcWithTwoDependencySingleParams_NoDiag() {
			const string test = PREAMBLE + @"
namespace Test {
	using D2L.Web;
	using D2L.LP.Extensibility.Activation.Domain;
	class Test {
		[Rpc]
		public void Test( IRpcContext x, [Dependency] int x, [Dependency] int y, int z ) {}
	}
}";
			AssertNoDiagnostic( test );
		}

		[Test]
		public void RpcWithDependencyAtEnd_SortDiag() {
			const string test = PREAMBLE + @"
namespace Test {
	using D2L.Web;
	using D2L.LP.Extensibility.Activation.Domain;
	class Test {
		[Rpc]
		public void Test( IRpcContext x, int y, int z, [Dependency] int a ) {}
	}
}";
			AssertSingleDiagnostic( Diagnostics.RpcArgumentSortOrder, test, 20, 50 );
		}

		[Test]
		public void RpcWithTwoDependencySingleParamInTheMiddle_SortDiag() {
			const string test = PREAMBLE + @"
namespace Test {
	using D2L.Web;
	using D2L.LP.Extensibility.Activation.Domain;
	class Test {
		[Rpc]
		public void Test( IRpcContext x, [Dependency] int y, int z, [Dependency] int w ) {}
	}
}";
			AssertSingleDiagnostic( Diagnostics.RpcArgumentSortOrder, test, 20, 63 );
		}

		private void AssertNoDiagnostic( string file ) {
			VerifyCSharpDiagnostic( file );
		}

		private void AssertSingleDiagnostic( DiagnosticDescriptor diag, string file, int line, int column ) {
			DiagnosticResult result = new DiagnosticResult {
				Id = diag.Id,
				Message = diag.MessageFormat.ToString(),
				Severity =  DiagnosticSeverity.Error,
				Locations = new [] {
					new DiagnosticResultLocation( "Test0.cs", line, column )
				}
			};

			VerifyCSharpDiagnostic( file, result );
		}

		protected override DiagnosticAnalyzer GetCSharpDiagnosticAnalyzer() {
			return new RpcAnalyzer();
		}
	}
}


<|start_filename|>tests/D2L.CodeStyle.Analyzers.Test/Specs/ImmutabilityAnalyzer.cs<|end_filename|>
﻿// analyzer: D2L.CodeStyle.Analyzers.Immutability.ImmutabilityAnalyzer

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Dynamic;
using System.Linq;
using D2L.CodeStyle.Annotations;
using static D2L.CodeStyle.Annotations.Objects;

#region Relevant Types

#if NETFRAMEWORK
namespace System.Runtime.CompilerServices {
	// Polyfill for .NET framework
    internal static class IsExternalInit {}
}
#endif

#endregion


namespace SpecTests {

	public sealed class Types {

		public enum SomeEnum {
			Foo
		}

		public interface RegularInterface { }

		[Immutable]
		public interface SomeImmutableInterface { }

		#region Constructor immutability
		[Immutable]
		public sealed class Good : RegularInterface { }

		public class Bad : RegularInterface { }

		[Immutable]
		public sealed class SomeClassWithConstructor1 {
			public readonly RegularInterface m_interface = new Good();

			public SomeClassWithConstructor1() {
				m_interface = /* NonImmutableTypeHeldByImmutable(class, SpecTests.Types.Bad,  (or [ImmutableBaseClass])) */ new Bad() /**/;
			}
		}

		[Immutable]
		public sealed class SomeClassWithConstructor2 {
			public readonly RegularInterface m_interface = /* NonImmutableTypeHeldByImmutable(class, SpecTests.Types.Bad,  (or [ImmutableBaseClass])) */ new Bad() /**/;

			public SomeClassWithConstructor2() {
				m_interface = /* NonImmutableTypeHeldByImmutable(class, SpecTests.Types.Bad,  (or [ImmutableBaseClass])) */ new Bad() /**/;
			}
		}

		[Immutable]
		public sealed class SomeClassWithConstructor3 {
			public readonly RegularInterface m_interface = new Good();

			public SomeClassWithConstructor3() {
				m_interface = new Good();
			}
		}

		[Immutable]
		public sealed class SomeClassWithConstructor4 {
			public readonly RegularInterface m_interface = new Good();

			public SomeClassWithConstructor4() {
				if( true == false ) {
					m_interface = /* NonImmutableTypeHeldByImmutable(class, SpecTests.Types.Bad,  (or [ImmutableBaseClass])) */ new Bad() /**/;
				}
			}
		}

		[Immutable]
		public sealed class SomeClassWithConstructor5 {
			public readonly int m_int = 5;
			public readonly object m_alwaysOk;
			public readonly object m_sometimesBad;

			public SomeClassWithConstructor5() {
				m_int = 29;

				m_alwaysOk = null;
				m_alwaysOk = new object();

				m_sometimesBad = null;
				m_sometimesBad = new object();
				m_sometimesBad = /* ArraysAreMutable(Int32) */ new[] { 3 } /**/;
				m_sometimesBad = new Good();
			}
		}

		[Immutable]
		public sealed class SomeClassWithConstructor6 {
			public int /* MemberIsNotReadOnly(Field, m_int, SomeClassWithConstructor6) */ m_int /**/ = 5;

			public SomeClassWithConstructor6() {
				m_int = 29;
			}
		}

		[ConditionallyImmutable]
		internal sealed class SomeClassWithConstructor7<[ConditionallyImmutable.OnlyIf] T, U> where U : T {
			public T Tee { get; }

			public SomeClassWithConstructor7( U you ) {
				Tee = you;
			}
		}

		[Immutable]
		public sealed class SomeClassWithConstructor8 {
			public readonly RegularInterface m_interface = new Good();

			public SomeClassWithConstructor8() {
				(m_interface) = /* NonImmutableTypeHeldByImmutable(class, SpecTests.Types.Bad,  (or [ImmutableBaseClass])) */ new Bad() /**/;
			}
		}

		[Immutable]
		public sealed class SomeClassWithConstructor9 {
			public readonly RegularInterface m_interface = new Good();
			public readonly RegularInterface m_interface2 = new Good();

			public SomeClassWithConstructor9() {
				/* UnknownImmutabilityAssignmentKind(Deconstructed assignment) */ (m_interface, m_interface2) /**/ = (new Good(), new Good());
			}
		}

		[Immutable]
		public sealed class SomeClassWithConstructor10 {
			public readonly /* NonImmutableTypeHeldByImmutable(interface, SpecTests.Types.RegularInterface, ) */ RegularInterface /**/ m_interface;
			public readonly /* NonImmutableTypeHeldByImmutable(interface, SpecTests.Types.RegularInterface, ) */ RegularInterface /**/ m_interface2;

			public SomeClassWithConstructor10() {
				/* UnknownImmutabilityAssignmentKind(Deconstructed assignment) */ (m_interface, m_interface2) /**/ = (new Good(), new Good());
			}
		}
		#endregion


		public class RegularClass {
			private const int m_const = 0;

			private int m_field1 = 0;

			private readonly int m_field2 = 0;

		}
		public class RegularExtension : RegularClass { }
		public sealed class RegularSealedExtension : RegularClass { }

		public class SomeMutableClassWithImmutableTypeParameter<[Immutable] T> { }

		[ImmutableBaseClass]
		public class SomeImmutableBaseClass { }
		public static SomeImmutableBaseClass FuncReturningSomeImmutableBaseClass() => null;
		public sealed class MutableExtensionOfSomeImmutableBaseClass : SomeImmutableBaseClass { }

		[ImmutableBaseClass]
		public class SomeImmutableGenericBaseClassRestrictingT<[Immutable] T> { }

		[Immutable]
		public class SomeImmutableClass { }
		public static SomeImmutableClass FuncReturningSomeImmutableClass() => null;

		[Immutable]
		public class SomeImmutableGenericClassRestrictingT<[Immutable] T> { }

		[Immutable]
		public sealed class ClassMarkedImmutableImplementingRegularInterface : RegularInterface { }

		[ImmutableBaseClass]
		public class ClassMarkedImmutableBaseClassImplementingRegularInterface : RegularInterface { }

		public readonly struct SomeStruct { }

		[Immutable]
		public readonly struct SomeImmutableStruct { }

		public interface SomeGenericInterface<T, U> { }

		[Immutable]
		public interface SomeImmutableGenericInterface<T, U> { }

		[ConditionallyImmutable]
		public interface SomeImmutableGenericInterfaceGivenT<[ConditionallyImmutable.OnlyIf] T, U> { }

		[ConditionallyImmutable]
		public interface SomeImmutableGenericInterfaceGivenU<T, [ConditionallyImmutable.OnlyIf] U> { }

		[ConditionallyImmutable]
		public interface SomeImmutableGenericInterfaceGivenTU<[ConditionallyImmutable.OnlyIf] T, [ConditionallyImmutable.OnlyIf] U> { }

		[Immutable]
		public interface SomeImmutableGenericInterfaceRestrictingT<[Immutable] T, U> { }

		[Immutable]
		public interface SomeImmutableGenericInterfaceRestrictingU<T, [Immutable] U> { }

		[Immutable]
		public interface SomeImmutableGenericInterfaceRestrictingTU<[Immutable] T, [Immutable] U> { }

		public static void SomeGenericMethod<T, U>() { }
		public static void SomeGenericMethodRestrictingT<[Immutable] T, U>() { }
		public static void SomeGenericMethodRestrictingU<T, [Immutable] U>() { }
		public static void SomeGenericMethodRestrictingTU<[Immutable] T, [Immutable] U>() { }
		public static void SomeGenericMethodRestrictingT<[Immutable] T>( T value ) { }


		/// <summary><see cref="SomeGenericMethodCref{T}()"/></summary>
		public static void SomeGenericMethodCref<[Immutable] T>( int x ) { }

}

	[Immutable]
	public sealed class AnalyzedClassMarkedImmutableExtendingMutableClass : /* NonImmutableTypeHeldByImmutable(class, SpecTests.Types.RegularClass,  (or [ImmutableBaseClass])) */ Types.RegularClass /**/ { }

	[ImmutableBaseClass]
	public sealed class AnalyzedClassMarkedImmutableBaseClassExtendingMutableClass : /* NonImmutableTypeHeldByImmutable(class, SpecTests.Types.RegularClass,  (or [ImmutableBaseClass])) */ Types.RegularClass /**/ { }

	[ImmutableBaseClass]
	public sealed class AnalyzedClassMarkedImmutableBaseClassExtendingImmutableBaseClass : Types.SomeImmutableBaseClass { }

	[Immutable]
	public sealed class AnalyzedClassMarkedImmutableExtendingImmutableBaseClass : Types.SomeImmutableBaseClass { }

	[Immutable]
	public sealed class AnalyzedClassMarkedImmutableExtendingImmutableClass : Types.SomeImmutableClass { }

	[Immutable]
	public sealed class AnalyzedClassWithNonImmutableTypeParameterImplementingImmutableInterfaceParameter<T, U> : Types.SomeImmutableGenericInterfaceRestrictingT</* TypeParameterIsNotKnownToBeImmutable(T) */ T /**/, U> { }

	public sealed class NonImmutableClassWithNonImmutableTypeParameterImplementingImmutableBaseClassParameter1<T> : Types.SomeImmutableGenericBaseClassRestrictingT</* TypeParameterIsNotKnownToBeImmutable(T) */ T /**/> { }
	public sealed class NonImmutableClassWithNonImmutableTypeParameterImplementingImmutableBaseClassParameter2<T> : Types.SomeMutableClassWithImmutableTypeParameter</* TypeParameterIsNotKnownToBeImmutable(T) */ T /**/> { }

	[Immutable]
	public sealed class AnalyzedClassWithNonImmutableTypeParameterImplementingImmutableBaseClassParameter<T> : Types.SomeImmutableGenericClassRestrictingT</* TypeParameterIsNotKnownToBeImmutable(T) */ T /**/> { }

	[ Immutable]
	[ConditionallyImmutable]
	public sealed class /* ConflictingImmutability(Immutable, ConditionallyImmutable, class) */ AnalyzedClassMarkedWithMultipleImmutabilities1 /**/ { }

	[Immutable]
	[ImmutableBaseClassAttribute]
	public sealed class /* ConflictingImmutability(Immutable, ImmutableBaseClassAttribute, class) */ AnalyzedClassMarkedWithMultipleImmutabilities2 /**/ { }

	[ConditionallyImmutable]
	[ImmutableBaseClassAttribute]
	public sealed class /* ConflictingImmutability(ConditionallyImmutable, ImmutableBaseClassAttribute, class) */ AnalyzedClassMarkedWithMultipleImmutabilities3 /**/ { }

	[Immutable]
	[ConditionallyImmutable]
	[ImmutableBaseClassAttribute]
	public sealed class /* ConflictingImmutability(Immutable, ConditionallyImmutable, class)
	                     | ConflictingImmutability(Immutable, ImmutableBaseClassAttribute, class)
	                     | ConflictingImmutability(ConditionallyImmutable, ImmutableBaseClassAttribute, class) */ AnalyzedClassMarkedWithMultipleImmutabilities4 /**/ { }

	[Immutable]
	[ConditionallyImmutable]
	public interface /* ConflictingImmutability(Immutable, ConditionallyImmutable, interface) */ AnalyzedInterfaceMarkedWithMultipleImmutabilities1 /**/ { }

	[Immutable]
	[ImmutableBaseClassAttribute]
	public interface /* ConflictingImmutability(Immutable, ImmutableBaseClassAttribute, interface) */ AnalyzedInterfaceMarkedWithMultipleImmutabilities2 /**/ { }

	[ConditionallyImmutable]
	[ImmutableBaseClassAttribute]
	public interface /* ConflictingImmutability(ConditionallyImmutable, ImmutableBaseClassAttribute, interface) */ AnalyzedInterfaceMarkedWithMultipleImmutabilities3 /**/ { }

	[Immutable]
	[ConditionallyImmutable]
	[ImmutableBaseClassAttribute]
	public interface /* ConflictingImmutability(Immutable, ConditionallyImmutable, interface)
					         | ConflictingImmutability(Immutable, ImmutableBaseClassAttribute, interface)
	                         | ConflictingImmutability(ConditionallyImmutable, ImmutableBaseClassAttribute, interface) */ AnalyzedInterfaceMarkedWithMultipleImmutabilities4 /**/ { }

	[Immutable]
	[ConditionallyImmutable]
	public struct /* ConflictingImmutability(Immutable, ConditionallyImmutable, struct) */ AnalyzedStructMarkedWithMultipleImmutabilities1 /**/ { }

	[Immutable]
	[ImmutableBaseClassAttribute]
	public struct /* ConflictingImmutability(Immutable, ImmutableBaseClassAttribute, struct) */ AnalyzedStructMarkedWithMultipleImmutabilities2 /**/ { }

	[ConditionallyImmutable]
	[ImmutableBaseClassAttribute]
	public struct /* ConflictingImmutability(ConditionallyImmutable, ImmutableBaseClassAttribute, struct) */ AnalyzedStructMarkedWithMultipleImmutabilities3 /**/ { }

	[Immutable]
	[ConditionallyImmutable]
	[ImmutableBaseClassAttribute]
	public struct /* ConflictingImmutability(Immutable, ConditionallyImmutable, struct)
	                 | ConflictingImmutability(Immutable, ImmutableBaseClassAttribute, struct)
	                 | ConflictingImmutability(ConditionallyImmutable, ImmutableBaseClassAttribute, struct) */ AnalyzedStructMarkedWithMultipleImmutabilities4 /**/	{ }

	[Immutable]
	public sealed class AnalyzedClassMarkedImmutable {



		class SomeEventArgs { }
		delegate void SomeEventHandler( object sender, SomeEventArgs e );
		/* EventMemberMutable() */ event SomeEventHandler SomeEvent; /**/


		object this[ int index ] {
			get { return null; }
			set { return; }
		}

		public int X { get; init; }

		// This isn't safe because init-only properties can be overwritten by
		// any caller (which we may not be able to analyze).
		public /* NonImmutableTypeHeldByImmutable(class, object, ) */ object /**/ Y { get; init; } = new object();

		static int /* MemberIsNotReadOnly(Field, m_field1, AnalyzedClassMarkedImmutable) */ m_field1 /**/ = 0;
		static readonly int m_field2 = 0;
		int /* MemberIsNotReadOnly(Field, m_field3, AnalyzedClassMarkedImmutable) */ m_field3 /**/ = 0;
		int /* MemberIsNotReadOnly(Field, m_field4, AnalyzedClassMarkedImmutable) */ m_field4 /**/;
		[Mutability.Unaudited( Because.ItHasntBeenLookedAt )]
		int m_field5 = 0;
		[Mutability.Audited( "<NAME>", "1970-01-01", "Rationale" )]
		int m_field6 = 0;
		readonly int m_field7 = 0;
		[/* UnnecessaryMutabilityAnnotation() */ Mutability.Unaudited( Because.ItHasntBeenLookedAt ) /**/]
		readonly int m_field8 = 0;
		[/* UnnecessaryMutabilityAnnotation() */ Mutability.Audited( "<NAME>", "1970-01-01", "Rationale" ) /**/]
		readonly int m_field9 = 0;
		readonly int m_field10;
		int Property1 { get; } = 0;
		int Property2 { get; }
		int Property3 { get { return 0; } }

		// target-typed new initializers
		readonly object m_lock = new();


		static Types.SomeEnum /* MemberIsNotReadOnly(Field, m_field11, AnalyzedClassMarkedImmutable) */ m_field11 /**/ = Types.SomeEnum.Foo;
		static readonly Types.SomeEnum m_field12 = Types.SomeEnum.Foo;
		Types.SomeEnum /* MemberIsNotReadOnly(Field, m_field13, AnalyzedClassMarkedImmutable) */ m_field13 /**/ = Types.SomeEnum.Foo;
		Types.SomeEnum /* MemberIsNotReadOnly(Field, m_field14, AnalyzedClassMarkedImmutable) */ m_field14 /**/;
		[Mutability.Unaudited( Because.ItHasntBeenLookedAt )]
		Types.SomeEnum m_field15 = Types.SomeEnum.Foo;
		[Mutability.Audited( "<NAME>", "1970-01-01", "Rationale" )]
		Types.SomeEnum m_field16 = Types.SomeEnum.Foo;
		readonly Types.SomeEnum m_field17 = Types.SomeEnum.Foo;
		readonly Types.SomeEnum m_field18;
		Types.SomeEnum Property4 { get; } = Types.SomeEnum.Foo;
		Types.SomeEnum Property5 { get; }
		Types.SomeEnum Property6 { get { return Types.SomeEnum.Foo; } }



		static int[] /* MemberIsNotReadOnly(Field, m_field19, AnalyzedClassMarkedImmutable) */ m_field19 /**/ = /* ArraysAreMutable(Int32) */ new[] { 0 } /**/;
		static readonly int[] m_field20 = /* ArraysAreMutable(Int32) */ new[] { 0 } /**/;
		int[] /* MemberIsNotReadOnly(Field, m_field21, AnalyzedClassMarkedImmutable) */ m_field21 /**/ = /* ArraysAreMutable(Int32) */ new[] { 0 } /**/;
		/* ArraysAreMutable(Int32) */ int[] /**/ /* MemberIsNotReadOnly(Field, m_field22, AnalyzedClassMarkedImmutable) */ m_field22 /**/;
		[Mutability.Unaudited( Because.ItHasntBeenLookedAt )]
		int[] m_field23 = new[] { 0 };
		[Mutability.Audited( "<NAME>", "1970-01-01", "Rationale" )]
		int[] m_field24 = new[] { 0 };
		readonly int[] m_field25 = /* ArraysAreMutable(Int32) */ new[] { 0 } /**/;
		readonly /* ArraysAreMutable(Int32) */ int[] /**/ m_field26;
		int[] Property7 { get; } = /* ArraysAreMutable(Int32) */ new[] { 0 } /**/;
		/* ArraysAreMutable(Int32) */ int[] /**/ Property8 { get; }
		int[] Property9 { get { return new[] { 0 }; } }



		static readonly /* UnexpectedTypeKind(PointerType) */ int* /**/ m_field27;
		/* UnexpectedTypeKind(PointerType) */ int* /**/ /* MemberIsNotReadOnly(Field, m_field28, AnalyzedClassMarkedImmutable) */ m_field28 /**/;
		[Mutability.Unaudited( Because.ItHasntBeenLookedAt )]
		int* m_field29;
		[Mutability.Audited( "<NAME>", "1970-01-01", "Rationale" )]
		int* m_field30;
		readonly /* UnexpectedTypeKind(PointerType) */ int* /**/ m_field31;
		/* UnexpectedTypeKind(PointerType) */ int* /**/ Property10 { get; }


		static readonly /* NonImmutableTypeHeldByImmutable(class, object, ) */ object /**/ m_field32;
		/* NonImmutableTypeHeldByImmutable(class, object, ) */ object /**/ /* MemberIsNotReadOnly(Field, m_field33, AnalyzedClassMarkedImmutable) */ m_field33 /**/;
		readonly /* NonImmutableTypeHeldByImmutable(class, object, ) */ object /**/ m_field34;
		/* NonImmutableTypeHeldByImmutable(class, object, ) */ object /**/ Property11 { get; }


		static object /* MemberIsNotReadOnly(Field, m_field35, AnalyzedClassMarkedImmutable) */ m_field35 /**/ = null;
		static readonly object m_field36 = null;
		object /* MemberIsNotReadOnly(Field, m_field37, AnalyzedClassMarkedImmutable) */ m_field37 /**/ = null;
		readonly object m_field38 = null;
		object Property12 { get; } = null;
		object Property13 { get { return null; } }



		static object /* MemberIsNotReadOnly(Field, m_field38_2, AnalyzedClassMarkedImmutable) */ m_field38_2 /**/ = new object();
		static readonly object m_field39 = new object();
		object /* MemberIsNotReadOnly(Field, m_field40, AnalyzedClassMarkedImmutable) */ m_field40 /**/ = new object();
		readonly object m_field41 = new object();
		object Property14 { get; } = new object();
		object Property15 { get { return new object(); } }



		static readonly /* DynamicObjectsAreMutable */ dynamic /**/ m_field42;
		/* DynamicObjectsAreMutable */ dynamic /**/ /* MemberIsNotReadOnly(Field, m_field44, AnalyzedClassMarkedImmutable) */ m_field44 /**/;
		readonly /* DynamicObjectsAreMutable */ dynamic /**/ m_field45;
		[Mutability.Unaudited( Because.ItHasntBeenLookedAt )]
		readonly dynamic m_field46;
		[Mutability.Audited( "<NAME>", "1970-01-01", "Rationale" )]
		readonly dynamic m_field47;
		/* DynamicObjectsAreMutable */ dynamic /**/ Property16 { get; }
		[Mutability.Unaudited( Because.ItHasntBeenLookedAt )]
		dynamic Property17 { get; }
		[Mutability.Audited( "<NAME>", "1970-01-01", "Rationale" )]
		dynamic Property18 { get; }
		dynamic Property19 { get { return new ExpandoObject(); } }



		static Func<object> /* MemberIsNotReadOnly(Field, m_field48, AnalyzedClassMarkedImmutable) */ m_field48 /**/ = () => null;
		static readonly Func<object> m_field49 = () => null;
		Func<object> /* MemberIsNotReadOnly(Field, m_field50, AnalyzedClassMarkedImmutable) */ m_field50 /**/ = () => null;
		readonly Func<object> m_field51 = static () => null;
		Func<object> Property20 { get; } = static () => null;
		Func<object> Property21 { get { return () => null; } }



		static Func<object> /* MemberIsNotReadOnly(Field, m_field53, AnalyzedClassMarkedImmutable) */ m_field53 /**/ = () => { return null; };
		static readonly Func<object> m_field54 = () => { return null; };
		Func<object> /* MemberIsNotReadOnly(Field, m_field55, AnalyzedClassMarkedImmutable) */ m_field55 /**/ = () => { return null; };
		readonly Func<object> m_field56 = () => { return null; };
		Func<object> Property22 { get; } = static () => { return null; };
		Func<object> Property23 { get { return () => { return null; }; } }



		static (int, int) /* MemberIsNotReadOnly(Field, m_field57, AnalyzedClassMarkedImmutable) */ m_field57 /**/;
		static readonly (int, int) m_field58;
		(int, int) /* MemberIsNotReadOnly(Field, m_field59, AnalyzedClassMarkedImmutable) */ m_field59 /**/;
		readonly (int, int) m_field60;
		(int, int) Property24 { get; }
		(int, int) Property25 { get { return default; } }



		static /* NonImmutableTypeHeldByImmutable(class, object, ) */ (object, int) /**/ /* MemberIsNotReadOnly(Field, m_field61, AnalyzedClassMarkedImmutable) */ m_field61 /**/;
		static readonly /* NonImmutableTypeHeldByImmutable(class, object, ) */ (object, int) /**/ m_field62;
		/* NonImmutableTypeHeldByImmutable(class, object, ) */ (object, int) /**/ /* MemberIsNotReadOnly(Field, m_field63, AnalyzedClassMarkedImmutable) */ m_field63 /**/;
		readonly /* NonImmutableTypeHeldByImmutable(class, object, ) */ (object, int) /**/ m_field64;
		/* NonImmutableTypeHeldByImmutable(class, object, ) */ (object, int) /**/ Property26 { get; }
		(object, int) Property27 { get { return default; } }



		static /* NonImmutableTypeHeldByImmutable(class, object, ) */ (int, object) /**/ /* MemberIsNotReadOnly(Field, m_field65, AnalyzedClassMarkedImmutable) */ m_field65 /**/;
		static readonly /* NonImmutableTypeHeldByImmutable(class, object, ) */ (int, object) /**/ m_field66;
		/* NonImmutableTypeHeldByImmutable(class, object, ) */ (int, object) /**/ /* MemberIsNotReadOnly(Field, m_field68, AnalyzedClassMarkedImmutable) */ m_field68 /**/;
		readonly  /* NonImmutableTypeHeldByImmutable(class, object, ) */ (int, object) /**/ m_field69;
		/* NonImmutableTypeHeldByImmutable(class, object, ) */ (int, object) /**/ Property { get; }
		(int, object) Property28 { get { return default; } }



		static Types.RegularClass /* MemberIsNotReadOnly(Field, m_field70, AnalyzedClassMarkedImmutable) */ m_field70 /**/ = /* NonImmutableTypeHeldByImmutable(class, SpecTests.Types.RegularClass,  (or [ImmutableBaseClass])) */ new Types.RegularClass() /**/;
		static readonly Types.RegularClass m_field71 = /* NonImmutableTypeHeldByImmutable(class, SpecTests.Types.RegularClass,  (or [ImmutableBaseClass])) */ new Types.RegularClass() /**/;
		Types.RegularClass /* MemberIsNotReadOnly(Field, m_field72, AnalyzedClassMarkedImmutable) */ m_field72 /**/ = /* NonImmutableTypeHeldByImmutable(class, SpecTests.Types.RegularClass,  (or [ImmutableBaseClass])) */ new Types.RegularClass() /**/;
		readonly Types.RegularClass m_field73 = /* NonImmutableTypeHeldByImmutable(class, SpecTests.Types.RegularClass,  (or [ImmutableBaseClass])) */ new Types.RegularClass() /**/;
		[Mutability.Unaudited( Because.ItHasntBeenLookedAt )]
		readonly Types.RegularClass m_field74 = new Types.RegularClass ();
		[Mutability.Audited( "<NAME>", "1970-01-01", "Rationale" )]
		readonly Types.RegularClass m_field75 = new Types.RegularClass();
		Types.RegularClass Property29 { get; } = /* NonImmutableTypeHeldByImmutable(class, SpecTests.Types.RegularClass,  (or [ImmutableBaseClass])) */ new Types.RegularClass() /**/;
		[Mutability.Unaudited( Because.ItHasntBeenLookedAt )]
		Types.RegularClass Property30 { get; } = new Types.RegularClass ();
		[Mutability.Audited( "<NAME>", "1970-01-01", "Rationale" )]
		Types.RegularClass Property31 { get; } = new Types.RegularClass();
		Types.RegularClass Property32 { get { return new Types.RegularClass(); } }



		static Types.RegularClass /* MemberIsNotReadOnly(Field, m_field76, AnalyzedClassMarkedImmutable) */ m_field76 /**/ = /* NonImmutableTypeHeldByImmutable(class, SpecTests.Types.RegularExtension,  (or [ImmutableBaseClass])) */ new Types.RegularExtension() /**/;
		static readonly Types.RegularClass m_field77 = /* NonImmutableTypeHeldByImmutable(class, SpecTests.Types.RegularExtension,  (or [ImmutableBaseClass])) */ new Types.RegularExtension() /**/;
		Types.RegularClass /* MemberIsNotReadOnly(Field, m_field78, AnalyzedClassMarkedImmutable) */ m_field78 /**/ = /* NonImmutableTypeHeldByImmutable(class, SpecTests.Types.RegularExtension,  (or [ImmutableBaseClass])) */ new Types.RegularExtension() /**/;
		readonly Types.RegularClass m_field79 = /* NonImmutableTypeHeldByImmutable(class, SpecTests.Types.RegularExtension,  (or [ImmutableBaseClass])) */ new Types.RegularExtension() /**/;
		Types.RegularClass Property33 { get; } = /* NonImmutableTypeHeldByImmutable(class, SpecTests.Types.RegularExtension,  (or [ImmutableBaseClass])) */ new Types.RegularExtension() /**/;
		Types.RegularClass Property34 { get { return new Types.RegularExtension(); } }



		static Types.RegularClass /* MemberIsNotReadOnly(Field, m_field80, AnalyzedClassMarkedImmutable) */ m_field80 /**/ = /* NonImmutableTypeHeldByImmutable(class, SpecTests.Types.RegularSealedExtension, ) */ new Types.RegularSealedExtension() /**/;
		static readonly Types.RegularClass m_field81 = /* NonImmutableTypeHeldByImmutable(class, SpecTests.Types.RegularSealedExtension, ) */ new Types.RegularSealedExtension() /**/;
		Types.RegularClass /* MemberIsNotReadOnly(Field, m_field82, AnalyzedClassMarkedImmutable) */ m_field82 /**/ = /* NonImmutableTypeHeldByImmutable(class, SpecTests.Types.RegularSealedExtension, ) */ new Types.RegularSealedExtension() /**/;
		readonly Types.RegularClass m_field83 = /* NonImmutableTypeHeldByImmutable(class, SpecTests.Types.RegularSealedExtension, ) */ new Types.RegularSealedExtension() /**/;
		Types.RegularClass Property35 { get; } = /* NonImmutableTypeHeldByImmutable(class, SpecTests.Types.RegularSealedExtension, ) */ new Types.RegularSealedExtension() /**/;
		Types.RegularClass Property36 { get { return new Types.RegularSealedExtension(); } }



		Types.SomeImmutableBaseClass /* MemberIsNotReadOnly(Field, m_field84, AnalyzedClassMarkedImmutable) */ m_field84 /**/ = new Types.SomeImmutableBaseClass();
		readonly Types.SomeImmutableBaseClass m_field85 = new Types.SomeImmutableBaseClass();
		Types.SomeImmutableBaseClass Property37 { get; } = new Types.SomeImmutableBaseClass();



		readonly Types.SomeImmutableBaseClass m_field86 = /* NonImmutableTypeHeldByImmutable(class, SpecTests.Types.SomeImmutableBaseClass, ) */ Types.FuncReturningSomeImmutableBaseClass() /**/;
		Types.SomeImmutableBaseClass Property38 { get; } = /* NonImmutableTypeHeldByImmutable(class, SpecTests.Types.SomeImmutableBaseClass, ) */ Types.FuncReturningSomeImmutableBaseClass() /**/;



		Types.SomeImmutableBaseClass /* MemberIsNotReadOnly(Field, m_field87, AnalyzedClassMarkedImmutable) */ m_field87 /**/ = /* NonImmutableTypeHeldByImmutable(class, SpecTests.Types.MutableExtensionOfSomeImmutableBaseClass, ) */ new Types.MutableExtensionOfSomeImmutableBaseClass() /**/;
		readonly Types.SomeImmutableBaseClass m_field88 = /* NonImmutableTypeHeldByImmutable(class, SpecTests.Types.MutableExtensionOfSomeImmutableBaseClass, ) */ new Types.MutableExtensionOfSomeImmutableBaseClass() /**/;
		Types.SomeImmutableBaseClass Property39 { get; } = /* NonImmutableTypeHeldByImmutable(class, SpecTests.Types.MutableExtensionOfSomeImmutableBaseClass, ) */ new Types.MutableExtensionOfSomeImmutableBaseClass() /**/;



		Types.SomeImmutableClass /* MemberIsNotReadOnly(Field, m_field89, AnalyzedClassMarkedImmutable) */ m_field89 /**/ = new Types.SomeImmutableClass();
		readonly Types.SomeImmutableClass m_field90 = new Types.SomeImmutableClass();
		Types.SomeImmutableClass Property40 { get; } = new Types.SomeImmutableClass();



		readonly Types.SomeImmutableClass m_field91 = Types.FuncReturningSomeImmutableClass();
		Types.SomeImmutableClass Property41 { get; } = Types.FuncReturningSomeImmutableClass();



		static /* NonImmutableTypeHeldByImmutable(interface, SpecTests.Types.RegularInterface, ) */ Types.RegularInterface /**/ /* MemberIsNotReadOnly(Field, m_field92, AnalyzedClassMarkedImmutable) */ m_field92 /**/;
		static readonly /* NonImmutableTypeHeldByImmutable(interface, SpecTests.Types.RegularInterface, ) */ Types.RegularInterface /**/ m_field93;
		/* NonImmutableTypeHeldByImmutable(interface, SpecTests.Types.RegularInterface, ) */ Types.RegularInterface /**/ /* MemberIsNotReadOnly(Field, m_field94, AnalyzedClassMarkedImmutable) */ m_field94 /**/;
		readonly /* NonImmutableTypeHeldByImmutable(interface, SpecTests.Types.RegularInterface, ) */ Types.RegularInterface /**/ m_field95;
		[Mutability.Unaudited( Because.ItHasntBeenLookedAt )]
		readonly Types.RegularInterface m_field96;
		[Mutability.Audited( "<NAME>", "1970-01-01", "Rationale" )]
		readonly Types.RegularInterface m_field97;
		/* NonImmutableTypeHeldByImmutable(interface, SpecTests.Types.RegularInterface, ) */ Types.RegularInterface /**/ Property42 { get; }
		[Mutability.Unaudited( Because.ItHasntBeenLookedAt )]
		Types.RegularInterface Property43 { get; }
		[Mutability.Audited( "<NAME>", "1970-01-01", "Rationale" )]
		Types.RegularInterface Property44 { get; }



		static Types.RegularInterface /* MemberIsNotReadOnly(Field, m_field98, AnalyzedClassMarkedImmutable) */ m_field98 /**/ = new Types.ClassMarkedImmutableImplementingRegularInterface();
		static readonly Types.RegularInterface m_field99 = new Types.ClassMarkedImmutableImplementingRegularInterface();
		Types.RegularInterface /* MemberIsNotReadOnly(Field, m_field100, AnalyzedClassMarkedImmutable) */ m_field100 /**/ = new Types.ClassMarkedImmutableImplementingRegularInterface();
		readonly Types.RegularInterface m_field101 = new Types.ClassMarkedImmutableImplementingRegularInterface();
		Types.RegularInterface Property45 { get; } = new Types.ClassMarkedImmutableImplementingRegularInterface();



		static Types.RegularInterface /* MemberIsNotReadOnly(Field, m_field102, AnalyzedClassMarkedImmutable) */ m_field102 /**/ = new Types.ClassMarkedImmutableBaseClassImplementingRegularInterface();
		static readonly Types.RegularInterface m_field103 = new Types.ClassMarkedImmutableBaseClassImplementingRegularInterface();
		Types.RegularInterface /* MemberIsNotReadOnly(Field, m_field104, AnalyzedClassMarkedImmutable) */ m_field104 /**/ = new Types.ClassMarkedImmutableBaseClassImplementingRegularInterface();
		readonly Types.RegularInterface m_field105 = new Types.ClassMarkedImmutableBaseClassImplementingRegularInterface();
		Types.RegularInterface Property46 { get; } = new Types.ClassMarkedImmutableBaseClassImplementingRegularInterface();



		static /* NonImmutableTypeHeldByImmutable(structure, SpecTests.Types.SomeStruct, ) */ Types.SomeStruct /**/ /* MemberIsNotReadOnly(Field, m_field106, AnalyzedClassMarkedImmutable) */ m_field106 /**/;
		static readonly /* NonImmutableTypeHeldByImmutable(structure, SpecTests.Types.SomeStruct, ) */ Types.SomeStruct /**/ m_field107;
		/* NonImmutableTypeHeldByImmutable(structure, SpecTests.Types.SomeStruct, ) */ Types.SomeStruct /**/ /* MemberIsNotReadOnly(Field, m_field108, AnalyzedClassMarkedImmutable) */ m_field108 /**/;
		Types.SomeStruct /* MemberIsNotReadOnly(Field, m_field109, AnalyzedClassMarkedImmutable) */ m_field109 /**/ = /* NonImmutableTypeHeldByImmutable(structure, SpecTests.Types.SomeStruct, ) */ new Types.SomeStruct() /**/;
		readonly /* NonImmutableTypeHeldByImmutable(structure, SpecTests.Types.SomeStruct, ) */ Types.SomeStruct /**/ m_field110;
		readonly Types.SomeStruct  m_field111 = /* NonImmutableTypeHeldByImmutable(structure, SpecTests.Types.SomeStruct, ) */ new Types.SomeStruct() /**/;
		[Mutability.Unaudited( Because.ItHasntBeenLookedAt )]
		readonly Types.SomeStruct m_field112;
		[Mutability.Audited( "<NAME>", "1970-01-01", "Rationale" )]
		readonly Types.SomeStruct m_field113;
		/* NonImmutableTypeHeldByImmutable(structure, SpecTests.Types.SomeStruct, ) */ Types.SomeStruct /**/ Property47 { get; }
		Types.SomeStruct Property48 { get; } = /* NonImmutableTypeHeldByImmutable(structure, SpecTests.Types.SomeStruct, ) */ new Types.SomeStruct() /**/;
		[Mutability.Unaudited( Because.ItHasntBeenLookedAt )]
		Types.SomeStruct Property49 { get; }
		[Mutability.Audited( "<NAME>", "1970-01-01", "Rationale" )]
		Types.SomeStruct Property50 { get; }



		static Types.SomeImmutableStruct /* MemberIsNotReadOnly(Field, m_field114, AnalyzedClassMarkedImmutable) */ m_field114 /**/;
		static readonly Types.SomeImmutableStruct m_field115;
		Types.SomeImmutableStruct /* MemberIsNotReadOnly(Field, m_field116, AnalyzedClassMarkedImmutable) */ m_field116 /**/;
		readonly Types.SomeImmutableStruct m_field117;
		Types.SomeImmutableStruct Property51 { get; }



		static /* NonImmutableTypeHeldByImmutable(interface, SpecTests.Types.SomeGenericInterface<int\, int>, ) */ Types.SomeGenericInterface<int, int> /**/ /* MemberIsNotReadOnly(Field, m_field118, AnalyzedClassMarkedImmutable) */ m_field118 /**/;
		static readonly /* NonImmutableTypeHeldByImmutable(interface, SpecTests.Types.SomeGenericInterface<int\, int>, ) */ Types.SomeGenericInterface<int, int> /**/ m_field119;
		/* NonImmutableTypeHeldByImmutable(interface, SpecTests.Types.SomeGenericInterface<int\, int>, ) */ Types.SomeGenericInterface<int, int> /**/ /* MemberIsNotReadOnly(Field, m_field120, AnalyzedClassMarkedImmutable) */ m_field120 /**/;
		readonly /* NonImmutableTypeHeldByImmutable(interface, SpecTests.Types.SomeGenericInterface<int\, int>, ) */ Types.SomeGenericInterface<int, int> /**/ m_field121;
		/* NonImmutableTypeHeldByImmutable(interface, SpecTests.Types.SomeGenericInterface<int\, int>, ) */ Types.SomeGenericInterface<int, int> /**/ Property52 { get; }
		Types.SomeGenericInterface<int, int> Property53 { get { return default; } }



		static Types.SomeImmutableGenericInterfaceGivenT<int, object> /* MemberIsNotReadOnly(Field, m_field122, AnalyzedClassMarkedImmutable) */ m_field122 /**/;
		static readonly Types.SomeImmutableGenericInterfaceGivenT<int, object> m_field123;
		Types.SomeImmutableGenericInterfaceGivenT<int, object> /* MemberIsNotReadOnly(Field, m_field124, AnalyzedClassMarkedImmutable) */ m_field124 /**/;
		readonly Types.SomeImmutableGenericInterfaceGivenT<int, object> m_field125;
		Types.SomeImmutableGenericInterfaceGivenT<int, object> Property54 { get; }
		Types.SomeImmutableGenericInterfaceGivenT<int, object> Property55 { get { return default; } }



		static /* NonImmutableTypeHeldByImmutable(class, object, ) */ Types.SomeImmutableGenericInterfaceGivenT<object, int> /**/ /* MemberIsNotReadOnly(Field, m_field126, AnalyzedClassMarkedImmutable) */ m_field126 /**/;
		static readonly /* NonImmutableTypeHeldByImmutable(class, object, ) */ Types.SomeImmutableGenericInterfaceGivenT<object, int> /**/ m_field127;
		/* NonImmutableTypeHeldByImmutable(class, object, ) */ Types.SomeImmutableGenericInterfaceGivenT<object, int> /**/ /* MemberIsNotReadOnly(Field, m_field128, AnalyzedClassMarkedImmutable) */ m_field128 /**/;
		readonly /* NonImmutableTypeHeldByImmutable(class, object, ) */ Types.SomeImmutableGenericInterfaceGivenT<object, int> /**/ m_field129;
		/* NonImmutableTypeHeldByImmutable(class, object, ) */ Types.SomeImmutableGenericInterfaceGivenT<object, int> /**/ Property56 { get; }
		Types.SomeImmutableGenericInterfaceGivenT<object, int> Property57 { get { return default; } }


		static Types.SomeImmutableGenericInterfaceGivenU<object, int> /* MemberIsNotReadOnly(Field, m_field130, AnalyzedClassMarkedImmutable) */ m_field130 /**/;
		static readonly Types.SomeImmutableGenericInterfaceGivenU<object, int> m_field131;
		Types.SomeImmutableGenericInterfaceGivenU<object, int> /* MemberIsNotReadOnly(Field, m_field132, AnalyzedClassMarkedImmutable) */ m_field132 /**/;
		readonly Types.SomeImmutableGenericInterfaceGivenU<object, int> m_field133;
		Types.SomeImmutableGenericInterfaceGivenU<object, int> Property58 { get; }
		Types.SomeImmutableGenericInterfaceGivenU<object, int> Property59 { get { return default; } }



		static /* NonImmutableTypeHeldByImmutable(class, object, ) */ Types.SomeImmutableGenericInterfaceGivenU<int, object> /**/ /* MemberIsNotReadOnly(Field, m_field134, AnalyzedClassMarkedImmutable) */ m_field134 /**/;
		static readonly /* NonImmutableTypeHeldByImmutable(class, object, ) */ Types.SomeImmutableGenericInterfaceGivenU<int, object> /**/ m_field135;
		/* NonImmutableTypeHeldByImmutable(class, object, ) */ Types.SomeImmutableGenericInterfaceGivenU<int, object> /**/ /* MemberIsNotReadOnly(Field, m_field136, AnalyzedClassMarkedImmutable) */ m_field136 /**/;
		readonly /* NonImmutableTypeHeldByImmutable(class, object, ) */ Types.SomeImmutableGenericInterfaceGivenU<int, object> /**/ m_field137;
		/* NonImmutableTypeHeldByImmutable(class, object, ) */ Types.SomeImmutableGenericInterfaceGivenU<int, object> /**/ Property60 { get; }
		Types.SomeImmutableGenericInterfaceGivenU<int, object> Property61 { get { return default; } }



		static Types.SomeImmutableGenericInterfaceGivenTU<int, int> /* MemberIsNotReadOnly(Field, m_field138, AnalyzedClassMarkedImmutable) */ m_field138 /**/;
		static readonly Types.SomeImmutableGenericInterfaceGivenTU<int, int> m_field139;
		Types.SomeImmutableGenericInterfaceGivenTU<int, int> /* MemberIsNotReadOnly(Field, m_field140, AnalyzedClassMarkedImmutable) */ m_field140 /**/;
		readonly Types.SomeImmutableGenericInterfaceGivenTU<int, int> m_field141;
		Types.SomeImmutableGenericInterfaceGivenTU<int, int> Property62 { get; }
		Types.SomeImmutableGenericInterfaceGivenTU<int, int> Property63 { get { return default; } }



		static /* NonImmutableTypeHeldByImmutable(class, object, ) */ Types.SomeImmutableGenericInterfaceGivenTU<int, object> /**/ /* MemberIsNotReadOnly(Field, m_field142, AnalyzedClassMarkedImmutable) */ m_field142 /**/;
		static readonly /* NonImmutableTypeHeldByImmutable(class, object, ) */ Types.SomeImmutableGenericInterfaceGivenTU<int, object> /**/ m_field143;
		/* NonImmutableTypeHeldByImmutable(class, object, ) */ Types.SomeImmutableGenericInterfaceGivenTU<int, object> /**/ /* MemberIsNotReadOnly(Field, m_field144, AnalyzedClassMarkedImmutable) */ m_field144 /**/;
		readonly /* NonImmutableTypeHeldByImmutable(class, object, ) */ Types.SomeImmutableGenericInterfaceGivenTU<int, object> /**/ m_field145;
		/* NonImmutableTypeHeldByImmutable(class, object, ) */ Types.SomeImmutableGenericInterfaceGivenTU<int, object> /**/ Property64 { get; }
		Types.SomeImmutableGenericInterfaceGivenTU<int, object> Property65 { get { return default; } }


		static /* NonImmutableTypeHeldByImmutable(class, object, ) */ Types.SomeImmutableGenericInterfaceGivenTU<object, int> /**/ /* MemberIsNotReadOnly(Field, m_field146, AnalyzedClassMarkedImmutable) */ m_field146 /**/;
		static readonly /* NonImmutableTypeHeldByImmutable(class, object, ) */ Types.SomeImmutableGenericInterfaceGivenTU<object, int> /**/ m_field147;
		/* NonImmutableTypeHeldByImmutable(class, object, ) */ Types.SomeImmutableGenericInterfaceGivenTU<object, int> /**/ /* MemberIsNotReadOnly(Field, m_field148, AnalyzedClassMarkedImmutable) */ m_field148 /**/;
		readonly /* NonImmutableTypeHeldByImmutable(class, object, ) */ Types.SomeImmutableGenericInterfaceGivenTU<object, int> /**/ m_field149;
		/* NonImmutableTypeHeldByImmutable(class, object, ) */ Types.SomeImmutableGenericInterfaceGivenTU<object, int> /**/ Property66 { get; }
		Types.SomeImmutableGenericInterfaceGivenTU<object, int> Property67 { get { return default; } }



		static Types.SomeImmutableGenericInterfaceRestrictingT<int, object> /* MemberIsNotReadOnly(Field, m_field150, AnalyzedClassMarkedImmutable) */ m_field150 /**/;
		static readonly Types.SomeImmutableGenericInterfaceRestrictingT<int, object> m_field151;
		Types.SomeImmutableGenericInterfaceRestrictingT<int, object> /* MemberIsNotReadOnly(Field, m_field151_2, AnalyzedClassMarkedImmutable) */ m_field151_2 /**/;
		readonly Types.SomeImmutableGenericInterfaceRestrictingT<int, object> m_field152;
		Types.SomeImmutableGenericInterfaceRestrictingT<int, object> Property68 { get; }
		Types.SomeImmutableGenericInterfaceRestrictingT<int, object> Property69 { get { return default; } }



		static Types.SomeImmutableGenericInterfaceRestrictingT</* NonImmutableTypeHeldByImmutable(class, object, ) */ object /**/, int> /* MemberIsNotReadOnly(Field, m_field153, AnalyzedClassMarkedImmutable) */ m_field153 /**/;
		static readonly Types.SomeImmutableGenericInterfaceRestrictingT</* NonImmutableTypeHeldByImmutable(class, object, ) */ object /**/, int> m_field154;
		Types.SomeImmutableGenericInterfaceRestrictingT</* NonImmutableTypeHeldByImmutable(class, object, ) */ object /**/, int> /* MemberIsNotReadOnly(Field, m_field155, AnalyzedClassMarkedImmutable) */ m_field155 /**/;
		readonly Types.SomeImmutableGenericInterfaceRestrictingT</* NonImmutableTypeHeldByImmutable(class, object, ) */ object /**/, int> m_field156;
		Types.SomeImmutableGenericInterfaceRestrictingT</* NonImmutableTypeHeldByImmutable(class, object, ) */ object /**/, int> Property70 { get; }
		Types.SomeImmutableGenericInterfaceRestrictingT</* NonImmutableTypeHeldByImmutable(class, object, ) */ object /**/, int> Property71 { get { return default; } }


		static Types.SomeImmutableGenericInterfaceRestrictingU<object, int> /* MemberIsNotReadOnly(Field, m_field157, AnalyzedClassMarkedImmutable) */ m_field157 /**/;
		static readonly Types.SomeImmutableGenericInterfaceRestrictingU<object, int> m_field158;
		Types.SomeImmutableGenericInterfaceRestrictingU<object, int> /* MemberIsNotReadOnly(Field, m_field159, AnalyzedClassMarkedImmutable) */ m_field159 /**/;
		readonly Types.SomeImmutableGenericInterfaceRestrictingU<object, int> m_field160;
		Types.SomeImmutableGenericInterfaceRestrictingU<object, int> Property72 { get; }
		Types.SomeImmutableGenericInterfaceRestrictingU<object, int> Property73 { get { return default; } }



		static Types.SomeImmutableGenericInterfaceRestrictingU<int, /* NonImmutableTypeHeldByImmutable(class, object, ) */ object /**/> /* MemberIsNotReadOnly(Field, m_field161, AnalyzedClassMarkedImmutable) */ m_field161 /**/;
		static readonly Types.SomeImmutableGenericInterfaceRestrictingU<int, /* NonImmutableTypeHeldByImmutable(class, object, ) */ object /**/> m_field162;
		Types.SomeImmutableGenericInterfaceRestrictingU<int, /* NonImmutableTypeHeldByImmutable(class, object, ) */ object /**/> /* MemberIsNotReadOnly(Field, m_field163, AnalyzedClassMarkedImmutable) */ m_field163 /**/;
		readonly Types.SomeImmutableGenericInterfaceRestrictingU<int, /* NonImmutableTypeHeldByImmutable(class, object, ) */ object /**/> m_field164;
		Types.SomeImmutableGenericInterfaceRestrictingU<int, /* NonImmutableTypeHeldByImmutable(class, object, ) */ object /**/> Property74 { get; }
		Types.SomeImmutableGenericInterfaceRestrictingU<int, /* NonImmutableTypeHeldByImmutable(class, object, ) */ object /**/> Property75 { get { return default; } }



		static Types.SomeImmutableGenericInterfaceRestrictingTU<int, int> /* MemberIsNotReadOnly(Field, m_field165, AnalyzedClassMarkedImmutable) */ m_field165 /**/;
		static readonly Types.SomeImmutableGenericInterfaceRestrictingTU<int, int> m_field166;
		Types.SomeImmutableGenericInterfaceRestrictingTU<int, int> /* MemberIsNotReadOnly(Field, m_field167, AnalyzedClassMarkedImmutable) */ m_field167 /**/;
		readonly Types.SomeImmutableGenericInterfaceRestrictingTU<int, int> m_field168;
		Types.SomeImmutableGenericInterfaceRestrictingTU<int, int> Property76 { get; }
		Types.SomeImmutableGenericInterfaceRestrictingTU<int, int> Property77 { get { return default; } }



		static Types.SomeImmutableGenericInterfaceRestrictingTU<int, /* NonImmutableTypeHeldByImmutable(class, object, ) */ object /**/> /* MemberIsNotReadOnly(Field, m_field169, AnalyzedClassMarkedImmutable) */ m_field169 /**/;
		static readonly Types.SomeImmutableGenericInterfaceRestrictingTU<int, /* NonImmutableTypeHeldByImmutable(class, object, ) */ object /**/> m_field170;
		Types.SomeImmutableGenericInterfaceRestrictingTU<int, /* NonImmutableTypeHeldByImmutable(class, object, ) */ object /**/> /* MemberIsNotReadOnly(Field, m_field171, AnalyzedClassMarkedImmutable) */ m_field171 /**/;
		readonly Types.SomeImmutableGenericInterfaceRestrictingTU<int, /* NonImmutableTypeHeldByImmutable(class, object, ) */ object /**/> m_field172;
		Types.SomeImmutableGenericInterfaceRestrictingTU<int, /* NonImmutableTypeHeldByImmutable(class, object, ) */ object /**/> Property78 { get; }
		Types.SomeImmutableGenericInterfaceRestrictingTU<int, /* NonImmutableTypeHeldByImmutable(class, object, ) */ object /**/> Property79 { get { return default; } }


		static Types.SomeImmutableGenericInterfaceRestrictingTU</* NonImmutableTypeHeldByImmutable(class, object, ) */ object /**/, int> /* MemberIsNotReadOnly(Field, m_field173, AnalyzedClassMarkedImmutable) */ m_field173 /**/;
		static readonly Types.SomeImmutableGenericInterfaceRestrictingTU</* NonImmutableTypeHeldByImmutable(class, object, ) */ object /**/, int> m_field174;
		Types.SomeImmutableGenericInterfaceRestrictingTU</* NonImmutableTypeHeldByImmutable(class, object, ) */ object /**/, int>/* MemberIsNotReadOnly(Field, m_field175, AnalyzedClassMarkedImmutable) */ m_field175 /**/;
		readonly Types.SomeImmutableGenericInterfaceRestrictingTU</* NonImmutableTypeHeldByImmutable(class, object, ) */ object /**/, int>m_field176;
		Types.SomeImmutableGenericInterfaceRestrictingTU</* NonImmutableTypeHeldByImmutable(class, object, ) */ object /**/, int> Property80 { get; }
		Types.SomeImmutableGenericInterfaceRestrictingTU</* NonImmutableTypeHeldByImmutable(class, object, ) */ object /**/, int> Property81 { get { return default; } }

		private readonly IEnumerable<int> m_enumerable = Enumerable.Empty<int>();
		private readonly int[] m_array = Array.Empty<int>();
	}

	[Immutable]
	public sealed class AnalyzedImmutableGenericClassRestrictingT<[Immutable] T, U>
		where T : new()
		where U : new()
	{



		static T /* MemberIsNotReadOnly(Field, m_field177, AnalyzedImmutableGenericClassRestrictingT) */ m_field177 /**/;
		static readonly T m_field178;
		T /* MemberIsNotReadOnly(Field, m_field179, AnalyzedImmutableGenericClassRestrictingT) */ m_field179 /**/;
		readonly T m_field180;
		T Property82 { get; }
		T Property83 { get { return default; } }



		static T /* MemberIsNotReadOnly(Field, m_field181, AnalyzedImmutableGenericClassRestrictingT) */ m_field181 /**/ = new T();
		static readonly T m_field182 = new T();
		T /* MemberIsNotReadOnly(Field, m_field183, AnalyzedImmutableGenericClassRestrictingT) */ m_field183 /**/ = new T();
		readonly T m_field184 = new T();
		T Property84 { get; } = new T();
		T Property85 { get { return new T(); } }



		static /* TypeParameterIsNotKnownToBeImmutable(U) */ U /**/ /* MemberIsNotReadOnly(Field, m_field185, AnalyzedImmutableGenericClassRestrictingT) */ m_field185 /**/;
		static readonly /* TypeParameterIsNotKnownToBeImmutable(U) */ U /**/ m_field186;
		/* TypeParameterIsNotKnownToBeImmutable(U) */ U /**/ /* MemberIsNotReadOnly(Field, m_field187, AnalyzedImmutableGenericClassRestrictingT) */ m_field187 /**/;
		[Mutability.Unaudited( Because.ItHasntBeenLookedAt )]
		U m_field188;
		[Mutability.Audited( "<NAME>", "1970-01-01", "Rationale" )]
		U m_field189;
		readonly /* TypeParameterIsNotKnownToBeImmutable(U) */ U /**/ m_field190;
		[Mutability.Unaudited( Because.ItHasntBeenLookedAt )]
		readonly U m_field191;
		[Mutability.Audited( "<NAME>", "1970-01-01", "Rationale" )]
		readonly U m_field192;
		/* TypeParameterIsNotKnownToBeImmutable(U) */ U /**/ Property86 { get; }
		[Mutability.Unaudited( Because.ItHasntBeenLookedAt )]
		U Property87 { get; }
		[Mutability.Audited( "<NAME>", "1970-01-01", "Rationale" )]
		U Property88 { get; }
		U Property89 { get { return default; } }



		static U /* MemberIsNotReadOnly(Field, m_field193, AnalyzedImmutableGenericClassRestrictingT) */ m_field193 /**/ = /* TypeParameterIsNotKnownToBeImmutable(U) */ new U() /**/;
		static readonly U m_field194 = /* TypeParameterIsNotKnownToBeImmutable(U) */ new U() /**/;
		U /* MemberIsNotReadOnly(Field, m_field195, AnalyzedImmutableGenericClassRestrictingT) */ m_field195 /**/ = /* TypeParameterIsNotKnownToBeImmutable(U) */ new U() /**/;
		[Mutability.Unaudited( Because.ItHasntBeenLookedAt )]
		U m_field196 = new U();
		[Mutability.Audited( "<NAME>", "1970-01-01", "Rationale" )]
		U m_field197 = new U();
		readonly U m_field198 = /* TypeParameterIsNotKnownToBeImmutable(U) */ new U() /**/;
		[Mutability.Unaudited( Because.ItHasntBeenLookedAt )]
		readonly U m_field199 = new U();
		[Mutability.Audited( "<NAME>", "1970-01-01", "Rationale" )]
		readonly U m_field200 = new U();
		U Property90 { get; } = /* TypeParameterIsNotKnownToBeImmutable(U) */ new U() /**/;
		[Mutability.Unaudited( Because.ItHasntBeenLookedAt )]
		U Property91 { get; } = new U();
		[Mutability.Audited( "<NAME>", "1970-01-01", "Rationale" )]
		U Property92 { get; } = new U();



		static Types.SomeImmutableGenericInterfaceGivenT<T, U> /* MemberIsNotReadOnly(Field, m_field201, AnalyzedImmutableGenericClassRestrictingT) */ m_field201 /**/;
		static readonly Types.SomeImmutableGenericInterfaceGivenT<T, U> m_field202;
		Types.SomeImmutableGenericInterfaceGivenT<T, U> /* MemberIsNotReadOnly(Field, m_field203, AnalyzedImmutableGenericClassRestrictingT) */ m_field203 /**/;
		readonly Types.SomeImmutableGenericInterfaceGivenT<T, U> m_field204;
		Types.SomeImmutableGenericInterfaceGivenT<T, U> Property93 { get; }
		Types.SomeImmutableGenericInterfaceGivenT<T, U> Property94 { get { return default; } }



		static /* TypeParameterIsNotKnownToBeImmutable(U) */ Types.SomeImmutableGenericInterfaceGivenT<U, T> /**/ /* MemberIsNotReadOnly(Field, m_field205, AnalyzedImmutableGenericClassRestrictingT) */ m_field205 /**/;
		static readonly /* TypeParameterIsNotKnownToBeImmutable(U) */ Types.SomeImmutableGenericInterfaceGivenT<U, T> /**/ m_field206;
		/* TypeParameterIsNotKnownToBeImmutable(U) */ Types.SomeImmutableGenericInterfaceGivenT<U, T> /**/ /* MemberIsNotReadOnly(Field, m_field207, AnalyzedImmutableGenericClassRestrictingT) */ m_field207 /**/;
		readonly /* TypeParameterIsNotKnownToBeImmutable(U) */ Types.SomeImmutableGenericInterfaceGivenT<U, T> /**/ m_field208;
		/* TypeParameterIsNotKnownToBeImmutable(U) */ Types.SomeImmutableGenericInterfaceGivenT<U, T> /**/ Property95 { get; }
		Types.SomeImmutableGenericInterfaceGivenT<U, T> Property96 { get { return default; } }


		static Types.SomeImmutableGenericInterfaceGivenU<U, T> /* MemberIsNotReadOnly(Field, m_field209, AnalyzedImmutableGenericClassRestrictingT) */ m_field209 /**/;
		static readonly Types.SomeImmutableGenericInterfaceGivenU<U, T> m_field210;
		Types.SomeImmutableGenericInterfaceGivenU<U, T> /* MemberIsNotReadOnly(Field, m_field211, AnalyzedImmutableGenericClassRestrictingT) */ m_field211 /**/;
		readonly Types.SomeImmutableGenericInterfaceGivenU<U, T> m_field212;
		Types.SomeImmutableGenericInterfaceGivenU<U, T> Property97 { get; }
		Types.SomeImmutableGenericInterfaceGivenU<U, T> Property98 { get { return default; } }



		static /* TypeParameterIsNotKnownToBeImmutable(U) */ Types.SomeImmutableGenericInterfaceGivenU<T, U> /**/ /* MemberIsNotReadOnly(Field, m_field213, AnalyzedImmutableGenericClassRestrictingT) */ m_field213 /**/;
		static readonly /* TypeParameterIsNotKnownToBeImmutable(U) */ Types.SomeImmutableGenericInterfaceGivenU<T, U> /**/ m_field214;
		/* TypeParameterIsNotKnownToBeImmutable(U) */ Types.SomeImmutableGenericInterfaceGivenU<T, U> /**/ /* MemberIsNotReadOnly(Field, m_field215, AnalyzedImmutableGenericClassRestrictingT) */ m_field215 /**/;
		readonly /* TypeParameterIsNotKnownToBeImmutable(U) */ Types.SomeImmutableGenericInterfaceGivenU<T, U> /**/ m_field216;
		/* TypeParameterIsNotKnownToBeImmutable(U) */ Types.SomeImmutableGenericInterfaceGivenU<T, U> /**/ Property99 { get; }
		Types.SomeImmutableGenericInterfaceGivenU<T, U> Property100 { get { return default; } }



		static Types.SomeImmutableGenericInterfaceGivenTU<T, T> /* MemberIsNotReadOnly(Field, m_field217, AnalyzedImmutableGenericClassRestrictingT) */ m_field217 /**/;
		static readonly Types.SomeImmutableGenericInterfaceGivenTU<T, T> m_field218;
		Types.SomeImmutableGenericInterfaceGivenTU<T, T> /* MemberIsNotReadOnly(Field, m_field219, AnalyzedImmutableGenericClassRestrictingT) */ m_field219 /**/;
		readonly Types.SomeImmutableGenericInterfaceGivenTU<T, T> m_field220;
		Types.SomeImmutableGenericInterfaceGivenTU<T, T> Property101 { get; }
		Types.SomeImmutableGenericInterfaceGivenTU<T, T> Property102 { get { return default; } }



		static /* TypeParameterIsNotKnownToBeImmutable(U) */ Types.SomeImmutableGenericInterfaceGivenTU<T, U> /**/ /* MemberIsNotReadOnly(Field, m_field222, AnalyzedImmutableGenericClassRestrictingT) */ m_field222 /**/;
		static readonly /* TypeParameterIsNotKnownToBeImmutable(U) */ Types.SomeImmutableGenericInterfaceGivenTU<T, U> /**/ m_field223;
		/* TypeParameterIsNotKnownToBeImmutable(U) */ Types.SomeImmutableGenericInterfaceGivenTU<T, U> /**/ /* MemberIsNotReadOnly(Field, m_field224, AnalyzedImmutableGenericClassRestrictingT) */ m_field224 /**/;
		readonly /* TypeParameterIsNotKnownToBeImmutable(U) */ Types.SomeImmutableGenericInterfaceGivenTU<T, U> /**/ m_field225;
		/* TypeParameterIsNotKnownToBeImmutable(U) */ Types.SomeImmutableGenericInterfaceGivenTU<T, U> /**/ Property103 { get; }
		Types.SomeImmutableGenericInterfaceGivenTU<T, U> Property104 { get { return default; } }


		static /* TypeParameterIsNotKnownToBeImmutable(U) */ Types.SomeImmutableGenericInterfaceGivenTU<U, T> /**/ /* MemberIsNotReadOnly(Field, m_field226, AnalyzedImmutableGenericClassRestrictingT) */ m_field226 /**/;
		static readonly /* TypeParameterIsNotKnownToBeImmutable(U) */ Types.SomeImmutableGenericInterfaceGivenTU<U, T> /**/ m_field227;
		/* TypeParameterIsNotKnownToBeImmutable(U) */ Types.SomeImmutableGenericInterfaceGivenTU<U, T> /**/ /* MemberIsNotReadOnly(Field, m_field229, AnalyzedImmutableGenericClassRestrictingT) */ m_field229 /**/;
		readonly /* TypeParameterIsNotKnownToBeImmutable(U) */ Types.SomeImmutableGenericInterfaceGivenTU<U, T> /**/ m_field230;
		/* TypeParameterIsNotKnownToBeImmutable(U) */ Types.SomeImmutableGenericInterfaceGivenTU<U, T> /**/ Property105 { get; }
		Types.SomeImmutableGenericInterfaceGivenTU<U, T> Property106 { get { return default; } }



		static Types.SomeImmutableGenericInterfaceRestrictingT<T, U> /* MemberIsNotReadOnly(Field, m_field231, AnalyzedImmutableGenericClassRestrictingT) */ m_field231 /**/;
		static readonly Types.SomeImmutableGenericInterfaceRestrictingT<T, U> m_field232;
		Types.SomeImmutableGenericInterfaceRestrictingT<T, U> /* MemberIsNotReadOnly(Field, m_field233, AnalyzedImmutableGenericClassRestrictingT) */ m_field233 /**/;
		readonly Types.SomeImmutableGenericInterfaceRestrictingT<T, U> m_field234;
		Types.SomeImmutableGenericInterfaceRestrictingT<T, U> Property107 { get; }
		Types.SomeImmutableGenericInterfaceRestrictingT<T, U> Property108 { get { return default; } }



		static Types.SomeImmutableGenericInterfaceRestrictingT</* TypeParameterIsNotKnownToBeImmutable(U) */ U /**/, T> /* MemberIsNotReadOnly(Field, m_field235, AnalyzedImmutableGenericClassRestrictingT) */ m_field235 /**/;
		static readonly Types.SomeImmutableGenericInterfaceRestrictingT</* TypeParameterIsNotKnownToBeImmutable(U) */ U /**/, T> m_field236;
		Types.SomeImmutableGenericInterfaceRestrictingT</* TypeParameterIsNotKnownToBeImmutable(U) */ U /**/, T>/* MemberIsNotReadOnly(Field, m_field237, AnalyzedImmutableGenericClassRestrictingT) */ m_field237 /**/;
		readonly Types.SomeImmutableGenericInterfaceRestrictingT</* TypeParameterIsNotKnownToBeImmutable(U) */ U /**/, T> m_field238;
		Types.SomeImmutableGenericInterfaceRestrictingT</* TypeParameterIsNotKnownToBeImmutable(U) */ U /**/, T> Property109 { get; }
		Types.SomeImmutableGenericInterfaceRestrictingT</* TypeParameterIsNotKnownToBeImmutable(U) */ U /**/, T> Property110 { get { return default; } }


		static Types.SomeImmutableGenericInterfaceRestrictingU<U, T> /* MemberIsNotReadOnly(Field, m_field238_2, AnalyzedImmutableGenericClassRestrictingT) */ m_field238_2 /**/;
		static readonly Types.SomeImmutableGenericInterfaceRestrictingU<U, T> m_field239;
		Types.SomeImmutableGenericInterfaceRestrictingU<U, T> /* MemberIsNotReadOnly(Field, m_field240, AnalyzedImmutableGenericClassRestrictingT) */ m_field240 /**/;
		readonly Types.SomeImmutableGenericInterfaceRestrictingU<U, T> m_field241;
		Types.SomeImmutableGenericInterfaceRestrictingU<U, T> Property111 { get; }
		Types.SomeImmutableGenericInterfaceRestrictingU<U, T> Property112 { get { return default; } }



		static Types.SomeImmutableGenericInterfaceRestrictingU<T, /* TypeParameterIsNotKnownToBeImmutable(U) */ U /**/> /* MemberIsNotReadOnly(Field, m_field242, AnalyzedImmutableGenericClassRestrictingT) */ m_field242 /**/;
		static readonly Types.SomeImmutableGenericInterfaceRestrictingU<T, /* TypeParameterIsNotKnownToBeImmutable(U) */ U /**/> m_field243;
		Types.SomeImmutableGenericInterfaceRestrictingU<T, /* TypeParameterIsNotKnownToBeImmutable(U) */ U /**/> /* MemberIsNotReadOnly(Field, m_field244, AnalyzedImmutableGenericClassRestrictingT) */ m_field244 /**/;
		readonly Types.SomeImmutableGenericInterfaceRestrictingU<T, /* TypeParameterIsNotKnownToBeImmutable(U) */ U /**/> m_field245;
		Types.SomeImmutableGenericInterfaceRestrictingU<T, /* TypeParameterIsNotKnownToBeImmutable(U) */ U /**/> Property113 { get; }
		Types.SomeImmutableGenericInterfaceRestrictingU<T, /* TypeParameterIsNotKnownToBeImmutable(U) */ U /**/> Property114 { get { return default; } }



		static Types.SomeImmutableGenericInterfaceRestrictingTU<T, T> /* MemberIsNotReadOnly(Field, m_field246, AnalyzedImmutableGenericClassRestrictingT) */ m_field246 /**/;
		static readonly Types.SomeImmutableGenericInterfaceRestrictingTU<T, T> m_field247;
		Types.SomeImmutableGenericInterfaceRestrictingTU<T, T> /* MemberIsNotReadOnly(Field, m_field248, AnalyzedImmutableGenericClassRestrictingT) */ m_field248 /**/;
		readonly Types.SomeImmutableGenericInterfaceRestrictingTU<T, T> m_field249;
		Types.SomeImmutableGenericInterfaceRestrictingTU<T, T> Property115 { get; }
		Types.SomeImmutableGenericInterfaceRestrictingTU<T, T> Property116 { get { return default; } }



		static Types.SomeImmutableGenericInterfaceRestrictingTU<T, /* TypeParameterIsNotKnownToBeImmutable(U) */ U /**/> /* MemberIsNotReadOnly(Field, m_field250, AnalyzedImmutableGenericClassRestrictingT) */ m_field250 /**/;
		static readonly Types.SomeImmutableGenericInterfaceRestrictingTU<T, /* TypeParameterIsNotKnownToBeImmutable(U) */ U /**/> m_field251;
		Types.SomeImmutableGenericInterfaceRestrictingTU<T, /* TypeParameterIsNotKnownToBeImmutable(U) */ U /**/> /* MemberIsNotReadOnly(Field, m_field252, AnalyzedImmutableGenericClassRestrictingT) */ m_field252 /**/;
		readonly Types.SomeImmutableGenericInterfaceRestrictingTU<T, /* TypeParameterIsNotKnownToBeImmutable(U) */ U /**/> m_field253;
		Types.SomeImmutableGenericInterfaceRestrictingTU<T, /* TypeParameterIsNotKnownToBeImmutable(U) */ U /**/> Property117 { get; }
		Types.SomeImmutableGenericInterfaceRestrictingTU<T, /* TypeParameterIsNotKnownToBeImmutable(U) */ U /**/> Property118 { get { return default; } }


		static Types.SomeImmutableGenericInterfaceRestrictingTU</* TypeParameterIsNotKnownToBeImmutable(U) */ U /**/, T> /* MemberIsNotReadOnly(Field, m_field254, AnalyzedImmutableGenericClassRestrictingT) */ m_field254 /**/;
		static readonly Types.SomeImmutableGenericInterfaceRestrictingTU</* TypeParameterIsNotKnownToBeImmutable(U) */ U /**/, T> m_field255;
		Types.SomeImmutableGenericInterfaceRestrictingTU</* TypeParameterIsNotKnownToBeImmutable(U) */ U /**/, T> /* MemberIsNotReadOnly(Field, m_field256, AnalyzedImmutableGenericClassRestrictingT) */ m_field256 /**/;
		readonly Types.SomeImmutableGenericInterfaceRestrictingTU</* TypeParameterIsNotKnownToBeImmutable(U) */ U /**/, T> m_field257;
		Types.SomeImmutableGenericInterfaceRestrictingTU</* TypeParameterIsNotKnownToBeImmutable(U) */ U /**/, T> Property119 { get; }
		Types.SomeImmutableGenericInterfaceRestrictingTU</* TypeParameterIsNotKnownToBeImmutable(U) */ U /**/, T> Property120 { get { return default; } }

		[Mutability.Audited( "<NAME>", "1970-01-01", "Rationale" )]
		[Mutability.Unaudited(Because.ItsSketchy)]
		object /* ConflictingImmutability(Mutability.Audited, Mutability.Unaudited, field) */ someMutabilityAuditedAndUnauditedObject /**/;

		[Statics.Audited]
		[Statics.Unaudited(Because.ItsSketchy)]
		static object /* ConflictingImmutability(Statics.Audited, Statics.Unaudited, field) */ someStaticsAuditedAndUnauditedObject /**/;

		[Mutability.Audited( "<NAME>", "1970-01-01", "Rationale" )]
		static /* NonImmutableTypeHeldByImmutable(class, object, ) */ object /**/ /* InvalidAuditType(static, field, Statics.*) | MemberIsNotReadOnly(Field, someStaticsMutabilityAuditedObject, AnalyzedImmutableGenericClassRestrictingT) */ someStaticsMutabilityAuditedObject /**/;

		[Statics.Audited]
		/* NonImmutableTypeHeldByImmutable(class, object, ) */ object /**/ /* InvalidAuditType(non-static, field, Mutability.*) | MemberIsNotReadOnly(Field, someNonstaticStaticsAuditedObject, AnalyzedImmutableGenericClassRestrictingT) */ someNonstaticStaticsAuditedObject /**/;

		[Statics.Unaudited(Because.ItsSketchy)]
		[Mutability.Audited( "<NAME>", "1970-01-01", "Rationale" )]
		[Mutability.Unaudited(Because.ItsSketchy)]
		object /* InvalidAuditType(non-static, field, Mutability.*) | ConflictingImmutability(Mutability.Audited, Mutability.Unaudited, field) */ someNonstaticDoublyAuditedObject /**/;

		[Mutability.Audited( "<NAME>", "1970-01-01", "Rationale" )]
		[Statics.Unaudited(Because.ItsSketchy)]
		static object /* InvalidAuditType(static, field, Statics.*) */ someStaticSortOfDoublyAuditedObject /**/;

		void Method() {
			Types.SomeGenericMethod<T, U>();
			Types.SomeGenericMethod<U, T>();
			Types.SomeGenericMethod<T, T>();
			Types.SomeGenericMethod<U, U>();
			Types.SomeGenericMethodRestrictingT<T, U>();
			Types.SomeGenericMethodRestrictingT</* TypeParameterIsNotKnownToBeImmutable(U) */ U /**/, T>();
			Types.SomeGenericMethodRestrictingT<T, T>();
			Types.SomeGenericMethodRestrictingT </* TypeParameterIsNotKnownToBeImmutable(U) */ U /**/, U>();
			Types.SomeGenericMethodRestrictingU<T, /* TypeParameterIsNotKnownToBeImmutable(U) */ U /**/>();
			Types.SomeGenericMethodRestrictingU<U, T>();
			Types.SomeGenericMethodRestrictingU<T, T>();
			Types.SomeGenericMethodRestrictingU<U, /* TypeParameterIsNotKnownToBeImmutable(U) */ U /**/>();
			Types.SomeGenericMethodRestrictingTU<T, /* TypeParameterIsNotKnownToBeImmutable(U) */ U /**/>();
			Types.SomeGenericMethodRestrictingTU</* TypeParameterIsNotKnownToBeImmutable(U) */ U /**/, T>();
			Types.SomeGenericMethodRestrictingTU<T, T>();
			Types.SomeGenericMethodRestrictingTU</* TypeParameterIsNotKnownToBeImmutable(U) */ U /**/, /* TypeParameterIsNotKnownToBeImmutable(U) */ U /**/>();
			object foo = new object();
			int bar = 529;
			Types.SomeGenericMethodRestrictingT</* NonImmutableTypeHeldByImmutable(class, object, ) */ object /**/>(foo);
			Types./* NonImmutableTypeHeldByImmutable(class, object, ) */ SomeGenericMethodRestrictingT /**/(foo);
			Types.SomeGenericMethodRestrictingT<int>(bar);
			Types.SomeGenericMethodRestrictingT(bar);
		}
	}

	[ConditionallyImmutable]
	public sealed class AnalyzedImmutableGenericClassGivenT<[ConditionallyImmutable.OnlyIf] T, U>
		where T : new()
		where U : new()
	{



		static /* TypeParameterIsNotKnownToBeImmutable(T) */ T /**/ /* MemberIsNotReadOnly(Field, m_field258, AnalyzedImmutableGenericClassGivenT) */ m_field258 /**/;
		static readonly /* TypeParameterIsNotKnownToBeImmutable(T) */ T /**/ m_field259;
		T /* MemberIsNotReadOnly(Field, m_field260, AnalyzedImmutableGenericClassGivenT) */ m_field260 /**/;
		readonly T m_field261;
		T Property121 { get; }
		T Property122 { get { return default; } }



		static T /* MemberIsNotReadOnly(Field, m_field262, AnalyzedImmutableGenericClassGivenT) */ m_field262 /**/ = /* TypeParameterIsNotKnownToBeImmutable(T) */ new T() /**/;
		static readonly T m_field263 = /* TypeParameterIsNotKnownToBeImmutable(T) */ new T() /**/;
		T /* MemberIsNotReadOnly(Field, m_field265, AnalyzedImmutableGenericClassGivenT) */ m_field265 /**/ = new T();
		readonly T m_field266 = new T();
		T Property123 { get; } = new T();
		T Property124 { get { return new T(); } }



		static /* TypeParameterIsNotKnownToBeImmutable(U) */ U /**/ /* MemberIsNotReadOnly(Field, m_field267, AnalyzedImmutableGenericClassGivenT) */ m_field267 /**/;
		static readonly /* TypeParameterIsNotKnownToBeImmutable(U) */ U /**/ m_field268;
		/* TypeParameterIsNotKnownToBeImmutable(U) */ U /**/ /* MemberIsNotReadOnly(Field, m_field269, AnalyzedImmutableGenericClassGivenT) */ m_field269 /**/;
		[Mutability.Unaudited( Because.ItHasntBeenLookedAt )]
		U m_field270;
		[Mutability.Audited( "<NAME>", "1970-01-01", "Rationale" )]
		U m_field271;
		readonly /* TypeParameterIsNotKnownToBeImmutable(U) */ U /**/ m_field272;
		[Mutability.Unaudited( Because.ItHasntBeenLookedAt )]
		readonly U m_field273;
		[Mutability.Audited( "<NAME>", "1970-01-01", "Rationale" )]
		readonly U m_field274;
		/* TypeParameterIsNotKnownToBeImmutable(U) */ U /**/ Property125 { get; }
		[Mutability.Unaudited( Because.ItHasntBeenLookedAt )]
		U Property126 { get; }
		[Mutability.Audited( "<NAME>", "1970-01-01", "Rationale" )]
		U Property127 { get; }
		U Property128 { get { return default; } }



		static U /* MemberIsNotReadOnly(Field, m_field275, AnalyzedImmutableGenericClassGivenT) */ m_field275 /**/ = /* TypeParameterIsNotKnownToBeImmutable(U) */ new U() /**/;
		static readonly U m_field276 = /* TypeParameterIsNotKnownToBeImmutable(U) */ new U() /**/;
		U /* MemberIsNotReadOnly(Field, m_field278, AnalyzedImmutableGenericClassGivenT) */ m_field278 /**/ = /* TypeParameterIsNotKnownToBeImmutable(U) */ new U() /**/;
		[Mutability.Unaudited( Because.ItHasntBeenLookedAt )]
		U m_field279 = new U();
		[Mutability.Audited( "<NAME>", "1970-01-01", "Rationale" )]
		U m_field280 = new U();
		readonly U m_field281 = /* TypeParameterIsNotKnownToBeImmutable(U) */ new U() /**/;
		[Mutability.Unaudited( Because.ItHasntBeenLookedAt )]
		readonly U m_field282 = new U();
		[Mutability.Audited( "<NAME>", "1970-01-01", "Rationale" )]
		readonly U m_field283 = new U();
		U Property129 { get; } = /* TypeParameterIsNotKnownToBeImmutable(U) */ new U() /**/;
		[Mutability.Unaudited( Because.ItHasntBeenLookedAt )]
		U Property130 { get; } = new U();
		[Mutability.Audited( "<NAME>", "1970-01-01", "Rationale" )]
		U Property131 { get; } = new U();



		static /* TypeParameterIsNotKnownToBeImmutable(T) */ Types.SomeImmutableGenericInterfaceGivenT<T, U> /**/ /* MemberIsNotReadOnly(Field, m_field284, AnalyzedImmutableGenericClassGivenT) */ m_field284 /**/;
		static readonly /* TypeParameterIsNotKnownToBeImmutable(T) */ Types.SomeImmutableGenericInterfaceGivenT<T, U> /**/ m_field285;
		Types.SomeImmutableGenericInterfaceGivenT<T, U> /* MemberIsNotReadOnly(Field, m_field287, AnalyzedImmutableGenericClassGivenT) */ m_field287 /**/;
		readonly Types.SomeImmutableGenericInterfaceGivenT<T, U> m_field288;
		Types.SomeImmutableGenericInterfaceGivenT<T, U> Property132 { get; }
		Types.SomeImmutableGenericInterfaceGivenT<T, U> Property133 { get { return default; } }



		static /* TypeParameterIsNotKnownToBeImmutable(U) */ Types.SomeImmutableGenericInterfaceGivenT<U, T> /**/ /* MemberIsNotReadOnly(Field, m_field289, AnalyzedImmutableGenericClassGivenT) */ m_field289 /**/;
		static readonly /* TypeParameterIsNotKnownToBeImmutable(U) */ Types.SomeImmutableGenericInterfaceGivenT<U, T> /**/ m_field290;
		/* TypeParameterIsNotKnownToBeImmutable(U) */ Types.SomeImmutableGenericInterfaceGivenT<U, T> /**/ /* MemberIsNotReadOnly(Field, m_field292, AnalyzedImmutableGenericClassGivenT) */ m_field292 /**/;
		readonly /* TypeParameterIsNotKnownToBeImmutable(U) */ Types.SomeImmutableGenericInterfaceGivenT<U, T> /**/ m_field293;
		/* TypeParameterIsNotKnownToBeImmutable(U) */ Types.SomeImmutableGenericInterfaceGivenT<U, T> /**/ Property134 { get; }
		Types.SomeImmutableGenericInterfaceGivenT<U, T> Property135 { get { return default; } }


		static  /* TypeParameterIsNotKnownToBeImmutable(T) */ Types.SomeImmutableGenericInterfaceGivenU<U, T> /**/ /* MemberIsNotReadOnly(Field, m_field294, AnalyzedImmutableGenericClassGivenT) */ m_field294 /**/;
		static readonly  /* TypeParameterIsNotKnownToBeImmutable(T) */ Types.SomeImmutableGenericInterfaceGivenU<U, T> /**/ m_field295;
		Types.SomeImmutableGenericInterfaceGivenU<U, T> /* MemberIsNotReadOnly(Field, m_field297, AnalyzedImmutableGenericClassGivenT) */ m_field297 /**/;
		readonly Types.SomeImmutableGenericInterfaceGivenU<U, T> m_field298;
		Types.SomeImmutableGenericInterfaceGivenU<U, T> Property136 { get; }
		Types.SomeImmutableGenericInterfaceGivenU<U, T> Property137 { get { return default; } }



		static /* TypeParameterIsNotKnownToBeImmutable(U) */ Types.SomeImmutableGenericInterfaceGivenU<T, U> /**/ /* MemberIsNotReadOnly(Field, m_field299, AnalyzedImmutableGenericClassGivenT) */ m_field299 /**/;
		static readonly /* TypeParameterIsNotKnownToBeImmutable(U) */ Types.SomeImmutableGenericInterfaceGivenU<T, U> /**/ m_field300;
		/* TypeParameterIsNotKnownToBeImmutable(U) */ Types.SomeImmutableGenericInterfaceGivenU<T, U> /**/ /* MemberIsNotReadOnly(Field, m_field301, AnalyzedImmutableGenericClassGivenT) */ m_field301 /**/;
		readonly /* TypeParameterIsNotKnownToBeImmutable(U) */ Types.SomeImmutableGenericInterfaceGivenU<T, U> /**/ m_field302;
		/* TypeParameterIsNotKnownToBeImmutable(U) */ Types.SomeImmutableGenericInterfaceGivenU<T, U> /**/ Property138 { get; }
		Types.SomeImmutableGenericInterfaceGivenU<T, U> Property139 { get { return default; } }



		static  /* TypeParameterIsNotKnownToBeImmutable(T) */ Types.SomeImmutableGenericInterfaceGivenTU<T, T> /**/ /* MemberIsNotReadOnly(Field, m_field303, AnalyzedImmutableGenericClassGivenT) */ m_field303 /**/;
		static readonly  /* TypeParameterIsNotKnownToBeImmutable(T) */ Types.SomeImmutableGenericInterfaceGivenTU<T, T> /**/ m_field304;
		Types.SomeImmutableGenericInterfaceGivenTU<T, T> /* MemberIsNotReadOnly(Field, m_field305, AnalyzedImmutableGenericClassGivenT) */ m_field305 /**/;
		readonly Types.SomeImmutableGenericInterfaceGivenTU<T, T> m_field306;
		Types.SomeImmutableGenericInterfaceGivenTU<T, T> Property140 { get; }
		Types.SomeImmutableGenericInterfaceGivenTU<T, T> Property141 { get { return default; } }



		static /* TypeParameterIsNotKnownToBeImmutable(T) */ Types.SomeImmutableGenericInterfaceGivenTU<T, U> /**/ /* MemberIsNotReadOnly(Field, m_field307, AnalyzedImmutableGenericClassGivenT) */ m_field307 /**/;
		static readonly /*  TypeParameterIsNotKnownToBeImmutable(T) */ Types.SomeImmutableGenericInterfaceGivenTU<T, U> /**/ m_field308;
		/* TypeParameterIsNotKnownToBeImmutable(U) */ Types.SomeImmutableGenericInterfaceGivenTU<T, U> /**/ /* MemberIsNotReadOnly(Field, m_field310, AnalyzedImmutableGenericClassGivenT) */ m_field310 /**/;
		readonly /* TypeParameterIsNotKnownToBeImmutable(U) */ Types.SomeImmutableGenericInterfaceGivenTU<T, U> /**/ m_field311;
		/* TypeParameterIsNotKnownToBeImmutable(U) */ Types.SomeImmutableGenericInterfaceGivenTU<T, U> /**/ Property142 { get; }
		Types.SomeImmutableGenericInterfaceGivenTU<T, U> Property143 { get { return default; } }


		static /* TypeParameterIsNotKnownToBeImmutable(U) */ Types.SomeImmutableGenericInterfaceGivenTU<U, T> /**/ /* MemberIsNotReadOnly(Field, m_field312, AnalyzedImmutableGenericClassGivenT) */ m_field312 /**/;
		static readonly /* TypeParameterIsNotKnownToBeImmutable(U) */ Types.SomeImmutableGenericInterfaceGivenTU<U, T> /**/ m_field313;
		/* TypeParameterIsNotKnownToBeImmutable(U) */ Types.SomeImmutableGenericInterfaceGivenTU<U, T> /**/ /* MemberIsNotReadOnly(Field, m_field314, AnalyzedImmutableGenericClassGivenT) */ m_field314 /**/;
		readonly /* TypeParameterIsNotKnownToBeImmutable(U) */ Types.SomeImmutableGenericInterfaceGivenTU<U, T> /**/ m_field315;
		/* TypeParameterIsNotKnownToBeImmutable(U) */ Types.SomeImmutableGenericInterfaceGivenTU<U, T> /**/ Property144 { get; }
		Types.SomeImmutableGenericInterfaceGivenTU<U, T> Property145 { get { return default; } }



		static Types.SomeImmutableGenericInterfaceRestrictingT</* TypeParameterIsNotKnownToBeImmutable(T) */ T /**/, U> /* MemberIsNotReadOnly(Field, m_field316, AnalyzedImmutableGenericClassGivenT) */ m_field316 /**/;
		static readonly Types.SomeImmutableGenericInterfaceRestrictingT</* TypeParameterIsNotKnownToBeImmutable(T) */ T /**/, U> m_field317;
		Types.SomeImmutableGenericInterfaceRestrictingT</* TypeParameterIsNotKnownToBeImmutable(T) */ T /**/, U> /* MemberIsNotReadOnly(Field, m_field318, AnalyzedImmutableGenericClassGivenT) */ m_field318 /**/;
		readonly Types.SomeImmutableGenericInterfaceRestrictingT</* TypeParameterIsNotKnownToBeImmutable(T) */ T /**/, U> m_field319;
		Types.SomeImmutableGenericInterfaceRestrictingT</* TypeParameterIsNotKnownToBeImmutable(T) */ T /**/, U> Property146 { get; }
		Types.SomeImmutableGenericInterfaceRestrictingT</* TypeParameterIsNotKnownToBeImmutable(T) */ T /**/, U> Property147 { get { return default; } }



		static Types.SomeImmutableGenericInterfaceRestrictingT</* TypeParameterIsNotKnownToBeImmutable(U) */ U /**/, T> /* MemberIsNotReadOnly(Field, m_field320, AnalyzedImmutableGenericClassGivenT) */ m_field320 /**/;
		static readonly Types.SomeImmutableGenericInterfaceRestrictingT</* TypeParameterIsNotKnownToBeImmutable(U) */ U /**/, T> m_field321;
		Types.SomeImmutableGenericInterfaceRestrictingT</* TypeParameterIsNotKnownToBeImmutable(U) */ U /**/, T>/* MemberIsNotReadOnly(Field, m_field323, AnalyzedImmutableGenericClassGivenT) */ m_field323 /**/;
		readonly Types.SomeImmutableGenericInterfaceRestrictingT</* TypeParameterIsNotKnownToBeImmutable(U) */ U /**/, T> m_field324;
		Types.SomeImmutableGenericInterfaceRestrictingT</* TypeParameterIsNotKnownToBeImmutable(U) */ U /**/, T> Property148 { get; }
		Types.SomeImmutableGenericInterfaceRestrictingT</* TypeParameterIsNotKnownToBeImmutable(U) */ U /**/, T> Property149 { get { return default; } }


		static Types.SomeImmutableGenericInterfaceRestrictingU<U, /* TypeParameterIsNotKnownToBeImmutable(T) */ T /**/> /* MemberIsNotReadOnly(Field, m_field325, AnalyzedImmutableGenericClassGivenT) */ m_field325 /**/;
		static readonly Types.SomeImmutableGenericInterfaceRestrictingU<U, /* TypeParameterIsNotKnownToBeImmutable(T) */ T /**/> m_field326;
		Types.SomeImmutableGenericInterfaceRestrictingU<U, /* TypeParameterIsNotKnownToBeImmutable(T) */ T /**/> /* MemberIsNotReadOnly(Field, m_field328, AnalyzedImmutableGenericClassGivenT) */ m_field328 /**/;
		readonly Types.SomeImmutableGenericInterfaceRestrictingU<U, /* TypeParameterIsNotKnownToBeImmutable(T) */ T /**/> m_field329;
		Types.SomeImmutableGenericInterfaceRestrictingU<U, /* TypeParameterIsNotKnownToBeImmutable(T) */ T /**/> Property150 { get; }
		Types.SomeImmutableGenericInterfaceRestrictingU<U, /* TypeParameterIsNotKnownToBeImmutable(T) */ T /**/> Property151 { get { return default; } }



		static Types.SomeImmutableGenericInterfaceRestrictingU<T, /* TypeParameterIsNotKnownToBeImmutable(U) */ U /**/> /* MemberIsNotReadOnly(Field, m_field331, AnalyzedImmutableGenericClassGivenT) */ m_field331 /**/;
		static readonly Types.SomeImmutableGenericInterfaceRestrictingU<T, /* TypeParameterIsNotKnownToBeImmutable(U) */ U /**/> m_field332;
		Types.SomeImmutableGenericInterfaceRestrictingU<T, /* TypeParameterIsNotKnownToBeImmutable(U) */ U /**/> /* MemberIsNotReadOnly(Field, m_field334, AnalyzedImmutableGenericClassGivenT) */ m_field334 /**/;
		readonly Types.SomeImmutableGenericInterfaceRestrictingU<T, /* TypeParameterIsNotKnownToBeImmutable(U) */ U /**/> m_field335;
		Types.SomeImmutableGenericInterfaceRestrictingU<T, /* TypeParameterIsNotKnownToBeImmutable(U) */ U /**/> Property152 { get; }
		Types.SomeImmutableGenericInterfaceRestrictingU<T, /* TypeParameterIsNotKnownToBeImmutable(U) */ U /**/> Property153 { get { return default; } }



		static Types.SomeImmutableGenericInterfaceRestrictingTU</* TypeParameterIsNotKnownToBeImmutable(T) */ T /**/, /* TypeParameterIsNotKnownToBeImmutable(T) */ T /**/> /* MemberIsNotReadOnly(Field, m_field336, AnalyzedImmutableGenericClassGivenT) */ m_field336 /**/;
		static readonly Types.SomeImmutableGenericInterfaceRestrictingTU</* TypeParameterIsNotKnownToBeImmutable(T) */ T /**/, /* TypeParameterIsNotKnownToBeImmutable(T) */ T /**/> m_field337;
		Types.SomeImmutableGenericInterfaceRestrictingTU</* TypeParameterIsNotKnownToBeImmutable(T) */ T /**/, /* TypeParameterIsNotKnownToBeImmutable(T) */ T /**/> /* MemberIsNotReadOnly(Field, m_field338, AnalyzedImmutableGenericClassGivenT) */ m_field338 /**/;
		readonly Types.SomeImmutableGenericInterfaceRestrictingTU</* TypeParameterIsNotKnownToBeImmutable(T) */ T /**/, /* TypeParameterIsNotKnownToBeImmutable(T) */ T /**/> m_field339;
		Types.SomeImmutableGenericInterfaceRestrictingTU</* TypeParameterIsNotKnownToBeImmutable(T) */ T /**/, /* TypeParameterIsNotKnownToBeImmutable(T) */ T /**/> Property154 { get; }
		Types.SomeImmutableGenericInterfaceRestrictingTU</* TypeParameterIsNotKnownToBeImmutable(T) */ T /**/, /* TypeParameterIsNotKnownToBeImmutable(T) */ T /**/> Property155 { get { return default; } }



		static Types.SomeImmutableGenericInterfaceRestrictingTU</* TypeParameterIsNotKnownToBeImmutable(T) */ T /**/, /* TypeParameterIsNotKnownToBeImmutable(U) */ U /**/> /* MemberIsNotReadOnly(Field, m_field340, AnalyzedImmutableGenericClassGivenT) */ m_field340 /**/;
		static readonly Types.SomeImmutableGenericInterfaceRestrictingTU</* TypeParameterIsNotKnownToBeImmutable(T) */ T /**/, /* TypeParameterIsNotKnownToBeImmutable(U) */ U /**/> m_field341;
		Types.SomeImmutableGenericInterfaceRestrictingTU</* TypeParameterIsNotKnownToBeImmutable(T) */ T /**/, /* TypeParameterIsNotKnownToBeImmutable(U) */ U /**/> /* MemberIsNotReadOnly(Field, m_field342, AnalyzedImmutableGenericClassGivenT) */ m_field342 /**/;
		readonly Types.SomeImmutableGenericInterfaceRestrictingTU</* TypeParameterIsNotKnownToBeImmutable(T) */ T /**/, /* TypeParameterIsNotKnownToBeImmutable(U) */ U /**/> m_field343;
		Types.SomeImmutableGenericInterfaceRestrictingTU</* TypeParameterIsNotKnownToBeImmutable(T) */ T /**/, /* TypeParameterIsNotKnownToBeImmutable(U) */ U /**/> Property156 { get; }
		Types.SomeImmutableGenericInterfaceRestrictingTU</* TypeParameterIsNotKnownToBeImmutable(T) */ T /**/, /* TypeParameterIsNotKnownToBeImmutable(U) */ U /**/> Property157 { get { return default; } }


		static Types.SomeImmutableGenericInterfaceRestrictingTU</* TypeParameterIsNotKnownToBeImmutable(U) */ U /**/, /* TypeParameterIsNotKnownToBeImmutable(T) */ T /**/> /* MemberIsNotReadOnly(Field, m_field344, AnalyzedImmutableGenericClassGivenT) */ m_field344 /**/;
		static readonly Types.SomeImmutableGenericInterfaceRestrictingTU</* TypeParameterIsNotKnownToBeImmutable(U) */ U /**/, /* TypeParameterIsNotKnownToBeImmutable(T) */ T /**/> m_field345;
		Types.SomeImmutableGenericInterfaceRestrictingTU</* TypeParameterIsNotKnownToBeImmutable(U) */ U /**/, /* TypeParameterIsNotKnownToBeImmutable(T) */ T /**/> /* MemberIsNotReadOnly(Field, m_field346, AnalyzedImmutableGenericClassGivenT) */ m_field346 /**/;
		readonly Types.SomeImmutableGenericInterfaceRestrictingTU</* TypeParameterIsNotKnownToBeImmutable(U) */ U /**/, /* TypeParameterIsNotKnownToBeImmutable(T) */ T /**/> m_field347;
		Types.SomeImmutableGenericInterfaceRestrictingTU</* TypeParameterIsNotKnownToBeImmutable(U) */ U /**/, /* TypeParameterIsNotKnownToBeImmutable(T) */ T /**/> Property158 { get; }
		Types.SomeImmutableGenericInterfaceRestrictingTU</* TypeParameterIsNotKnownToBeImmutable(U) */ U /**/, /* TypeParameterIsNotKnownToBeImmutable(T) */ T /**/> Property159 { get { return default; } }

		// These are MethodDeclarationSyntax
		void SomeGenericMethodConditionallyRestrictingT</* UnexpectedConditionalImmutability */ [ConditionallyImmutable.OnlyIf] T /**/>() { }
		void SomeGenericMethodConditionallyRestrictingT</* UnexpectedConditionalImmutability */ [ConditionallyImmutable.OnlyIf] T /**/, U>() { }
		void SomeGenericMethodConditionallyRestrictingU<T, /* UnexpectedConditionalImmutability */ [ConditionallyImmutable.OnlyIf] U /**/>() { }
		void SomeGenericMethodConditionallyRestrictingTU</* UnexpectedConditionalImmutability */ [ConditionallyImmutable.OnlyIf] T /**/, /* UnexpectedConditionalImmutability */ [ConditionallyImmutable.OnlyIf] U /**/>() { }

		sealed class SomeGenericClassDoublyRestrictingT<[Immutable] [ConditionallyImmutable.OnlyIf] /* ConflictingImmutability(Immutable, ConditionallyImmutable.OnlyIf, typeparameter) */ T /**/> { }
		sealed class SomeGenericClassRestrictingT<[Immutable] T> { }
		sealed class SomeGenericClassConditionallyRestrictingT<[ConditionallyImmutable.OnlyIf] T> { }

		void Method()
		{
			// These are LocalFunctionStatementSyntax
			void SomeGenericMethodConditionallyRestrictingT</* UnexpectedConditionalImmutability */ [ConditionallyImmutable.OnlyIf] T /**/>() { }
			void SomeGenericMethodConditionallyRestrictingT2</* UnexpectedConditionalImmutability */ [ConditionallyImmutable.OnlyIf] T /**/, U>() { }
			void SomeGenericMethodConditionallyRestrictingU<T, /* UnexpectedConditionalImmutability */ [ConditionallyImmutable.OnlyIf] U /**/>() { }
			void SomeGenericMethodConditionallyRestrictingTU</* UnexpectedConditionalImmutability */ [ConditionallyImmutable.OnlyIf] T /**/, /* UnexpectedConditionalImmutability */ [ConditionallyImmutable.OnlyIf] U /**/>() { }
			Types.SomeGenericMethod<T, U>();
			Types.SomeGenericMethod<U, T>();
			Types.SomeGenericMethod<T, T>();
			Types.SomeGenericMethod<U, U>();
			Types.SomeGenericMethodRestrictingT</* TypeParameterIsNotKnownToBeImmutable(T) */ T /**/, U>();
			Types.SomeGenericMethodRestrictingT</* TypeParameterIsNotKnownToBeImmutable(U) */ U /**/, T>();
			Types.SomeGenericMethodRestrictingT</* TypeParameterIsNotKnownToBeImmutable(T) */ T /**/, T>();
			Types.SomeGenericMethodRestrictingT</* TypeParameterIsNotKnownToBeImmutable(U) */ U /**/, U>();
			Types.SomeGenericMethodRestrictingU<T, /* TypeParameterIsNotKnownToBeImmutable(U) */ U /**/>();
			Types.SomeGenericMethodRestrictingU<U, /* TypeParameterIsNotKnownToBeImmutable(T) */ T /**/>();
			Types.SomeGenericMethodRestrictingU<T, /* TypeParameterIsNotKnownToBeImmutable(T) */ T /**/>();
			Types.SomeGenericMethodRestrictingU<U, /* TypeParameterIsNotKnownToBeImmutable(U) */ U /**/>();
			Types.SomeGenericMethodRestrictingTU</* TypeParameterIsNotKnownToBeImmutable(T) */ T /**/, /* TypeParameterIsNotKnownToBeImmutable(U) */ U /**/>();
			Types.SomeGenericMethodRestrictingTU</* TypeParameterIsNotKnownToBeImmutable(U) */ U /**/, /* TypeParameterIsNotKnownToBeImmutable(T) */ T /**/>();
			Types.SomeGenericMethodRestrictingTU</* TypeParameterIsNotKnownToBeImmutable(T) */ T /**/, /* TypeParameterIsNotKnownToBeImmutable(T) */ T /**/>();
			Types.SomeGenericMethodRestrictingTU</* TypeParameterIsNotKnownToBeImmutable(U) */ U /**/, /* TypeParameterIsNotKnownToBeImmutable(U) */ U /**/>();
			object foo = new object();
			int bar = 529;
			Types.SomeGenericMethodRestrictingT</* NonImmutableTypeHeldByImmutable(class, object, ) */ object /**/>(foo);
			Types./* NonImmutableTypeHeldByImmutable(class, object, ) */ SomeGenericMethodRestrictingT /**/(foo);
			Types.SomeGenericMethodRestrictingT<int>(bar);
			Types.SomeGenericMethodRestrictingT(bar);
		}
	}

	public static class StaticAudits {
		[Statics.Audited]
		static int staticint1;

		[Statics.Audited]
		static readonly object staticreadonlyobject1;

		[/* UnnecessaryMutabilityAnnotation */ Statics.Audited /**/]
		static readonly object m_lock1 = new object();

		[Statics.Unaudited( Because.ItHasntBeenLookedAt )]
		static int staticint2;

		[Statics.Unaudited( Because.ItHasntBeenLookedAt )]
		static readonly object staticreadonlyobject2;

		[/* UnnecessaryMutabilityAnnotation */ Statics.Unaudited( Because.ItHasntBeenLookedAt ) /**/]
		static readonly object m_lock = new object();
    }

	[Immutable]
	public record SomeRecord {
		SomeRecord V { get; }

		int W { get; }
		Types.SomeImmutableClass X { get; }

		int /* MemberIsNotReadOnly(Property, Y, SomeRecord) */ Y /**/ { get; set; }

		/* NonImmutableTypeHeldByImmutable(class, object, ) */ object /**/ Z { get; }

		public SomeRecord( SomeRecord v, int w, Types.SomeImmutableClass x, int y, object z )
			=> /* UnknownImmutabilityAssignmentKind(Deconstructed assignment) */ (V, W, X, Y, Z) /**/ = (v, w, x, y, z);
    }

	record NonImmutableBaseRecord(object x);

	[Immutable]
	record DerivedRecordWithQuestionableBase :
		/* NonImmutableTypeHeldByImmutable(class, SpecTests.NonImmutableBaseRecord,  (or [ImmutableBaseClass])) */ NonImmutableBaseRecord /**/
	{
	  public DerivedRecordWithQuestionableBase() : base( new object() ) {}
	}

	[ImmutableBaseClass]
	record BaseRecordWithImmutableBaseClass { }

	[Immutable]
	record DerivedFromImmutableBaseClassRecord : BaseRecordWithImmutableBaseClass {
		/* NonImmutableTypeHeldByImmutable(class, SpecTests.BaseRecordWithImmutableBaseClass, ) */ BaseRecordWithImmutableBaseClass /**/ X { get; }
    }

	[Immutable]
	record ConciseRecord(
		int xxxx,
		/* NonImmutableTypeHeldByImmutable(class, object, ) */ object /**/ x,
		/* NonImmutableTypeHeldByImmutable(class, SpecTests.BaseRecordWithImmutableBaseClass, ) */ BaseRecordWithImmutableBaseClass /**/ y,
		ConciseRecord z
	);
}

namespace ConsistencyTests {

	public interface IVanilla { }
	public interface IVanilla2 { }
	public class VanillaBase : IVanilla { }
	public class VanillaDerived : VanillaBase { }
	public sealed class VanillaDerived2 : VanillaDerived, IVanilla2 { }
	public struct VanillaStruct : IVanilla, IVanilla2 { }

	[Immutable]
	public interface ISomethingImmutable { }

	[Immutable]
	public class HappyImplementor : ISomethingImmutable { }

	[Immutable]
	public sealed class HappyDeriver : HappyImplementor { }

	[Immutable]
	public struct HappyStructImplementor : ISomethingImmutable { }

	public sealed class
		/* MissingTransitiveImmutableAttribute(ConsistencyTests.SadImplementor, , interface, ConsistencyTests.ISomethingImmutable) */ SadImplementor /**/
		: ISomethingImmutable { }

	public struct
		/* MissingTransitiveImmutableAttribute(ConsistencyTests.SadStructImplementor, , interface, ConsistencyTests.ISomethingImmutable) */ SadStructImplementor /**/
		: ISomethingImmutable { }

	public sealed class
		/* MissingTransitiveImmutableAttribute(ConsistencyTests.SadDeriver, , base class, ConsistencyTests.HappyImplementor) */ SadDeriver /**/
		: HappyImplementor { }

	public sealed class
		/* MissingTransitiveImmutableAttribute(ConsistencyTests.SadImplementor2, , interface, ConsistencyTests.ISomethingImmutable) */ SadImplementor2 /**/
		: VanillaBase, IVanilla, IVanilla2, ISomethingImmutable { }

	public sealed class
		/* MissingTransitiveImmutableAttribute(ConsistencyTests.SadImplementor3, , base class, ConsistencyTests.HappyImplementor) */ SadImplementor3 /**/
		: HappyImplementor, IVanilla, IVanilla2 { }

	public interface
		/* MissingTransitiveImmutableAttribute(ConsistencyTests.SadExtender, , interface, ConsistencyTests.ISomethingImmutable) */ SadExtender /**/
		: ISomethingImmutable { }

	// This won't emit an error because SadDeriver hasn't added [Immutable].
	// There is a diagnostic for that mistake, but once its fixed we would
	// get a diagnostic here. It would be nicer to developers to report all
	// the violations, probably, but this keeps the implementation very simple
	// and it's unlikely to come up in practice if you make small changes
	// between compiles.
	public sealed class IndirectlySadClass : SadDeriver { }

	public partial class
	/* MissingTransitiveImmutableAttribute(ConsistencyTests.PartialClass, , interface, ConsistencyTests.ISomethingImmutable) */ PartialClass /**/
		: ISomethingImmutable { }

	// This one doesn't get the diagnostic. We attach it to the one that specified
	// the interface.
	public partial class PartialClass { }

	// We will emit another diagnostic here though... this makes sense but the
	// code fix will try to apply multiple [Immutable] attributes which isn't
	// allowed...
	public partial class
	/* MissingTransitiveImmutableAttribute(ConsistencyTests.PartialClass, , base class, ConsistencyTests.HappyImplementor) */ PartialClass /**/
		: HappyImplementor { }

	// This shouldn't crash the analyzer
	public sealed class Foo : IThingThatDoesntExist { }

	[Immutable]
	public record UnsealedImmutableRecord { }

	public sealed record
		/* MissingTransitiveImmutableAttribute(ConsistencyTests.DerivedRecordMissingAttribute, , base class, ConsistencyTests.UnsealedImmutableRecord) */ DerivedRecordMissingAttribute /**/
		: UnsealedImmutableRecord { }

	[Immutable]
	public sealed record SealedDerivedWithAttribute : UnsealedImmutableRecord { }

	[Immutable]
	public record UnsealedDerivedWithAttribute : UnsealedImmutableRecord { }

	public record RegularRecord { }
	public sealed record RegularDerivedRecord : RegularRecord { }

	[Immutable]
	public record ConciseRecord : UnsealedImmutableRecord;

	[Immutable]
	public record BaseRecordWithArgs( int x ) { }

	[Immutable]
	public record ImmutableDerivedWithArgs( int y ) : BaseRecordWithArgs( y );

	public sealed record
		/* MissingTransitiveImmutableAttribute(ConsistencyTests.DerivedRecordNoAttrConstArg, , base class, ConsistencyTests.BaseRecordWithArgs) */ DerivedRecordNoAttrConstArg /**/
		: BaseRecordWithArgs( 0 );

	public sealed record
		/* MissingTransitiveImmutableAttribute(ConsistencyTests.DerivedRecordNoAttrWithArg, , base class, ConsistencyTests.BaseRecordWithArgs) */ DerivedRecordNoAttrWithArg /**/
		( int z ) : BaseRecordWithArgs( z );

	[ConditionallyImmutable]
	public interface ISomethingConditionallyImmutable<[ConditionallyImmutable.OnlyIf] T> { }

	[Immutable]
	public sealed class ImmutableClassImplementingConditionallyImmutable<[Immutable] T> : ISomethingConditionallyImmutable<T> { }

	[ConditionallyImmutable]
	public sealed class ConditionallyImmutableClassImplementingConditionallyImmutable<[ConditionallyImmutable.OnlyIf] T> : ISomethingConditionallyImmutable<T> { }

	public sealed class /* MissingTransitiveImmutableAttribute(ConsistencyTests.SadImplementerOfConditionallyImmutable,  (or [ConditionallyImmutable]), interface, ConsistencyTests.ISomethingConditionallyImmutable) */ SadImplementerOfConditionallyImmutable /**/<T> : ISomethingConditionallyImmutable<T> { }

	public partial class PartialClassNeedingImmutable { }
	public partial class /* MissingTransitiveImmutableAttribute(ConsistencyTests.PartialClassNeedingImmutable, , interface, ConsistencyTests.ISomethingImmutable) */ PartialClassNeedingImmutable /**/ : ISomethingImmutable { }
	public partial class PartialClassNeedingImmutable { }

	public class ClassWithMethod {
		public abstract void MethodWithImmutable<[Immutable] T>();
	}
	public interface IInterfaceWithMethodA {
		void MethodWithImmutable<T, [Immutable] U>();
	}
	public interface IInterfaceWithMethodB {
		void MethodWithImmutable<[Immutable] T, U>();
	}

	public class ClassImplicitlyImplementingMethodsA : IInterfaceWithMethodA {
		public void MethodWithImmutable<
			T,
			[Immutable] U
		>() { }
	}
	public class ClassImplicitlyImplementingMethodsB : IInterfaceWithMethodB {
		public void MethodWithImmutable<
			[Immutable] T,
			U
		>() { }
	}
	public class ClassImplicitlyImplementingMethodsInconsistent : IInterfaceWithMethodA, IInterfaceWithMethodB {
		public void MethodWithImmutable<
			/* InconsistentMethodAttributeApplication(Immutable, ClassImplicitlyImplementingMethodsInconsistent.MethodWithImmutable, IInterfaceWithMethodA.MethodWithImmutable) */ [Immutable] T /**/,
			/* InconsistentMethodAttributeApplication(Immutable, ClassImplicitlyImplementingMethodsInconsistent.MethodWithImmutable, IInterfaceWithMethodA.MethodWithImmutable) */ U /**/
		>() {}
	}

	public class ClassOverridingMethods : ClassWithMethod {
		public override void MethodWithImmutable<
			[Immutable] T
		>() { }
	}
	public class ClassOverridingMethodsInconsistent : ClassWithMethod {
		public override void MethodWithImmutable<
			/* InconsistentMethodAttributeApplication(Immutable, ClassOverridingMethodsInconsistent.MethodWithImmutable, ClassWithMethod.MethodWithImmutable) */ T /**/
		>() { }
	}

	public class ClassExplicitlyImplementingMethods : IInterfaceWithMethodA, IInterfaceWithMethodB {
		void IInterfaceWithMethodA.MethodWithImmutable<
			T,
			[Immutable] U
		>() { }
		void IInterfaceWithMethodB.MethodWithImmutable<
			[Immutable] T,
			U
		>()
	}
	public class ClassExplicitlyImplementingMethodsInconsistent : IInterfaceWithMethodA, IInterfaceWithMethodB {
		void IInterfaceWithMethodA.MethodWithImmutable<
			/* InconsistentMethodAttributeApplication(Immutable, ClassExplicitlyImplementingMethodsInconsistent.ConsistencyTests.IInterfaceWithMethodA.MethodWithImmutable, IInterfaceWithMethodA.MethodWithImmutable) */ [Immutable] T /**/,
			/* InconsistentMethodAttributeApplication(Immutable, ClassExplicitlyImplementingMethodsInconsistent.ConsistencyTests.IInterfaceWithMethodA.MethodWithImmutable, IInterfaceWithMethodA.MethodWithImmutable) */ U /**/
		>() { }
		void IInterfaceWithMethodB.MethodWithImmutable<
			/* InconsistentMethodAttributeApplication(Immutable, ClassExplicitlyImplementingMethodsInconsistent.ConsistencyTests.IInterfaceWithMethodB.MethodWithImmutable, IInterfaceWithMethodB.MethodWithImmutable) */ T /**/,
			/* InconsistentMethodAttributeApplication(Immutable, ClassExplicitlyImplementingMethodsInconsistent.ConsistencyTests.IInterfaceWithMethodB.MethodWithImmutable, IInterfaceWithMethodB.MethodWithImmutable) */ [Immutable] U /**/
		>()
	}

	[Immutable]
	public sealed class ClassWithCapturedMutability {
		public static readonly Func<int> AlwaysOk = static () => 3;
		public static readonly Func<int> AlwaysOk2 = () => AlwaysOk() + AlwaysOk();
		public static readonly Func<int> SometimesBad = () => AlwaysOk2();

		// won't error, but its cool because we'll error for SometimesBad
		public readonly Func<int> SometimesBad2 = () => SometimesBad()*3; 

		static ClassWithCapturedMutability() {
			int x = 0;

			AlwaysOk = static () => 3;

			// won't error, but its cool because we'll error for SometimesBad.
			AlwaysOk2 = static () => SometimesBad();

			SometimesBad = /* AnonymousFunctionsMayCaptureMutability */ () => x-- /**/;
        }

		ClassWithCapturedMutability() {
			int x = 0;

			SometimesBad2 = static () => 4;
			SometimesBad2 = static () => AlwaysOk();
			SometimesBad2 = /* AnonymousFunctionsMayCaptureMutability */ () => x++ /**/;
			var x = SometimesBad2 = /* AnonymousFunctionsMayCaptureMutability */ () => x++ /**/;
			SometimesBad2 = static () => 2;
        }
    }
}


<|start_filename|>tests/D2L.CodeStyle.TestAnalyzers.Tests/NUnit/CategoryAnalyzerTests.cs<|end_filename|>
﻿using D2L.CodeStyle.TestAnalyzers.Common;
using D2L.CodeStyle.TestAnalyzers.Test.Verifiers;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using NUnit.Framework;

namespace D2L.CodeStyle.TestAnalyzers.NUnit {
    [TestFixture]
    internal sealed class CategoryAnalyzerTests : DiagnosticVerifier {

        private const string PREAMBLE = @"
namespace NUnit.Framework {
	public abstract class NUnitAttribute : System.Attribute {}

	public class CategoryAttribute : NUnitAttribute { public CategoryAttribute( string name ) {} }
	public class TestFixtureAttribute : NUnitAttribute {
		public string Category { get; set; }
	}

	public class TestAttribute : NUnitAttribute {}
	public class TestCaseAttribute : NUnitAttribute {}
	public class TestCaseSourceAttribute : NUnitAttribute {}
}
";
        private static readonly int PREAMBLE_LINES = PREAMBLE.Split( '\n' ).Length;

        [Test]
        public void Categorized_MethodMatched_Fixture_Assmebly_NoDiagnostic() {
            const string test = PREAMBLE + @"
namespace TestNamespace {
	[NUnit.Framework.Category( ""fixture category"" )]
	class TestClass {
		[NUnit.Framework.Test]
		[NUnit.Framework.Category( ""Unit"" )]
		public void TestMethod( int x ) {}
	}
}";
            AssertNoDiagnostic(
                otherFile: @"
[assembly: NUnit.Framework.Category( ""assembly category"" )]
",
                file: test
            );
        }

        [Test]
        public void Categorized_FixtureMatched_Assmebly_NoDiagnostic() {
            const string test = PREAMBLE + @"
namespace TestNamespace {
	[NUnit.Framework.Category( ""Integration"" )]
	class TestClass {
		[NUnit.Framework.Test]
		public void TestMethod( int x ) {}
	}
}";
            AssertNoDiagnostic(
                otherFile: @"
[assembly: NUnit.Framework.Category( ""assembly category"" )]
",
                file: test
            );
        }

        [Test]
        public void Categorized_InheritedFixtureMatched_Assmebly_NoDiagnostic() {
            const string test = PREAMBLE + @"
namespace TestNamespace {
	[NUnit.Framework.Category( ""Integration"" )]
	class BaseClass : {}

	class TestClass : BaseClass {
		[NUnit.Framework.Test]
		public void TestMethod( int x ) {}
	}
}";
            AssertNoDiagnostic(
                otherFile: @"
[assembly: NUnit.Framework.Category( ""assembly category"" )]
",
                file: test
            );
        }

        [Test]
        public void Categorized_FixtureMatchedByFixture_Assmebly_NoDiagnostic() {
            const string test = PREAMBLE + @"
namespace TestNamespace {
	[NUnit.Framework.TestFixture( Category = ""UI"" )]
	class TestClass {
		[NUnit.Framework.Test]
		public void TestMethod( int x ) {}
	}
}";
            AssertNoDiagnostic(
                otherFile: @"
[assembly: NUnit.Framework.Category( ""assembly category"" )]
",
                file: test
            );
        }

        [Test]
        public void Categorized_FixtureMatchedByFixtureCsv_Assmebly_NoDiagnostic() {
            const string test = PREAMBLE + @"
namespace TestNamespace {
	[NUnit.Framework.TestFixture( Category = ""Cats,Unit"" )]
	class TestClass {
		[NUnit.Framework.Test]
		public void TestMethod( int x ) {}
	}
}";
            AssertNoDiagnostic(
                otherFile: @"
[assembly: NUnit.Framework.Category( ""assembly category"" )]
",
                file: test
            );
        }

        [Test]
        public void Categorized_InhertierFixtureMatchedByFixture_Assmebly_NoDiagnostic() {
            const string test = PREAMBLE + @"
namespace TestNamespace {
	[NUnit.Framework.TestFixture( Category = ""UI"" )]
	class BaseClass : {}

	class TestClass : BaseClass {
		[NUnit.Framework.Test]
		public void TestMethod( int x ) {}
	}
}";
            AssertNoDiagnostic(
                otherFile: @"
[assembly: NUnit.Framework.Category( ""assembly category"" )]
",
                file: test
            );
        }

        [Test]
        public void Categorized_AssmeblyMatched_NoDiagnostic() {
            const string test = PREAMBLE + @"
namespace TestNamespace {
	class TestClass {
		[NUnit.Framework.Test]
		public void TestMethod( int x ) {}
	}
}";
            AssertNoDiagnostic(
                otherFile: @"
[assembly: NUnit.Framework.Category( ""Unit"" )]
",
                file: test
            );
        }

        [Test]
        public void Categorized_Method_Fixture_Assmebly_NoMatch_Diagnostic() {
            const string test = PREAMBLE + @"
namespace TestNamespace {
	[NUnit.Framework.Category( ""fixture category"" )]
	class TestClass {
		[NUnit.Framework.Test]
		[NUnit.Framework.Category( ""method category"" )]
		public void TestMethod( int x ) {}
	}
}";
            AssertSingleDiagnostic(
                diag: Diagnostics.NUnitCategory,
                otherFile: @"
[assembly: NUnit.Framework.Category( ""assembly category"" )]
",
                file: test,
                line: PREAMBLE_LINES + 6,
                column: 15,
                BuildWrongCategoriesMessage( "assembly category", "fixture category", "method category" )
            );
        }

        [Test]
        public void ProhibitedAssemblyCategory_Diagnostic() {
            const string test = @"
[assembly: NUnit.Framework.Category( ""Isolated"" )]
";
            AssertSingleDiagnostic(
                diag: Diagnostics.NUnitCategory,
                otherFile: PREAMBLE,
                file: test,
                line: 2,
                column: 12,
                $"Assemblies cannot be categorized as any of [Isolated], but saw 'Isolated'."
            );
        }

        private static string BuildWrongCategoriesMessage(
            params string[] categories
        ) =>
            $"Test must be categorized as one of [Integration, Load, System, UI, Unit], but saw [{string.Join( ", ", categories )}]. See http://docs.dev.d2l/index.php/Test_Categories.";


        private void AssertNoDiagnostic( string file, string otherFile ) {
            VerifyCSharpDiagnostic( sources: new[] { file, otherFile } );
        }

        private void AssertSingleDiagnostic(
            DiagnosticDescriptor diag,
            string otherFile,
            string file,
            int line,
            int column,
            params object[] messageArgs
        ) {
            DiagnosticResult result = new DiagnosticResult {
                Id = diag.Id,
                Message = string.Format( diag.MessageFormat.ToString(), messageArgs ),
                Severity = DiagnosticSeverity.Error,
                Locations = new[] {
                    new DiagnosticResultLocation( "Test1.cs", line, column )
                }
            };

            VerifyCSharpDiagnostic(
                sources: new[] {
                    otherFile,
                    file,
                }, result
            );
        }

        protected override DiagnosticAnalyzer GetCSharpDiagnosticAnalyzer() {
            return new CategoryAnalyzer();
        }
    }
}


<|start_filename|>src/D2L.CodeStyle.Annotations/UndiffBucket.cs<|end_filename|>
﻿// ReSharper disable once CheckNamespace
namespace D2L.CodeStyle.Annotations {
	public enum UndiffBucket {
		/// <summary>
		/// This variable is required for all undiff clients
		/// </summary>
		Core,

		WebDAV,
		D2LWS,
		Banner,
		IPSIS,
		ServiceManager
	}
}


<|start_filename|>tests/D2L.CodeStyle.Analyzers.Test/Specs/ClassShouldBeSealedAnalyzer.cs<|end_filename|>
﻿// analyzer: D2L.CodeStyle.Analyzers.Language.ClassShouldBeSealedAnalyzer

namespace D2L.CodeStyle.Analyzers.Specs {
	public class PublicClass { } // no diagnostic because its public
	internal abstract class AbstractClass { } // no diagnostic because its abstract (but it is dead code)
	interface ISomeInterface { } // interfaces can't be sealed
	internal struct Struct { } // structs are implicitly sealed
	internal static class StaticClass { }

	internal sealed class SealedClass { } // already sealed

	internal class /* ClassShouldBeSealed */ InternalUnsealedClass /**/ {
		private class /* ClassShouldBeSealed */ PrivateUnsealedClass /**/ { }
	}

	// internal/private classes can be unsealed if they are actually used as a base somewhere
	internal class InternalBaseClass {
		private class PrivateBaseBaseClass { }

		private class PrivateBaseClass : PrivateBaseBaseClass { }

		private sealed class Derived : PrivateBaseClass { }
	}

	internal sealed class Derived : InternalBaseClass {}

	internal class AnotherInternalBaseBaseClass { } // this one is used (albeit not usefully) as a base class
	internal class /* ClassShouldBeSealed */ AnotherInternalBaseClass /**/ : AnotherInternalBaseBaseClass { }

	// Controversial opinion: there seems to be a significant amount of
	// inappopriate use of protected in our code base. A large reason is a form
	// of code-gen that no longer exists that would create derived types for
	// webpage classes. We'll be ruthless and call this an error too.
	internal class /* ClassShouldBeSealed */ ThingWithProtectedMembers /**/ {
		protected int x;

		protected ThingWithProtectedMembers() {}

		public virtual void Foo() {}
	}
}


<|start_filename|>src/D2L.CodeStyle.Analyzers/Language/AwaitedTasksAnalyzer.Codefix.cs<|end_filename|>
﻿using System.Collections.Immutable;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;

namespace D2L.CodeStyle.Analyzers.Language {
	partial class AwaitedTasksAnalyzer {

		[ExportCodeFixProvider(
			LanguageNames.CSharp,
			Name = nameof( ConfigureAwaitedTaskCodeFix )
		)]
		public sealed class ConfigureAwaitedTaskCodeFix : CodeFixProvider {

			public override ImmutableArray<string> FixableDiagnosticIds
				=> ImmutableArray.Create( Diagnostics.AwaitedTaskNotConfigured.Id );

			public override FixAllProvider GetFixAllProvider()
				=> WellKnownFixAllProviders.BatchFixer;

			public override async Task RegisterCodeFixesAsync(
				CodeFixContext ctx
			) {
				var root = await ctx.Document
					.GetSyntaxRootAsync( ctx.CancellationToken )
					.ConfigureAwait( false );

				bool useSafeAsync = await CanReferenceSafeAsync( ctx )
					.ConfigureAwait( false );

				foreach( var diagnostic in ctx.Diagnostics ) {
					var span = diagnostic.Location.SourceSpan;

					SyntaxNode syntax = root.FindNode( span, getInnermostNodeForTie: true );
					if( !( syntax is AwaitExpressionSyntax awaitExpression ) ) {
						continue;
					}

					ctx.RegisterCodeFix(
						CodeAction.Create(
							title: "Configure awaited task",
							ct => ConfigureAwaitedTask(
								orig: ctx.Document,
								root: root,
								awaitExpression: awaitExpression,
								useSafeAsync: useSafeAsync,
								cancellationToken: ct
							)
						),
						diagnostic
					);
				}
			}

			private static InvocationExpressionSyntax ConfigureTask( ExpressionSyntax task ) =>
				SyntaxFactory
					.InvocationExpression( SyntaxFactory.MemberAccessExpression(
						SyntaxKind.SimpleMemberAccessExpression,
						task,
						SyntaxFactory.IdentifierName( "ConfigureAwait" )
					) )
					.WithArgumentList( SyntaxFactory.ArgumentList( SyntaxFactory.SingletonSeparatedList(
						SyntaxFactory
							.Argument( SyntaxFactory.LiteralExpression( SyntaxKind.FalseLiteralExpression ) )
							.WithNameColon( SyntaxFactory.NameColon( "continueOnCapturedContext" ) )
					) ) );

			private static InvocationExpressionSyntax SafeAsyncify( ExpressionSyntax task ) =>
				SyntaxFactory
					.InvocationExpression( SyntaxFactory.MemberAccessExpression(
						SyntaxKind.SimpleMemberAccessExpression,
						task,
						SyntaxFactory.IdentifierName( "SafeAsync" )
					) );

			private static Task<Document> ConfigureAwaitedTask(
				Document orig,
				SyntaxNode root,
				AwaitExpressionSyntax awaitExpression,
				bool useSafeAsync,
				CancellationToken cancellationToken
			) {
				ExpressionSyntax rhs = awaitExpression.Expression;
				InvocationExpressionSyntax replacement = useSafeAsync
					? SafeAsyncify( rhs )
					: ConfigureTask( rhs );

				root = root.ReplaceNode( rhs, replacement );

				var newDoc = orig.WithSyntaxRoot( root );

				return Task.FromResult( newDoc );
			}

			private static async Task<bool> CanReferenceSafeAsync(
				CodeFixContext ctx
			) {
				Compilation compilation = await ctx
					.Document
					.Project
					.GetCompilationAsync( ctx.CancellationToken )
					.ConfigureAwait( false );

				foreach( AssemblyIdentity reference in compilation.ReferencedAssemblyNames ) {
					if( reference.Name == "D2L.Core" ) {
						return true;
					}
				}

				return false;
			}
		}
	}
}


<|start_filename|>tests/D2L.CodeStyle.Analyzers.Test/Specs/LoggingExecutionContextScopeBuilderAnalyzer.cs<|end_filename|>
﻿// analyzer: D2L.CodeStyle.Analyzers.ApiUsage.Logging.LoggingExecutionContextScopeBuilderAnalyzer

namespace D2L.LP.Logging.ExecutionContexts {
	using System.Threading.Tasks;

	public interface ILoggingExecutionContextScopeBuilder {
		ILoggingExecutionContextScopeBuilder With( string key, object value );

		void Run( Action action );
		TResult Run<TResult>( Func<TResult> action );

		Task RunAsync( Func<Task> action );
		Task<TResult> RunAsync<TResult>( Func<Task<TResult>> action );
	}
}

namespace D2L.CodeStyle.Analyzers.Specs {

	using System.Runtime.CompilerServices;
	using System.Threading.Tasks;
	using D2L.LP.Logging.ExecutionContexts;

	internal sealed class Foo {

		private readonly ILoggingExecutionContextScopeBuilder m_builder;

		public void ReportedCalls() {
			m_builder./* LoggingContextRunAwaitable(Use RunAsync for awaitable actions.) */ Run /**/( TaskAction );
			{ Task t = m_builder./* LoggingContextRunAwaitable(Use RunAsync for awaitable actions.) */ Run /**/( TaskAction ); }
			{ Task t = m_builder./* LoggingContextRunAwaitable(Use RunAsync for awaitable actions.) */ Run<Task> /**/( TaskAction ); }

			m_builder./* LoggingContextRunAwaitable(Use RunAsync for awaitable actions.) */ Run /**/( TaskTAction );
			{ Task<int> t = m_builder./* LoggingContextRunAwaitable(Use RunAsync for awaitable actions.) */ Run /**/( TaskTAction ); }
			{ Task<int> t =  m_builder./* LoggingContextRunAwaitable(Use RunAsync for awaitable actions.) */ Run<Task<int>> /**/( TaskTAction ) ; }

			m_builder./* LoggingContextRunAwaitable(Use RunAsync for awaitable actions.) */ Run /**/( async () => { } ) ;
			{ Task t =  m_builder./* LoggingContextRunAwaitable(Use RunAsync for awaitable actions.) */ Run /**/( async () => { } ) ; }
			{ Task t =  m_builder./* LoggingContextRunAwaitable(Use RunAsync for awaitable actions.) */ Run<Task> /**/( async () => { } ) ; }

			m_builder./* LoggingContextRunAwaitable(Use RunAsync for awaitable actions.) */ Run /**/( async () => 0 ) ;
			{ Task<int> t =  m_builder./* LoggingContextRunAwaitable(Use RunAsync for awaitable actions.) */ Run /**/( async () => 0 ) ; }
			{ Task<int> t =  m_builder./* LoggingContextRunAwaitable(Use RunAsync for awaitable actions.) */ Run<Task<int>> /**/( async () => 0 ) ; }

			m_builder./* LoggingContextRunAwaitable(Use RunAsync for awaitable actions.) */ Run /**/( ConfiguredTaskAction );
			{ ConfiguredTaskAwaitable t = m_builder./* LoggingContextRunAwaitable(Use RunAsync for awaitable actions.) */ Run /**/( ConfiguredTaskAction ); }
			{ ConfiguredTaskAwaitable t = m_builder./* LoggingContextRunAwaitable(Use RunAsync for awaitable actions.) */ Run<ConfiguredTaskAwaitable> /**/( ConfiguredTaskAction ); }

			m_builder./* LoggingContextRunAwaitable(Use RunAsync for awaitable actions.) */ Run /**/( ConfiguredTaskTAction );
			{ ConfiguredTaskAwaitable<int> t = m_builder./* LoggingContextRunAwaitable(Use RunAsync for awaitable actions.) */ Run /**/( ConfiguredTaskTAction ); }
			{ ConfiguredTaskAwaitable<int> t = m_builder./* LoggingContextRunAwaitable(Use RunAsync for awaitable actions.) */ Run<ConfiguredTaskAwaitable<int>> /**/( ConfiguredTaskTAction ); }

			m_builder./* LoggingContextRunAwaitable(Use RunAsync for awaitable actions.) */ Run /**/( () => TaskAction().ConfigureAwait( false ) );
			{ ConfiguredTaskAwaitable t = m_builder./* LoggingContextRunAwaitable(Use RunAsync for awaitable actions.) */ Run /**/( TaskAction().ConfigureAwait( false ) ); }
			{ ConfiguredTaskAwaitable t = m_builder./* LoggingContextRunAwaitable(Use RunAsync for awaitable actions.) */ Run<ConfiguredTaskAwaitable> /**/( TaskAction().ConfigureAwait( false ) ); }

			m_builder./* LoggingContextRunAwaitable(Use RunAsync for awaitable actions.) */ Run /**/( () => TaskTAction().ConfigureAwait( false ) );
			{ ConfiguredTaskAwaitable<int> t = m_builder./* LoggingContextRunAwaitable(Use RunAsync for awaitable actions.) */ Run /**/( () => TaskTAction().ConfigureAwait( false ) ); }
			{ ConfiguredTaskAwaitable<int> t = m_builder./* LoggingContextRunAwaitable(Use RunAsync for awaitable actions.) */ Run<ConfiguredTaskAwaitable<int>> /**/( () => TaskTAction().ConfigureAwait( false ) ); }
		}

		public void WouldBeNice() {
			T Helper<T>( Action<T> action ) {
				return m_builder.Run( action );
			};

			Helper( TaskAction );
			{ Task t = Helper( TaskAction ); }
			{ Task t = Helper<Task>( TaskAction ); }

			Helper( TaskTAction );
			{ Task<int> t = Helper( TaskTAction ); }
			{ Task<int> t = Helper<Task<int>>( TaskTAction ); }
		}

		public void OtherCalls() {
			m_builder.Run( VoidAction );

			m_builder.Run( IntAction );
			{ int i = m_builder.Run( IntAction ); }
			{ int i = m_builder.Run<int>( IntAction ); }

			m_builder.RunAsync( TaskAction );
			{ Task t = m_builder.RunAsync( TaskAction ); }

			m_builder.RunAsync( TaskTAction );
			{ Task<int> i = m_builder.RunAsync( TaskTAction ); }
			{ Task<int> i = m_builder.RunAsync<int>( TaskTAction ); }

			m_builder.RunAsync( async () => { } );
			{ Task t = m_builder.RunAsync( async () => { } ); }

			m_builder.RunAsync( async () => 0 );
			{ Task<int> i = m_builder.RunAsync( async () => 0 ); }
			{ Task<int> i = m_builder.RunAsync<int>( async () => 0 ); }
		}

		private Task TaskAction() { }
		private Task<int> TaskTAction() { }
		private ConfiguredTaskAwaitable ConfiguredTaskAction() { }
		private ConfiguredTaskAwaitable<int> ConfiguredTaskTAction() { }

		private void VoidAction() { }
		private int IntAction() { }

	}
}


<|start_filename|>src/D2L.CodeStyle.TestAnalyzers/Properties/AssemblyInfo.cs<|end_filename|>
﻿using System.Runtime.CompilerServices;

[assembly: InternalsVisibleTo( "D2L.CodeStyle.TestAnalyzers.Tests" )]


<|start_filename|>tests/D2L.CodeStyle.Analyzers.Test/Specs/ILpContentFilePhysicalPathAnalyzer.cs<|end_filename|>
﻿// analyzer: D2L.CodeStyle.Analyzers.ApiUsage.ContentPhysicalPaths.ILpContentFilePhysicalPathAnalyzer

namespace D2L.LP.Files.Domain {
	public interface ILpContentFile {
		string PhysicalPath { get; }
	}

	public interface ILpContentFileNew : ILpContentFile {
	}

	public interface IOtherFile {
		string PhysicalPath { get; }
	}
}

namespace D2L.CodeStyle.Analyzers.Test {

	internal sealed class ExplicitImplementor : D2L.LP.Files.Domain.ILpContentFile {
		public string PhysicalPath {
			get { return ""; }
		}
	}

	internal sealed class ExplicitImplementorNew : D2L.LP.Files.Domain.ILpContentFileNew {
		public string PhysicalPath {
			get { return ""; }
		}
	}

	internal sealed class ImplicitImplementor : D2L.LP.Files.Domain.ILpContentFile {
		string D2L.LP.Files.Domain.ILPContentFile.PhysicalPath {
			get { return ""; }
		}
	}

	internal sealed class ImplicitImplementorNew : D2L.LP.Files.Domain.ILpContentFileNew {
		string D2L.LP.Files.Domain.ILPContentFile.PhysicalPath {
			get { return ""; }
		}
	}

	internal sealed class OtherFile : D2L.LP.Files.Domain.IOtherFile {
		public string PhysicalPath {
			get { return ""; }
		}

		public static void DoSomething( int num, string str, bool flag ) {
		}
	}
}

namespace SpecTests {

	using D2L.LP.Files.Domain;
	using D2L.CodeStyle.Analyzers.Test;

	internal sealed class Usages {

		public string ExplicitImplementorInterfaceUsage() {
			ILpContentFile explicitImplementor = new ExplicitImplementor();
			return /* ContentPhysicalPathUsages */ explicitImplementor.PhysicalPath /**/;
		}

		public string ExplicitImplementorNewInterfaceUsage() {
			ILpContentFile explicitImplementor = new ExplicitImplementorNew();
			return /* ContentPhysicalPathUsages */ explicitImplementor.PhysicalPath /**/;
		}

		public string ExplicitImplementorImplementationUsage() {
			ExplicitImplementor explicitImplementor = new ExplicitImplementor();
			return /* ContentPhysicalPathUsages */ explicitImplementor.PhysicalPath /**/;
		}

		public string ExplicitImplementorNewImplementationUsage() {
			ExplicitImplementorNew explicitImplementor = new ExplicitImplementorNew();
			return /* ContentPhysicalPathUsages */ explicitImplementor.PhysicalPath /**/;
		}

		public string ImplicitImplementorInterfaceUsage() {
			ILpContentFile implicitImplementor = new ImplicitImplementor();
			return /* ContentPhysicalPathUsages */ implicitImplementor.PhysicalPath /**/;
		}

		public string ImplicitImplementorNewInterfaceUsage() {
			ILpContentFile implicitImplementor = new ImplicitImplementorNew();
			return /* ContentPhysicalPathUsages */ implicitImplementor.PhysicalPath /**/;
		}

		public string UsageInMethodCall() {
			ILpContentFile explicitImplementor = new ExplicitImplementor();
			OtherFile.DoSomething( 1, /* ContentPhysicalPathUsages */ explicitImplementor.PhysicalPath /**/, true );
		}

		public string UnrelatedUsage() {
			OtherFile instance = new OtherFile();
			return instance.PhysicalPath;
		}
	}
}


<|start_filename|>src/D2L.CodeStyle.Analyzers/ApiUsage/JsonParamBinderAttribute/JsonParamBinderAnalyzer.cs<|end_filename|>
﻿using System.Collections.Immutable;
using System.Linq;
using D2L.CodeStyle.Analyzers.Helpers;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;

namespace D2L.CodeStyle.Analyzers.ApiUsage.JsonParamBinderAttribute {

	[DiagnosticAnalyzer( LanguageNames.CSharp )]
	public class JsonParamBinderAnalyzer : DiagnosticAnalyzer {

		public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(
			Diagnostics.ObsoleteJsonParamBinder,
			Diagnostics.UnnecessaryAllowedListEntry
		);

		public override void Initialize( AnalysisContext context ) {

			context.EnableConcurrentExecution();
			context.ConfigureGeneratedCodeAnalysis( GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics );
			context.RegisterCompilationStartAction( RegisterAnalysis );
		}

		private void RegisterAnalysis( CompilationStartAnalysisContext context ) {

			var attributeType = context.Compilation.GetTypeByMetadataName( "D2L.LP.Web.Rest.Attributes.JsonParamBinder" );
			if( attributeType == null ) {
				// Attribute is presumably not being used, so no need to register our analyzer
				return;
			}

			AllowedTypeList allowedTypeList = AllowedTypeList.CreateFromAnalyzerOptions(
				allowedListFileName: "LegacyJsonParamBinderAllowedList.txt",
				analyzerOptions: context.Options
			);

			context.RegisterSyntaxNodeAction(
				ctx => AnalyzeAttribute( ctx, attributeType, allowedTypeList ),
				SyntaxKind.Attribute
			);

			context.RegisterSymbolAction(
				ctx => PreventUnnecessaryAllowedListing(
					ctx,
					attributeType,
					allowedTypeList
				),
				SymbolKind.NamedType
			);
		}

		private void AnalyzeAttribute(
			SyntaxNodeAnalysisContext context,
			INamedTypeSymbol jsonParamBinderT,
			AllowedTypeList allowedList
		) {
			if( !( context.Node is AttributeSyntax attribute ) ) {
				return;
			}

			if( !AttributeIsOfDisallowedType( context.SemanticModel, jsonParamBinderT, attribute ) ) {
				return;
			}

			ISymbol methodSymbol = context.ContainingSymbol;
			if( methodSymbol.Kind != SymbolKind.Method ) {
				return;
			}

			if( !( methodSymbol.ContainingType is INamedTypeSymbol classSymbol ) ) {
				return;
			}

			if( allowedList.Contains( classSymbol ) ) {
				return;
			}

			Location location = attribute.GetLocation();
			Diagnostic diagnostic = Diagnostic.Create(
				Diagnostics.ObsoleteJsonParamBinder,
				location
			);

			context.ReportDiagnostic( diagnostic );
		}

		private void PreventUnnecessaryAllowedListing(
			SymbolAnalysisContext context,
			INamedTypeSymbol jsonParamBinderT,
			AllowedTypeList allowedTypeList
		) {
			if( !( context.Symbol is INamedTypeSymbol namedType ) ) {
				return;
			}

			if( !allowedTypeList.Contains( namedType ) ) {
				return;
			}

			Location diagnosticLocation = null;
			foreach( var syntaxRef in namedType.DeclaringSyntaxReferences ) {
				var typeSyntax = syntaxRef.GetSyntax( context.CancellationToken ) as TypeDeclarationSyntax;

				diagnosticLocation = diagnosticLocation ?? typeSyntax.Identifier.GetLocation();

				SemanticModel model = context.Compilation.GetSemanticModel( typeSyntax.SyntaxTree );

				bool usesDisallowedTypes = typeSyntax
					.DescendantNodes()
					.OfType<AttributeSyntax>()
					.Any( syntax => AttributeIsOfDisallowedType( model, jsonParamBinderT, syntax ) );

				if( usesDisallowedTypes ) {
					return;
				}
			}

			if( diagnosticLocation != null ) {
				allowedTypeList.ReportEntryAsUnnecesary(
					entry: namedType,
					location: diagnosticLocation,
					report: context.ReportDiagnostic
				);
			}
		}

		private static bool AttributeIsOfDisallowedType(
			SemanticModel model,
			INamedTypeSymbol jsonParamBinderT,
			AttributeSyntax syntax
		) {
			if( !( model.GetTypeInfo( syntax ).Type is INamedTypeSymbol actualType ) ) {
				return false;
			}

			if( !actualType.Equals( jsonParamBinderT, SymbolEqualityComparer.Default ) ) {
				return false;
			}

			return true;
		}

	}

}


<|start_filename|>src/D2L.CodeStyle.Analyzers/ApiUsage/DependencyInjection/Domain/RegisterParentAwareFactoryExpression.cs<|end_filename|>
﻿using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;

namespace D2L.CodeStyle.Analyzers.ApiUsage.DependencyInjection.Domain {
	// void RegisterParentAwareFactory<TDependencyType, TFactoryType>();
	internal sealed class RegisterParentAwareFactoryExpression : DependencyRegistrationExpression {

		internal override bool CanHandleMethod( IMethodSymbol method ) {
			return method.Name == "RegisterParentAwareFactory"
				&& method.TypeArguments.Length == 2
				&& method.Parameters.Length == 0;
		}

		internal override DependencyRegistration GetRegistration(
			IMethodSymbol method,
			SeparatedSyntaxList<ArgumentSyntax> arguments,
			SemanticModel semanticModel
		) {
			ITypeSymbol concreteType = GetConstructedTypeOfIFactory(
				semanticModel,
				method.TypeArguments[0],
				method.TypeArguments[1]
			);

			return DependencyRegistration.Factory( 
				ObjectScope.AlwaysCreateNewInstance, 
				method.TypeArguments[0], 
				method.TypeArguments[1],
				concreteType
			);
		}

	}

}


<|start_filename|>src/D2L.CodeStyle.Analyzers/ApiUsage/ContentPhysicalPaths/PhysicalPathPropertyAnalysis.cs<|end_filename|>
﻿using System.Collections.Immutable;
using System.Linq;
using D2L.CodeStyle.Analyzers.Extensions;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Diagnostics;

namespace D2L.CodeStyle.Analyzers.ApiUsage.ContentPhysicalPaths {

	internal sealed class PhysicalPathPropertyAnalysis {

		internal static readonly DiagnosticDescriptor DiagnosticDescriptor = Diagnostics.ContentPhysicalPathUsages;

		private readonly string m_dangerousTypeName;
		private readonly string m_dangerousPropertyName;
		private readonly IImmutableSet<string> m_allowedTypes;

		public PhysicalPathPropertyAnalysis(
				string dangerousTypeName,
				string dangerousPropertyName,
				IImmutableSet<string> allowedTypes
			) {

			m_dangerousTypeName = dangerousTypeName;
			m_dangerousPropertyName = dangerousPropertyName;
			m_allowedTypes = allowedTypes;
		}

		public void Initialize( AnalysisContext context ) {

			context.EnableConcurrentExecution();
			context.ConfigureGeneratedCodeAnalysis( GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics );
			context.RegisterCompilationStartAction( Register );
		}

		private void Register( CompilationStartAnalysisContext context ) {

			IImmutableSet<ISymbol> dangerousProperties = GetDangerousPropertySymbols( context.Compilation );

			if( dangerousProperties.Count == 0 ) {
				return;
			}

			context.RegisterSyntaxNodeAction(
				ctxt => AnalyzePropertyAccess( ctxt, dangerousProperties ),
				SyntaxKind.SimpleMemberAccessExpression
			);
		}

		private void AnalyzePropertyAccess(
				SyntaxNodeAnalysisContext context,
				IImmutableSet<ISymbol> dangerousProperties
			) {

			ISymbol propertySymbol = context.SemanticModel
				.GetSymbolInfo( context.Node )
				.Symbol;

			if( propertySymbol.IsNullOrErrorType() ) {
				return;
			}

			if( propertySymbol.Kind != SymbolKind.Property ) {
				return;
			}

			bool isDangerousProperty = IsDangerousMemberSymbol( propertySymbol, dangerousProperties );

			if( !isDangerousProperty ) {
				return;
			}

			bool isAllowedType = m_allowedTypes.Contains( context.ContainingSymbol.ContainingType.ToString() );
			if( isAllowedType ) {
				return;
			}

			ReportDiagnostic( context, propertySymbol, DiagnosticDescriptor );
		}

		private bool IsDangerousMemberSymbol(
				ISymbol memberSymbol,
				IImmutableSet<ISymbol> dangerousMembers
			) {
		
			ISymbol originalDefinition = memberSymbol.OriginalDefinition;
		
			if( dangerousMembers.Contains( originalDefinition ) ) {
				return true;
			}
		
			if( !ReferenceEquals( memberSymbol, originalDefinition ) ) {
		
				if( dangerousMembers.Contains( memberSymbol ) ) {
					return true;
				}
			}

			// handles explicit implementations, accessed via concrete type;
			// ( i.e. determines whether the member symbol under analysis implements a dangerous interface member )
			foreach( ISymbol dm in dangerousMembers ) {

				ISymbol implementation = memberSymbol.ContainingType.FindImplementationForInterfaceMember( dm );
				if( implementation == null ) {
					// does not implement the interface containing the dangerous property
					continue;
				}

				if( implementation.Equals( memberSymbol, SymbolEqualityComparer.Default ) ) {
					return true;
				}
			}

			return false;
		}

		private void ReportDiagnostic(
				SyntaxNodeAnalysisContext context,
				ISymbol memberSymbol,
				DiagnosticDescriptor diagnosticDescriptor
			) {

			Location location = context.Node.GetLocation();
			string memberName = memberSymbol.ToDisplayString( MemberDisplayFormat );
			Diagnostic diagnostic = Diagnostic.Create(
					diagnosticDescriptor,
					location,
					memberName
				);

			context.ReportDiagnostic( diagnostic );
		}

		private static readonly SymbolDisplayFormat MemberDisplayFormat = new SymbolDisplayFormat(
				memberOptions: SymbolDisplayMemberOptions.IncludeContainingType,
				localOptions: SymbolDisplayLocalOptions.IncludeType,
				typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces
			);

		private IImmutableSet<ISymbol> GetDangerousPropertySymbols( Compilation compilation ) {

			INamedTypeSymbol type = compilation.GetTypeByMetadataName( m_dangerousTypeName );
			if( type == null ) {
				return ImmutableHashSet<ISymbol>.Empty;
			}

			IImmutableSet<ISymbol> properties = type
				.GetMembers( m_dangerousPropertyName )
				.Where( m => m.Kind == SymbolKind.Property )
				.ToImmutableHashSet();

			return properties;
		}
	}
}


<|start_filename|>src/D2L.CodeStyle.Analyzers/ApiUsage/Events/EventHandlersDisallowedList.cs<|end_filename|>
﻿using System.Collections.Generic;
using System.Collections.Immutable;

namespace D2L.CodeStyle.Analyzers.ApiUsage.Events {

	internal static class EventHandlersDisallowedList {

		private static readonly ImmutableArray<ImmutableArray<string>> DisallowedEventHandlerTypes = ImmutableArray.Create(

				// External only
				ImmutableArray.Create( "D2L.LP.Distributed.Events.ExternalPublish.UserInteraction.UserInteractionEvent" ),

				// Broadcast events
				ImmutableArray.Create( "D2L.Core.JobManagement.Events.JobAbortRequested" ),
				ImmutableArray.Create( "D2L.LP.Distributed.WorkQueues.Events.WorkQueueEvent" ),
				ImmutableArray.Create( "D2L.LP.Tasks.Events.CancelTaskEvent" ),
				ImmutableArray.Create( "D2L.LP.Tools.DataPurgeArchive.PurgeTasks.Events.AbortPurgeTaskEvent" ),
				ImmutableArray.Create( "D2L.LP.Tools.DataPurgeArchive.PurgeTasks.Events.PausePurgeTaskEvent" ),
				ImmutableArray.Create( "D2L.LP.Tools.DataPurgeArchive.PurgeTasks.Scheduling.Events.OrgScheduledEvent" ),
				ImmutableArray.Create( "D2L.LP.Tools.DataPurgeArchive.PurgeTasks.Scheduling.Events.OrgUnscheduledEvent" ),
				ImmutableArray.Create( "D2L.AP.S3.Analysis.Service.PredictiveModelingAbortEvent" )
			);

		public static readonly IReadOnlyDictionary<string, ImmutableArray<ImmutableArray<string>>> DisallowedTypes = ImmutableDictionary
			.Create<string, ImmutableArray<ImmutableArray<string>>>()
			.Add(
				"D2L.LP.Distributed.Events.Handlers.IEventHandler`1", 
				DisallowedEventHandlerTypes
			)
			.Add(
				"D2L.LP.Distributed.Events.Handlers.IOrgEventHandler`1", 
				DisallowedEventHandlerTypes
			);
	}
}


<|start_filename|>tests/D2L.CodeStyle.Analyzers.Test/Specs/EventHandlerLoaderTypesAnalyzer.cs<|end_filename|>
﻿
// analyzer: D2L.CodeStyle.Analyzers.ApiUsage.Events.EventHandlerLoaderTypesAnalyzer

namespace D2L.LP.Distributed.Events.Domain {

	using System;

	[AttributeUsage( validOn: AttributeTargets.Class, AllowMultiple = false, Inherited = false )]
	public sealed class EventAttribute : Attribute { }
}

namespace D2L.LP.Distributed.Events.Handlers {

	using System;

	[AttributeUsage( validOn: AttributeTargets.Class, AllowMultiple = false, Inherited = false )]
	public sealed class EventHandlerAttribute : Attribute {
		public EventHandlerAttribute( string id ) { }
	}

	public interface IEventHandlerRegistry {
		void RegisterEventHandler<TEvent, THandler>() where THandler : IEventHandler<TEvent>;
		void RegisterOrgEventHandler<TEvent, THandler>() where THandler : IOrgEventHandler<TEvent>;
	}

	public interface IEventHandler<T> { }
	public interface IOrgEventHandler<T> { }
}

namespace Tests {

	using System;
	using D2L.LP.Distributed.Events.Domain;
	using D2L.LP.Distributed.Events.Handlers;

	[Event]
	public sealed class JumpEvent { }

	[EventHandler( "AAAAAAAA-AAAA-AAAA-AAAA-AAAAAAAAAAAA" )]
	public sealed class AttributedJumpEventHandler : IEventHandler<JumpEvent> { }

	[EventHandler( "BBBBBBBB-BBBB-BBBB-BBBB-BBBBBBBBBBBB" )]
	public sealed class AttributedJumpOrgEventHandler : IOrgEventHandler<JumpEvent> { }

	public sealed class NonAttributedJumpEventHandler : IEventHandler<JumpEvent> { }
	public sealed class NonAttributedJumpOrgEventHandler : IOrgEventHandler<JumpEvent> { }

	public sealed class SkipEvent { }

	[EventHandler( "CCCCCCCC-CCCC-CCCC-CCCC-CCCCCCCCCCCC" )]
	public sealed class AttributedSkipEventHandler : IEventHandler<SkipEvent> { }

	[EventHandler( "DDDDDDDD-DDDD-DDDD-DDDD-DDDDDDDDDDDD" )]
	public sealed class AttributedSkipOrgEventHandler : IOrgEventHandler<SkipEvent> { }

	public sealed class WithEventAttribute {

		public void Load( IEventHandlerRegistry registry ) {

			registry.RegisterEventHandler<JumpEvent, AttributedJumpEventHandler>();
			registry.RegisterOrgEventHandler<JumpEvent, AttributedJumpOrgEventHandler>();
		}
	}

	public sealed class WithoutEventAttribute {

		public void Load( IEventHandlerRegistry registry ) {

			/* EventTypeMissingEventAttribute(Tests.SkipEvent) */	registry.RegisterEventHandler<SkipEvent, AttributedSkipEventHandler>() /**/;
			/* EventTypeMissingEventAttribute(Tests.SkipEvent) */	registry.RegisterOrgEventHandler<SkipEvent, AttributedSkipOrgEventHandler>() /**/;
		}
	}
	
	public sealed class WithoutEventHandlerAttribute {

		public void Load( IEventHandlerRegistry registry ) {

			/* EventHandlerTypeMissingEventAttribute(Tests.NonAttributedJumpEventHandler) */	registry.RegisterEventHandler<JumpEvent, NonAttributedJumpEventHandler>() /**/;
			/* EventHandlerTypeMissingEventAttribute(Tests.NonAttributedJumpOrgEventHandler) */	registry.RegisterOrgEventHandler<JumpEvent, NonAttributedJumpOrgEventHandler>() /**/;
		}
	}
}


<|start_filename|>src/D2L.CodeStyle.Annotations/DangerousMethodUsage/UnauditedAttribute.cs<|end_filename|>
﻿using System;

namespace D2L.CodeStyle.Annotations {

	public static partial class DangerousMethodUsage {

		/// <summary>
		/// Indicates usages of a dangerous method are still unaudited
		/// </summary>
		[AttributeUsage(
			validOn: AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property,
			AllowMultiple = true,
			Inherited = false
		)]
		public sealed class UnauditedAttribute : Attribute {

			/// <summary>
			/// Indicates usages of a dangerous method are still unaudited
			/// </summary>
			/// <param name="declaringType">The type that declares the dangerous method.</param>
			/// <param name="methodName">The name of the dangerous method.</param>
			public UnauditedAttribute(
					Type declaringType,
					string methodName
				) {

				DeclaringType = declaringType;
				MethodName = methodName;
			}

			public Type DeclaringType { get; }
			public string MethodName { get; }
		}
	}
}


<|start_filename|>tests/D2L.CodeStyle.Analyzers.Test/Specs/RpcAnalyzer.cs<|end_filename|>
﻿// analyzer: D2L.CodeStyle.Analyzers.ApiUsage.RpcAnalyzer

using System;
using D2L.LP.Extensibility.Activation.Domain;
using D2L.Web;
using D2L.Web.RequestContext;

namespace D2L.Web {
	public interface IRpcContext { }
	public interface IRpcPostContext { }
	public class RpcAttribute : Attribute { }
}

namespace D2L.Web.RequestContext {
	public interface IRpcPostContextBase { }
}

namespace D2L.LP.Extensibility.Activation.Domain {
	public class DependencyAttribute : Attribute { }
}

namespace D2L.CodeStyle.Analyzers.RpcDependencies.Examples {
	public sealed class FooDependency { }
	public sealed class BarDependency { }

	public sealed class OkayRpcHandler {
		public void NonRpcMethod( int x ) { }

		public static void NonRpcStaticMethod() { }

		[UndefinedAttribute]
		public static void WeShouldntEmitDiagnosticsWhenAnAttributeDoesntExist() { }

		[Rpc]
		public static void BasicIRpcContext( IRpcContext context ) { }

		[Rpc]
		public static void BasicIRpcPostContext( IRpcPostContext context ) { }

		[Rpc]
		public static void BasicIRpcPostContextBase( IRpcPostContextBase context ) { }

		[Rpc]
		public static void RpcWithSingleParameter( IRpcContext context, int x ) { }

		[Rpc]
		public static void RpcWithMultipleParameters( IRpcContext context, int x, int y ) { }
		
		[Rpc]
		public static void RpcWithDependencyParameter(
			IRpcContext context,
			[Dependency] FooDependency x
		) { }

		[Rpc]
		public static void RpcWithMultipleDependencyParameters(
			IRpcContext context,
			[Dependency] FooDependency x,
			[Dependency] BarDependency y
		) { }

		[Rpc]
		public static void GeneralRpc(
			IRpcContext context,
			[Dependency] FooDependency x,
			[Dependency] BarDependency y,
			int a,
			string b
		) { }

		[Rpc]
		public static void GeneralRpcWithWeirdParameterAttribute(
			IRpcContext context,
			[Dependency] FooDependency x,
			[Dependency] BarDependency y,
			int a,
			[UndefinedAttribute] string b
		) { }
	}

	public sealed class BadRpcs {
		// The first argument must be wither IRpcContext, IRpcPostContext or IRpcPostContextBase

		[Rpc]
		public static void MissingFirstArgument /* RpcContextFirstArgument */ () /**/ { }

		[Rpc]
		public static void BadFirstArgument( /* RpcContextFirstArgument */ int x /**/ ) { }

		[Rpc]
		[UndefinedAttribute]
		public static void BadFirstArgumentWithUnrelatedUndefinedMethodAttribute( /* RpcContextFirstArgument */ int x /**/ ) { }

		[Rpc]
		public static void BadFirstArgument2(
			/* RpcContextFirstArgument */ int x /**/,
			IRpcContext context
		) { }

		// [Dependency] arguments must come after the first argument but before
		// any of the arguments that aren't [Dependency] (those come from the
		// user-agent.)

		[Rpc]
		public static void IncorrectDependencySortOrder(
			IRpcContext context,
			int x,
			/* RpcArgumentSortOrder */ [Dependency] FooDependency foo /**/
		) { }

		[Rpc]
		public static void IncorrectDependencySortOrderWithMultipleThings(
			IRpcContext context,
			int x,
			int y,
			/* RpcArgumentSortOrder */ [Dependency] FooDependency foo /**/,
			/* RpcArgumentSortOrder */ [Dependency] BarDependency bar /**/
		) { }

		[Rpc]
		public static void IncorrectDependencySortOrderEvenWithRandomBadAttributeInTheMiddle(
			IRpcContext context,
			[UndefinedAttribute] int x,
			/* RpcArgumentSortOrder */ [Dependency] FooDependency foo /**/
		) { }

		[Rpc]
		public static void RpcContextMarkedDependency(
			/* RpcContextMarkedDependency */ [Dependency] IRpcContext context /**/
		) { }

		[Rpc]
		public static void RpcPostContextMarkedDependency(
			/* RpcContextMarkedDependency */ [Dependency] IRpcPostContext context /**/
		) { }

		[Rpc]
		public static void RpcPostContextBaseMarkedDependency(
			/* RpcContextMarkedDependency */ [Dependency] IRpcPostContextBase context /**/
		) { }
	}
}


<|start_filename|>src/D2L.CodeStyle.Analyzers/Language/DefaultValueConsistencyAnalyzer.cs<|end_filename|>
﻿using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using System;
using System.Collections.Immutable;
using System.Linq;

namespace D2L.CodeStyle.Analyzers.Language {
	[DiagnosticAnalyzer( LanguageNames.CSharp )]
	public sealed class DefaultValueConsistencyAnalyzer : DiagnosticAnalyzer {
		public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
			=> ImmutableArray.Create(
				Diagnostics.IncludeDefaultValueInOverrideForReadability,
				Diagnostics.DontIntroduceNewDefaultValuesInOverrides,
				Diagnostics.DefaultValuesInOverridesShouldBeConsistent
			);

		public override void Initialize( AnalysisContext context ) {
			context.EnableConcurrentExecution();
			context.ConfigureGeneratedCodeAnalysis( GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics );

			// Check for methods with the "override" modifier
			context.RegisterSyntaxNodeAction(
				AnalyzeOverrideMethodDeclaration,
				SyntaxKind.MethodDeclaration
			);

			// We also need to check interface method impls, and unfortunately
			// this is the best way I know how to do it:
			context.RegisterSyntaxNodeAction(
				AnalyzeBaseList,
				SyntaxKind.BaseList
			);
		}

		private static void AnalyzeOverrideMethodDeclaration(
			SyntaxNodeAnalysisContext ctx
		) {
			var methodDecl = (MethodDeclarationSyntax)ctx.Node;

			var hostDecl = methodDecl.Parent as TypeDeclarationSyntax;

			// The parser may create MethodDeclaration nodes in weird places
			// (e.g. as a member of a NamespaceDeclaration) just to continue
			// on parsing and generating diagnostics. The only sensible places
			// are TypeDeclarationSyntaxs (class, struct or interface decls)
			// so just bail out if we're in a weird situation.
			if ( hostDecl == null ) {
				return;
			}

			// As a performance optimization we can bail out early if there
			// are no overrides we care about (e.g. our base class is either
			// System.Object or System.ValueType and we don't implement any
			// interfaces.) This saves querying the semantic model.
			if ( hostDecl.BaseList == null ) {
				return;
			}

			var method = ctx.SemanticModel
				.GetDeclaredSymbol( methodDecl );

			if ( method == null || method.Kind == SymbolKind.ErrorType ) {
				return;
			}

			if( !method.IsOverride ) {
				return;
			}

			// There might be other build errors (e.g. from missing partial
			// classes from code-gen, or incomplete code when we're running
			// in intellisense) preventing the semantic model from locating
			// the original method. Ignore these.
			if( method.OverriddenMethod == null ) {
				return;
			}

			AnalyzeMethod(
				ctx.ReportDiagnostic,
				baseMethod: method.OverriddenMethod,
				implMethod: method
			);
		}

		private static void AnalyzeBaseList( SyntaxNodeAnalysisContext ctx ) {
			var baseList = (BaseListSyntax)ctx.Node;

			// ignore enums, their BaseListSyntax isn't relevant
			if ( baseList.Parent is EnumDeclarationSyntax ) {
				return;
			}

			// Not likely, maybe as the user is typing stuff out.
			if ( baseList.Types.Count == 0 ) {
				return;
			}

			var implType = ctx.SemanticModel
				.GetDeclaredSymbol( (TypeDeclarationSyntax)baseList.Parent );

			// The most expensive thing we do:
			var interfaceMethodsAndImpls = implType
				.AllInterfaces // interfaces can extend interfaces, etc.
				.SelectMany( iface => iface.GetMembers() )
				.OfType<IMethodSymbol>()
				.Select( ifaceMethod =>
					(ifaceMethod, implType.FindImplementationForInterfaceMember( ifaceMethod ) as IMethodSymbol )
				);

			foreach( (var ifaceMethod, var implMethod) in interfaceMethodsAndImpls ) {
				if ( implMethod == null ) {
					// Maybe they implemented a method with a non-method (which
					// would be a different build error) or they haven't
					// implemented it yet.
					continue;
				}

				if ( !implMethod.ContainingType.Equals( implType, SymbolEqualityComparer.Default ) ) {
					// Our base class could implement the method. We don't want
					// to duplicate the work on principle but also when we look
					// at DeclaringSyntaxReferences in GetLocation it could
					// fail because our base could be in a different assembly.
					return;
				}

				// This is O(# of explicit implementations for this method), in
				// a loop which includes at least that many things, so O(n^2)
				// for some n. But n is probably small.
				if ( implMethod.ExplicitInterfaceImplementations.Contains( ifaceMethod ) ) {
					// Explicit implementations can't change default value
					// behaviour, so we don't need to worry about them.
					continue;
				}

				AnalyzeMethod(
					ctx.ReportDiagnostic,
					baseMethod: ifaceMethod,
					implMethod: implMethod
				);
			}
		}

		private static void AnalyzeMethod(
			Action<Diagnostic> reportDiagnostic,
			IMethodSymbol baseMethod,
			IMethodSymbol implMethod
		) {
			foreach( var implParameter in implMethod.Parameters ) {
				// The order of parameters in both methods will line up
				var baseParameter = baseMethod
					.Parameters.First(
						p => p.Ordinal == implParameter.Ordinal
					);

				var hasDefault = implParameter.HasExplicitDefaultValue;
				var shouldHaveDefault = baseParameter.HasExplicitDefaultValue;

				// the best case: no default values
				if( !hasDefault && !shouldHaveDefault ) {
					continue;
				}

				if( !hasDefault && shouldHaveDefault ) {
					// It makes the implementation more readable if it
					// duplicates the default value from the original
					// definition. Additionally it removes inconsistency
					// in using a reference to the impl vs. base.
					reportDiagnostic(
						Diagnostic.Create(
							Diagnostics.IncludeDefaultValueInOverrideForReadability,
							GetLocation( implParameter ),
							implParameter.Name,
							baseMethod.ContainingType.Name
						)
					);

					continue;
				}

				if( hasDefault && !shouldHaveDefault ) {
					// Giving default values only for the impl leads to
					// inconsistent behaviour that we don't like.
					reportDiagnostic(
						Diagnostic.Create(
							Diagnostics.DontIntroduceNewDefaultValuesInOverrides,
							GetLocation( implParameter ),
							implParameter.Name,
							baseMethod.ContainingType.Name
						)
					);

					continue;
				}

				// therefore, hasDefault && shouldHaveDefault. Make sure that
				// they used the same default value.

				var implDefault = implParameter.ExplicitDefaultValue;
				var baseDefault = baseParameter.ExplicitDefaultValue;

				// Use the static object.Equals because implDefault could
				// legtimately be null and implDefault.Equals( baseDefault )
				// would throw a NRE.
				if( !Equals( implDefault, baseDefault ) ) {
					// Inconsistent default values are VERY confusing. It
					// almost surely isn't intentional; it usually happens
					// because someone wants to change the default but doesn't
					// update everything.

					reportDiagnostic(
						Diagnostic.Create(
							Diagnostics.DefaultValuesInOverridesShouldBeConsistent,
							GetLocation( implParameter ),
							implParameter.Name,
							FormatDefaultValue( implDefault ),
							FormatDefaultValue( baseDefault ),
							baseMethod.ContainingType.Name
						)
					);
				}
			}
		}

		private static string FormatDefaultValue( object val ) {
			if ( val == null ) {
				return "null";
			}

			if ( val is string s ) {
				return $@"""{s}""";
			}

			return val.ToString();
		}

		private static Location GetLocation( IParameterSymbol param ) {
			return param
				.DeclaringSyntaxReferences[0]
				.GetSyntax()
				.GetLocation();
		}
	}
}


<|start_filename|>src/D2L.CodeStyle.Annotations/Contract/StatelessFuncAttribute.cs<|end_filename|>
﻿using System;

namespace D2L.CodeStyle.Annotations.Contract {
	[AttributeUsage( AttributeTargets.Parameter, AllowMultiple = false )]
	public sealed class StatelessFuncAttribute : ReadOnlyAttribute {}
}


<|start_filename|>src/D2L.CodeStyle.Analyzers/ApiUsage/System.Collections.Immutable/ImmutableCollectionsAnalyzer.cs<|end_filename|>
﻿using System.Collections.Immutable;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;

namespace D2L.CodeStyle.Analyzers.ApiUsage.SystemCollectionsImmutable {
	[DiagnosticAnalyzer( LanguageNames.CSharp )]
	public sealed class ImmutableCollectionsAnalyzer : DiagnosticAnalyzer {
		public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; }
			= ImmutableArray.Create(
				Diagnostics.DontUseImmutableArrayConstructor
			);

		public override void Initialize( AnalysisContext context ) {
			context.EnableConcurrentExecution();
			context.ConfigureGeneratedCodeAnalysis( GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics );
			context.RegisterCompilationStartAction( RegisterAnalysis );

		}
		public static void RegisterAnalysis(
			CompilationStartAnalysisContext context
		) {
			var immutableArrayType = context.Compilation
				.GetTypeByMetadataName( "System.Collections.Immutable.ImmutableArray`1" );

			// Bail early if we don't have a reference to ImmutableArray
			if( immutableArrayType == null ) {
				return;
			}

			context.RegisterSyntaxNodeAction(
				ctx => AnalyzeNewImmutableArray( ctx, immutableArrayType ),
				SyntaxKind.ObjectCreationExpression
			);
		}

		public static void AnalyzeNewImmutableArray(
			SyntaxNodeAnalysisContext context,
			INamedTypeSymbol immutableArrayType
		) {
			var node = context.Node as ObjectCreationExpressionSyntax;

			// We're only concerned with the default (no arg) constructor for ImmutableArray
			if( node.ArgumentList != null && node.ArgumentList.Arguments.Count != 0 ) {
				return;
			}

			var specificType = context.SemanticModel.GetTypeInfo( context.Node ).Type
				as INamedTypeSymbol;

			// This happens for generic types
			if ( specificType == null ) {
				return;
			}

			// We only care about ImmutableArray`1
			if( !specificType.OriginalDefinition.Equals( immutableArrayType, SymbolEqualityComparer.Default ) ) {
				return;
			}

			// All usages are bad
			context.ReportDiagnostic(
				Diagnostic.Create(
					Diagnostics.DontUseImmutableArrayConstructor,
					node.GetLocation()
				)
			);
		}
	}
}


<|start_filename|>tests/D2L.CodeStyle.Analyzers.Test/Specs/DefaultValueConsistencyAnalyzer.cs<|end_filename|>
﻿// analyzer: D2L.CodeStyle.Analyzers.Language.DefaultValueConsistencyAnalyzer

namespace ThingThatShouldntCrash {
	public void FunctionOutOfPlace( int arg = 3 );

	// This has a BaseListSyntax but should be ignored
	enum Foo : int {
		A, B, C
	}

	public class EmptyBase { }

	public class ThingWithMysteriousOverride : EmptyBase {
		public override void Bar( int abc ) { }
	}
}

namespace ClassInheritance {

	public class ClassWithNoInterestingOverrides {
		public int Foo();
		public void Bar( int x = 3 );
		public override string ToString() { }
		public override int GetHashCode() { }
		// We don't worry about Equals
		public override bool Equals( object obj = null ) { }
	}

	public abstract class BaseClass {
		public abstract void Foo();
		public abstract void Bar( int x = 3 );
		public abstract void Baz( float x, string y = "hello" );
	}

	public sealed class GoodImpl : BaseClass {
		public override void Foo() { }
		public override void Bar( int x = 3 ) { }
		public override void Baz( float x, string y = "hello" ) { }
	}

	public sealed class InconsistentImpl : BaseClass {
		public override void Foo() { }

		public override void Bar(
			/* DefaultValuesInOverridesShouldBeConsistent(x, 123123, 3, BaseClass) */ int x = 123123 /**/
		) { }

		public override void Baz(
			float x,
			/* DefaultValuesInOverridesShouldBeConsistent(y, null, "hello", BaseClass) */ string y = null /**/
		) { }
	}

	public sealed class MissingImpl : BaseClass {
		public override void Foo() { }

		public override void Bar(
			/* IncludeDefaultValueInOverrideForReadability(x, BaseClass) */ int x /**/
		) { }

		public override void Baz(
			float x,
			/* IncludeDefaultValueInOverrideForReadability(y, BaseClass) */ string y /**/
		) { }
	}

	public sealed class ImplWithNewDefaults : BaseClass {
		public override void Foo() { }
		public override void Bar( int x = 3 ) { }
		public override void Baz(
			/* DontIntroduceNewDefaultValuesInOverrides(x, BaseClass) */ float x = 3.14 /**/,
			string y = "hello"
		) { }
	}

	public abstract class OtherBase {
		public abstract void Foo( int multi = 0, int param = 1, int method = 2 );
	}

	public sealed class MultiInconsistentMethodImpl : OtherBase {
		public override void Foo(
			/* DefaultValuesInOverridesShouldBeConsistent(multi, 3, 0, OtherBase) */ int multi = 3 /**/,
			/* DefaultValuesInOverridesShouldBeConsistent(param, 4, 1, OtherBase) */ int param = 4 /**/,
			/* DefaultValuesInOverridesShouldBeConsistent(method, 5, 2, OtherBase) */ int method = 5 /**/
		) {}
	}

}

namespace Interfaces {
	public interface IFoo {
		void Foo();
		void Bar( int x = 3 );
		void Baz( float x, string y = null );
	}

	public struct GoodImplicitImpl : IFoo {
		public void Foo() { }
		public void Bar( int x = 3 ) { }
		public void Baz( float x, string y = null ) { }
	}

	public struct ExplicitImplIsAlwaysGood : IFoo {
		void IFoo.Foo() { }
		void IFoo.Bar( int x ) { }
		void IFoo.Baz( float x, string y ) { }
	}

	public struct InconsistentImpl : IFoo {
		public void Foo() { }

		public void Bar(
			/* DefaultValuesInOverridesShouldBeConsistent(x, 123123, 3, IFoo) */ int x = 123123 /**/
		) { }

		public void Baz(
			float x,
			/* DefaultValuesInOverridesShouldBeConsistent(y, "bye", null, IFoo) */ string y = "bye" /**/
		) { }
	}

	public sealed class MissingImpl : IFoo {
		public void Foo() { }

		public void Bar(
			/* IncludeDefaultValueInOverrideForReadability(x, IFoo) */ int x /**/
		) { }

		public void Baz(
			float x,
			/* IncludeDefaultValueInOverrideForReadability(y, IFoo) */ string y /**/
		) { }
	}

	public sealed class ImplWithNewDefaults : IFoo {
		public void Foo() { }
		public void Bar( int x = 3 ) { }
		public void Baz(
			/* DontIntroduceNewDefaultValuesInOverrides(x, IFoo) */ float x = 3.14 /**/,
			string y = null
		) { }
	}
}

namespace MultipleInterfaces {
	public interface IFoo {
		void Hello( int x = 111 );
	}

	public interface IBar {
		void Hello( int x = 222 );
	}

	public sealed class ExplicitDude : IFoo, IBar {
		void IFoo.Hello( int x ) { }
		void IBar.Hello( int x ) { }
	}

	// Not advocating this
	public sealed class HalfExplicitDudeButConsistent : IFoo, IBar {
		void IFoo.Hello( int x ) { }
		void Hello( int x = 222 ) { }
	}

	public sealed class HalfExplicitDudeInconsistent : IFoo, IBar {
		void IFoo.Hello( int x ) { }

		public void Hello(
			/* DefaultValuesInOverridesShouldBeConsistent(x, 333, 222, IBar) */ int x = 333 /**/
		) { }
	}

	public sealed class HalfExplicitDudeMissingDefault : IFoo, IBar {
		void IFoo.Hello( int x ) { }

		public void Hello(
			/* IncludeDefaultValueInOverrideForReadability(x, IBar) */ int x /**/
		) { }
	}
}

namespace NewModifier {
	interface IBaseInterface {
		void GetStuff( int count = 99 );
	}

	interface IMiddleInterface : IBaseInterface {
		// not advocating this in any way, and should probably be its own error
		new void GetStuff( int count = 55 );
	}

	class SomeClass : IMiddleInterface {
		// Gets called via SomeClass and IBaseInterface
		public void GetStuff( int count = 99 ) {}

		void IMiddleInterface.GetStuff( int count ) {}
	}

	class SomeClass2 : IMiddleInterface {
		public void GetStuff(
			/* DefaultValuesInOverridesShouldBeConsistent(count, 123, 99, IBaseInterface) */ int count = 123 /**/
		) { }
		void IMiddleInterface.GetStuff( int count ) {}
	}
}


<|start_filename|>tests/D2L.CodeStyle.Analyzers.Test/Specs/AwaitedTasksAnalyzer.cs<|end_filename|>
﻿// analyzer: D2L.CodeStyle.Analyzers.Language.AwaitedTasksAnalyzer

using System.Threading.Tasks;
using System.Runtime.CompilerServices;

namespace System.Threading.Tasks {

	public struct ValueTask {
		public ConfiguredValueTaskAwaitable ConfigureAwait( bool continueOnCapturedContext ) { }
	}
	public struct ValueTask<TResult> {
		public ConfiguredValueTaskAwaitable<TResult> ConfigureAwait( bool continueOnCapturedContext ) { }
	}

}

namespace System.Runtime.CompilerServices {

	public struct ConfiguredValueTaskAwaitable { }
	public struct ConfiguredValueTaskAwaitable<TResult> { }

}

namespace SpecTests {
	public sealed class SpecTests<T> {

		async public Task Good() {

			await TaskReturningFunction().ConfigureAwait( false );
			await TaskTReturningFunction().ConfigureAwait( false );
			await SomeClass.TaskReturningMemberFunction().ConfigureAwait( false );
			await SomeClass.TaskTReturningMemberFunction().ConfigureAwait( false );
			await ValueTaskReturningFunction().ConfigureAwait( false );
			await ValueTaskTReturningFunction().ConfigureAwait( false );
			await SomeClass.ValueTaskReturningMemberFunction().ConfigureAwait( false );
			await SomeClass.ValueTaskTReturningMemberFunction().ConfigureAwait( false );

			await ConfiguredTaskReturningFunction();
			await ConfiguredTaskTReturningFunction();
			await ConfiguredValueTaskReturningFunction();
			await ConfiguredValueTaskTReturningFunction();

			{
				Task t = default;
				await t.ConfigureAwait( false );
			}

			{
				Task<T> t = default;
				await t.ConfigureAwait( false );
			}

			{
				ValueTask t = default;
				await t.ConfigureAwait( false );
			}

			{
				ValueTask<T> t = default;
				await t.ConfigureAwait( false );
			}

		}

		async public Task IdeallyBadBecauseConfiguredWrong() {

			await TaskReturningFunction().ConfigureAwait( true );
			await TaskTReturningFunction().ConfigureAwait( true );
			await SomeClass.TaskReturningMemberFunction().ConfigureAwait( true );
			await SomeClass.TaskTReturningMemberFunction().ConfigureAwait( true );
			await ValueTaskReturningFunction().ConfigureAwait( true );
			await ValueTaskTReturningFunction().ConfigureAwait( true );
			await SomeClass.ValueTaskReturningMemberFunction().ConfigureAwait( true );
			await SomeClass.ValueTaskTReturningMemberFunction().ConfigureAwait( true );

			{
				Task t = default;
				await t.ConfigureAwait( true );
			}

			{
				Task<T> t = default;
				await t.ConfigureAwait( true );
			}

			{
				ValueTask t = default;
				await t.ConfigureAwait( true );
			}

			{
				ValueTask<T> t = default;
				await t.ConfigureAwait( true );
			}

		}

		async public Task BadBecauseNotConfigured() {

			/* AwaitedTaskNotConfigured() */ await TaskReturningFunction() /**/;
			/* AwaitedTaskNotConfigured() */ await TaskTReturningFunction() /**/;
			/* AwaitedTaskNotConfigured() */ await SomeClass.TaskReturningMemberFunction() /**/;
			/* AwaitedTaskNotConfigured() */ await SomeClass.TaskTReturningMemberFunction() /**/;
			/* AwaitedTaskNotConfigured() */ await ValueTaskReturningFunction() /**/;
			/* AwaitedTaskNotConfigured() */ await ValueTaskTReturningFunction() /**/;
			/* AwaitedTaskNotConfigured() */ await SomeClass.ValueTaskReturningMemberFunction() /**/;
			/* AwaitedTaskNotConfigured() */ await SomeClass.ValueTaskTReturningMemberFunction() /**/;

			{
				Task t = default;
				/* AwaitedTaskNotConfigured() */ await t /**/;
			}

			{
				Task<T> t = default;
				/* AwaitedTaskNotConfigured() */ await t /**/;
			}

			{
				ValueTask t = default;
				/* AwaitedTaskNotConfigured() */ await t /**/;
			}

			{
				ValueTask<T> t = default;
				/* AwaitedTaskNotConfigured() */ await t /**/;
			}

		}

		async public Task TaskReturningFunction() { }
		async public Task<T> TaskTReturningFunction() { }
		async public ValueTask ValueTaskReturningFunction() { }
		async public ValueTask<T> ValueTaskTReturningFunction() { }

		async public ConfiguredTaskAwaitable ConfiguredTaskReturningFunction() { }
		async public ConfiguredTaskAwaitable<T> ConfiguredTaskTReturningFunction() { }
		async public ConfiguredValueTaskAwaitable ConfiguredValueTaskReturningFunction() { }
		async public ConfiguredValueTaskAwaitable<T> ConfiguredValueTaskTReturningFunction() { }

		public static class SomeClass {

			async public static Task TaskReturningMemberFunction() { }
			async public static Task<T> TaskTReturningMemberFunction() { }
			async public static ValueTask ValueTaskReturningMemberFunction() { }
			async public static ValueTask<T> ValueTaskTReturningMemberFunction() { }

		}

	}
}


<|start_filename|>src/D2L.CodeStyle.Analyzers/ApiUsage/DependencyInjection/Domain/RegisterExtensionPointExpression.cs<|end_filename|>
﻿using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;

namespace D2L.CodeStyle.Analyzers.ApiUsage.DependencyInjection.Domain {
	// 	public static void RegisterPluginExtensionPoint<TExtensionPoint, T>(
	//			this IDependencyRegistry @this,
	//			ObjectScope scope
	//		) where TExtensionPoint : IExtensionPoint<T>;
	internal sealed class RegisterExtensionPointExpression : DependencyRegistrationExpression {
		internal override bool CanHandleMethod( IMethodSymbol method ) {
			return method.Name == "RegisterPluginExtensionPoint"
				&& method.IsExtensionMethod
				&& method.Parameters.Length == 1
				&& method.TypeArguments.Length == 2;
		}

		internal override DependencyRegistration GetRegistration( IMethodSymbol method, SeparatedSyntaxList<ArgumentSyntax> arguments, SemanticModel semanticModel ) {
			if( arguments.Count != 1 ) {
				return null;
			}

			ObjectScope scope;
			if( !TryGetObjectScope( arguments[0], semanticModel, out scope ) ) {
				return null;
			}

			return DependencyRegistration.NonFactory(
				scope,
				method.TypeArguments[1],
				method.TypeArguments[1]
			);
		}
	}
}


<|start_filename|>tests/D2L.CodeStyle.Analyzers.Test/Specs/FeatureDefinitionAnalyzer.cs<|end_filename|>
﻿// analyzer: D2L.CodeStyle.Analyzers.ApiUsage.LaunchDarkly.FeatureDefinitionAnalyzer

namespace D2L.LP.LaunchDarkly {
	public abstract class FeatureDefinition<TValue> { }
}

namespace SpecTests {
	using System;
	using D2L.LP.LaunchDarkly;

	public sealed class ObjectDefinition :/* InvalidLaunchDarklyFeatureDefinition(long) */ FeatureDefinition<long> /**/{ }
	public sealed class ObjectDefinition :/* InvalidLaunchDarklyFeatureDefinition(System.TimeSpan) */ FeatureDefinition<TimeSpan> /**/{ }

	public sealed class BoolDefinition : FeatureDefinition<bool> { }
	public sealed class FloatDefinition : FeatureDefinition<float> { }
	public sealed class IntDefinition : FeatureDefinition<int> { }
	public sealed class StringDefinition : FeatureDefinition<string> { }

	public sealed class BoolDefinition : FeatureDefinition<Boolean> { }
	public sealed class FloatDefinition : FeatureDefinition<Single> { }
	public sealed class IntDefinition : FeatureDefinition<Int32> { }
	public sealed class StringDefinition : FeatureDefinition<String> { }

	public sealed class BoolDefinition : FeatureDefinition<System.Boolean> { }
	public sealed class FloatDefinition : FeatureDefinition<System.Single> { }
	public sealed class IntDefinition : FeatureDefinition<System.Int32> { }
	public sealed class StringDefinition : FeatureDefinition<System.String> { }
}

namespace MissingLaunchDarklyUsingStatement {

	public sealed class ObjectDefinition : FeatureDefinition<System.TimeSpan> { }
}

namespace UnresolvedValueType {
	using D2L.LP.LaunchDarkly;

	public sealed class ObjectDefinition : FeatureDefinition<YMCA> { }
}

<|start_filename|>src/D2L.CodeStyle.Analyzers/Immutability/ImmutabilityQuery.cs<|end_filename|>
﻿using Microsoft.CodeAnalysis;

namespace D2L.CodeStyle.Analyzers.Immutability {
	internal readonly struct ImmutabilityQuery {
		public ImmutabilityQuery(
			ImmutableTypeKind kind,
			ITypeSymbol type
		) {
			Kind = kind;
			Type = type;
		}

		public ImmutableTypeKind Kind { get; }
		public ITypeSymbol Type { get; }
	}
}


<|start_filename|>tests/D2L.CodeStyle.TestAnalyzers.Tests/NUnit/ConfigTestSetupStringsAnalyzerTests.cs<|end_filename|>
﻿using D2L.CodeStyle.TestAnalyzers.Common;
using D2L.CodeStyle.TestAnalyzers.Test.Verifiers;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using NUnit.Framework;

namespace D2L.CodeStyle.TestAnalyzers.NUnit {
	[TestFixture]
	internal sealed class ConfigTestSetupStringsAnalyzerTests : DiagnosticVerifier {

		private const string PREAMBLE = @"
using System;
using D2L.LP.Configuration.Config;
using NUnit.Framework;

namespace D2L.LP.Configuration.Config {
	[AttributeUsage( AttributeTargets.Method | AttributeTargets.Class )]
	public sealed class ConfigTestSetupAttribute : Attribute {
		public ConfigTestSetupAttribute( string sourceName ) { }
	}
}
";

		[Test]
		public void NoAttribute_NoDiag() {
			const string test = PREAMBLE + @"
namespace Test {
	class Test {
		[Test]
		public void Test() {}
	}
}";
			AssertNoDiagnostic( test );
		}

		[Test]
		public void MethodAttribute_WithNameOf_NoDiag() {
			const string test = PREAMBLE + @"
namespace Test {
	class Test {
		private static readonly object SourceName = new object();

		[Test]
		[ConfigTestSetup( nameof( SourceName ) )]
		public void Test() {}
	}
}";
			AssertNoDiagnostic( test );
		}

		[Test]
		public void MethodAttribute_WithLiteral_Diag() {
			const string test = PREAMBLE + @"
namespace Test {
	class Test {
		private static readonly object SourceName = new object();

		[Test]
		[ConfigTestSetup( ""SourceName"" )]
		public void Test() {}
	}
}";
			AssertSingleDiagnostic( Diagnostics.ConfigTestSetupStrings, test, 18, 21, "SourceName" );
		}

		[Test]
		public void MethodAttribute_WithConstant_Diag() {
			const string test = PREAMBLE + @"
namespace Test {
	class Test {
		private const string Source = ""SourceName"";
		private static readonly object SourceName = new object();

		[Test]
		[ConfigTestSetup( Source )]
		public void Test() {}
	}
}";
			AssertSingleDiagnostic( Diagnostics.ConfigTestSetupStrings, test, 19, 21, "..." );
		}

		[Test]
		public void FixtureAttribute_WithNameOf_NoDiag() {
			const string test = PREAMBLE + @"
namespace Test {
	[ConfigTestSetup( nameof( SourceName ) )]
	class Test {
		private static readonly object SourceName = new object();

		[Test]
		public void Test() {}
	}
}";
			AssertNoDiagnostic( test );
		}

		[Test]
		public void FixtureAttribute_WithLiteral_Diag() {
			const string test = PREAMBLE + @"
namespace Test {
	[ConfigTestSetup( ""SourceName"" )]
	class Test {
		private static readonly object SourceName = new object();

		[Test]
		public void Test() {}
	}
}";
			AssertSingleDiagnostic( Diagnostics.ConfigTestSetupStrings, test, 14, 20, "SourceName" );
		}

		[Test]
		public void FixtureAttribute_WithConstant_Diag() {
			const string test = PREAMBLE + @"
namespace Test {
	[ConfigTestSetup( Source )]
	class Test {
		public const string Source = ""SourceName"";
		private static readonly object SourceName = new object();

		[Test]
		public void Test() {}
	}
}";
			AssertSingleDiagnostic( Diagnostics.ConfigTestSetupStrings, test, 14, 20, "..." );
		}

		private void AssertNoDiagnostic( string file ) {
			VerifyCSharpDiagnostic( file );
		}

		private void AssertSingleDiagnostic(
				DiagnosticDescriptor diag,
				string file,
				int line,
				int column,
				params object[] messageArgs
			) {

			DiagnosticResult result = new DiagnosticResult {
				Id = diag.Id,
				Message = string.Format( diag.MessageFormat.ToString(), messageArgs ),
				Severity = DiagnosticSeverity.Error,
				Locations = new[] {
					new DiagnosticResultLocation( "Test0.cs", line, column )
				}
			};

			VerifyCSharpDiagnostic( file, result );
		}


		private static readonly MetadataReference NUnitReference =
			MetadataReference.CreateFromFile( typeof(TestAttribute).Assembly.Location );

		protected override MetadataReference[] GetAdditionalReferences() => new[] {
			NUnitReference
		};

		protected override DiagnosticAnalyzer GetCSharpDiagnosticAnalyzer() {
			return new ConfigTestSetupStringsAnalyzer();
		}

	}
}


<|start_filename|>src/D2L.CodeStyle.Analyzers/ApiUsage/DependencyInjection/Domain/DependencyRegistration.cs<|end_filename|>
﻿using Microsoft.CodeAnalysis;

namespace D2L.CodeStyle.Analyzers.ApiUsage.DependencyInjection.Domain {
	internal sealed class DependencyRegistration {
		private DependencyRegistration(
			ObjectScope scope,
			ITypeSymbol dependencyType,
			ITypeSymbol concreteType = null,
			ITypeSymbol factoryType = null,
			ITypeSymbol dynamicObjectType = null,
			bool isInstanceRegistration = false
		) {
			ObjectScope = scope;
			DependencyType = dependencyType;
			ConcreteType = concreteType;
			FactoryType = factoryType;
			DynamicObjectFactoryType = dynamicObjectType;
			IsInstanceRegistration = isInstanceRegistration;
		}

		public ObjectScope ObjectScope { get; }
		public ITypeSymbol DependencyType { get; }
		public ITypeSymbol ConcreteType { get; }
		public ITypeSymbol FactoryType { get; }
		public ITypeSymbol DynamicObjectFactoryType { get; }
		public bool IsInstanceRegistration { get; }

		internal static DependencyRegistration Marker( ObjectScope scope, ITypeSymbol dependencyType )
			=> new DependencyRegistration(
				scope,
				dependencyType: dependencyType
			);

		internal static DependencyRegistration NonFactory( ObjectScope scope, ITypeSymbol dependencyType, ITypeSymbol concreteType )
			=> new DependencyRegistration(
				scope,
				dependencyType: dependencyType,
				concreteType: concreteType
			);

		internal static DependencyRegistration Factory( ObjectScope scope, ITypeSymbol dependencyType, ITypeSymbol factoryType, ITypeSymbol concreteType )
			=> new DependencyRegistration(
				scope,
				dependencyType: dependencyType,
				factoryType: factoryType,
				concreteType: concreteType
			);

		internal static DependencyRegistration DynamicObjectFactory( ObjectScope scope, ITypeSymbol dependencyType, ITypeSymbol dynamicObjectType )
			=> new DependencyRegistration(
				scope,
				dependencyType: dependencyType,
				dynamicObjectType: dynamicObjectType
			);

		internal static DependencyRegistration Instance( ObjectScope scope, ITypeSymbol dependencyType, ITypeSymbol concreteType )
			=> new DependencyRegistration(
				scope,
				dependencyType: dependencyType,
				concreteType: concreteType,
				isInstanceRegistration: true
			);
	}

	/// <summary>
	/// This is copied from lp/framework/core/D2L.LP.Foundation/LP/Extensibility/Activation/Domain/ObjectScope.cs
	/// so that the values match.
	/// </summary>
	internal enum ObjectScope {
		AlwaysCreateNewInstance = 0,
		Singleton = 1,
		WebRequest = 3
	}

}


<|start_filename|>src/D2L.CodeStyle.Analyzers/Immutability/ImmutableTypeInfo.cs<|end_filename|>
﻿using System;
using System.Collections.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis;

namespace D2L.CodeStyle.Analyzers.Immutability {
	/// <summary>
	/// A representation of the immutability of some type.
	/// </summary>
	internal readonly struct ImmutableTypeInfo {

		// a mapping of which type parameters considered necessarily immutable for the
		// conditionally immutable type to be immutable
		private readonly ImmutableArray<bool> m_conditionalTypeParameters;

		private ImmutableTypeInfo(
			ImmutableTypeKind kind,
			INamedTypeSymbol type,
			ImmutableArray<bool> conditionalTypeParameters
		) {
			Kind = kind;
			Type = type;
			m_conditionalTypeParameters = conditionalTypeParameters;
		}

		public ImmutableTypeKind Kind { get; }

		public INamedTypeSymbol Type { get; }

		public bool IsConditional => m_conditionalTypeParameters.Length > 0;

		public bool IsImmutableDefinition(
			ImmutabilityContext context,
			INamedTypeSymbol definition,
			Func<Location> getLocation,
			out Diagnostic diagnostic
		) {
			if( !Type.Equals( definition, SymbolEqualityComparer.Default )
				&& !Type.Equals( definition?.OriginalDefinition, SymbolEqualityComparer.Default )
			) {
				throw new InvalidOperationException( $"{ nameof( IsImmutableDefinition ) } should only be called with an equivalent type definition" );
			}

			var argRelevance = definition
				.TypeArguments
				.Zip( m_conditionalTypeParameters, ( a, relevant ) => (a, relevant) );
			foreach( (ITypeSymbol argument, bool isRelevant) in argRelevance ) {
				if( !isRelevant ) {
					continue;
				}

				if( !context.IsImmutable(
					new ImmutabilityQuery(
						ImmutableTypeKind.Total,
						argument
					),
					getLocation,
					out diagnostic
				) ) {
					return false;
				}
			}

			diagnostic = null;
			return true;
		}

		public static ImmutableTypeInfo Create(
			AnnotationsContext annotationsContext,
			ImmutableTypeKind kind,
			INamedTypeSymbol type
		) {
			ImmutableArray<bool> immutableTypeParameters = type
				.TypeParameters
				.Select( p => annotationsContext.Objects.OnlyIf.IsDefined( p ) )
				.ToImmutableArray();

			return new ImmutableTypeInfo(
				kind: kind,
				type: type,
				conditionalTypeParameters: immutableTypeParameters
			);
		}

		public static ImmutableTypeInfo CreateWithAllConditionalTypeParameters(
			ImmutableTypeKind kind,
			INamedTypeSymbol type
		) {
			ImmutableArray<bool> immutableTypeParameters = type
				.TypeParameters
				.Select( p => true )
				.ToImmutableArray();

			return new ImmutableTypeInfo(
				kind: kind,
				type: type,
				conditionalTypeParameters: immutableTypeParameters
			);
		}
	}
}


<|start_filename|>src/D2L.CodeStyle.Analyzers/Immutability/MutabilityAuditor.cs<|end_filename|>
﻿using System.Linq;
using Microsoft.CodeAnalysis;
using static D2L.CodeStyle.Analyzers.Immutability.ImmutableDefinitionChecker;

namespace D2L.CodeStyle.Analyzers.Immutability {
	internal sealed class MutabilityAuditor {
		public static bool CheckAudited(
			AnnotationsContext annotationsContext,
			ISymbol symbol,
			DiagnosticSink diagnosticSink,
			out Location location ) {

			// Collect audit information
			var hasStaticAudited = annotationsContext.Statics.Audited.IsDefined( symbol );
			var hasStaticUnaudited = annotationsContext.Statics.Unaudited.IsDefined( symbol );
			var hasMutabilityAudited = annotationsContext.Mutability.Audited.IsDefined( symbol );
			var hasMutabilityUnaudited = annotationsContext.Mutability.Unaudited.IsDefined( symbol );
			var hasBothStaticsAttributes = hasStaticAudited && hasStaticUnaudited;
			var hasBothMutabilityAttributes = hasMutabilityAudited && hasMutabilityUnaudited;
			var hasEitherStaticsAttributes = hasStaticAudited || hasStaticUnaudited;
			var hasEitherMutabilityAttributes = hasMutabilityAudited || hasMutabilityUnaudited;

			// If there are no audits, don't do anything
			if( !hasEitherStaticsAttributes && !hasEitherMutabilityAttributes ) {
				location = null;
				return false;
			}

			var syntaxLocation = symbol
				.DeclaringSyntaxReferences[0]
				.GetSyntax()
				.GetLastToken()
				.GetLocation();

			// Check if both static audits are applied
			if( hasBothStaticsAttributes ) {
				var diagnostic = Diagnostic.Create(
					Diagnostics.ConflictingImmutability,
					syntaxLocation,
					"Statics.Audited",
					"Statics.Unaudited",
					symbol.Kind.ToString().ToLower() );
				diagnosticSink( diagnostic );
			}

			// Check if both mutability audits are applied
			if( hasBothMutabilityAttributes ) {
				var diagnostic = Diagnostic.Create(
					Diagnostics.ConflictingImmutability,
					syntaxLocation,
					"Mutability.Audited",
					"Mutability.Unaudited",
					symbol.Kind.ToString().ToLower() );
				diagnosticSink( diagnostic );
			}

			AttributeData attr = null;

			if( symbol.IsStatic ) {
				// Check if a static member is using mutability audits
				if( hasEitherMutabilityAttributes ) {
					var diagnostic = Diagnostic.Create(
						Diagnostics.InvalidAuditType,
						syntaxLocation,
						"static",
						symbol.Kind.ToString().ToLower(),
						"Statics.*" );
					diagnosticSink( diagnostic );
				}

				attr = annotationsContext.Statics.Audited.GetAll( symbol ).FirstOrDefault()
					?? annotationsContext.Statics.Unaudited.GetAll( symbol ).FirstOrDefault();
			} else {
				// Check if a non-static member is using static audits
				if( hasEitherStaticsAttributes ) {
					var diagnostic = Diagnostic.Create(
						Diagnostics.InvalidAuditType,
						syntaxLocation,
						"non-static",
						symbol.Kind.ToString().ToLower(),
						"Mutability.*" );
					diagnosticSink( diagnostic );
				}

				attr = annotationsContext.Mutability.Audited.GetAll( symbol ).FirstOrDefault()
					?? annotationsContext.Mutability.Unaudited.GetAll( symbol ).FirstOrDefault();
			}

			if( attr != null ) {
				location = GetLocation( attr );
				return true;
			}
			location = null;
			return false;
		}

		private static Location GetLocation( AttributeData attr )
			=> attr.ApplicationSyntaxReference.GetSyntax().GetLocation();
	}
}


<|start_filename|>src/D2L.CodeStyle.Analyzers/ApiUsage/DependencyInjection/Domain/RegisterSubInterfaceExpression.cs<|end_filename|>
﻿using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;

namespace D2L.CodeStyle.Analyzers.ApiUsage.DependencyInjection.Domain {
	// void RegisterSubInterface<TSubInterfaceType, TInjectableSuperInterfaceType>(
	//		this IDependencyRegistry @this,
	//		ObjectScope scope
	// ) where TInjectableSuperInterfaceType : TSubInterfaceType
	internal sealed class RegisterSubInterfaceExpression : DependencyRegistrationExpression {
		internal override bool CanHandleMethod( IMethodSymbol method ) {
			return method.Name == "RegisterSubInterface"
				&& method.IsExtensionMethod
				&& method.TypeArguments.Length == 2
				&& method.Parameters.Length == 1;
		}

		internal override DependencyRegistration GetRegistration( IMethodSymbol method, SeparatedSyntaxList<ArgumentSyntax> arguments, SemanticModel semanticModel ) {
			if( arguments.Count != 1 ) {
				return null;
			}

			ObjectScope scope;
			if( !TryGetObjectScope( arguments[0], semanticModel, out scope ) ) {
				return null;
			}
			return DependencyRegistration.NonFactory(
				scope: scope,
				dependencyType: method.TypeArguments[0],
				concreteType: method.TypeArguments[1]
			);
		}
	}
}


<|start_filename|>tests/D2L.CodeStyle.Analyzers.Test/Extensions/SyntaxNodeExtensionTests.cs<|end_filename|>
﻿using Microsoft.CodeAnalysis.CSharp.Syntax;
using NUnit.Framework;
using System.Linq;
using static D2L.CodeStyle.Analyzers.RoslynSymbolFactory;

namespace D2L.CodeStyle.Analyzers.Extensions {

    [TestFixture]
    internal sealed class SyntaxNodeExtensionTests {

        [Test]
        public void IsPropertyGetterImplemented_Yes_ReturnsTrue() {
            var prop = Property( "private string random { get { return \"\"; } }" );
            var syntax = prop.Symbol.DeclaringSyntaxReferences.FirstOrDefault()?.GetSyntax() as PropertyDeclarationSyntax;
            Assert.IsNotNull( syntax );

            var isGetterImplemented = syntax.IsAutoImplemented();

            Assert.IsFalse( isGetterImplemented );
        }

        [Test]
        public void IsPropertyGetterImplemented_No_ReturnsFalse() {
            var prop = Property( "private string random { get; }" );
            var syntax = prop.Symbol.DeclaringSyntaxReferences.FirstOrDefault()?.GetSyntax() as PropertyDeclarationSyntax;
            Assert.IsNotNull( syntax );

            var isGetterImplemented = syntax.IsAutoImplemented();

            Assert.IsTrue( isGetterImplemented );
        }
    }
}


<|start_filename|>src/D2L.CodeStyle.Analyzers/ApiUsage/DependencyInjection/DependencyRegistrationsAnalyzer.cs<|end_filename|>
﻿using System.Collections.Immutable;
using System.Linq;
using D2L.CodeStyle.Analyzers.ApiUsage.DependencyInjection.Domain;
using D2L.CodeStyle.Analyzers.Extensions;
using D2L.CodeStyle.Analyzers.Immutability;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;

namespace D2L.CodeStyle.Analyzers.ApiUsage.DependencyInjection {
	[DiagnosticAnalyzer( LanguageNames.CSharp )]
	public sealed class DependencyRegistrationsAnalyzer : DiagnosticAnalyzer {

		// It might be worthwhile to refactor this to an attribute instead later.
		private static readonly IImmutableSet<string> s_blessedClasses = ImmutableHashSet.Create(
			"D2L.LP.Extensibility.Activation.Domain.DependencyRegistryExtensionPointExtensions",
			"D2L.LP.Extensibility.Activation.Domain.DynamicObjectFactoryRegistryExtensions",
			"D2L.LP.Extensibility.Activation.Domain.IDependencyRegistryConfigurePluginsExtensions",
			"D2L.LP.Extensibility.Activation.Domain.IDependencyRegistryExtensions",
			"D2L.LP.Extensibility.Activation.Domain.LegacyPluginsDependencyLoaderExtensions",

			"SpecTests.SomeTestCases.RegistrationCallsInThisClassAreIgnored", // this comes from a test
			"SpecTests.SomeTestCases.RegistrationCallsInThisStructAreIgnored" // this comes from a test
		);

		public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create( 
			Diagnostics.UnsafeSingletonRegistration,
			Diagnostics.RegistrationKindUnknown,
			Diagnostics.AttributeRegistrationMismatch,
			Diagnostics.DependencyRegistraionMissingPublicConstructor
		);

		public override void Initialize( AnalysisContext context ) {
			context.EnableConcurrentExecution();
			context.ConfigureGeneratedCodeAnalysis( GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics );
			context.RegisterCompilationStartAction( RegisterAnalysis );
		}

		private void RegisterAnalysis( CompilationStartAnalysisContext context ) {
			DependencyRegistry dependencyRegistry;
			if( !DependencyRegistry.TryCreateRegistry( context.Compilation, out dependencyRegistry ) ) {
				return;
			}

			if( !AnnotationsContext.TryCreate( context.Compilation, out AnnotationsContext annotationsContext ) ) {
				return;
			}
			var immutabilityCtx = ImmutabilityContext.Create( context.Compilation, annotationsContext );

			context.RegisterSyntaxNodeAction(
				ctx => AnalyzeInvocation( ctx, immutabilityCtx, dependencyRegistry ),
				SyntaxKind.InvocationExpression
			);
		}

		private void AnalyzeInvocation(
			SyntaxNodeAnalysisContext context,
			ImmutabilityContext immutabilityCtx,
			DependencyRegistry registry
		) {
			var root = context.Node as InvocationExpressionSyntax;
			if( root == null ) {
				return;
			}
			var method = context.SemanticModel.GetSymbolInfo( root ).Symbol as IMethodSymbol;
			if( method == null ) {
				return;
			}

			if( !registry.IsRegistationMethod( method ) ) {
				return;
			}

			if( IsExpressionInClassInIgnoreList( root, context.SemanticModel ) ) {
				return;
			}

			DependencyRegistrationExpression dependencyRegistrationExpresion;
			if( !registry.TryMapRegistrationMethod( 
				method, 
				root.ArgumentList.Arguments, 
				context.SemanticModel, 
				out dependencyRegistrationExpresion 
			) ) {
				// this can happen where there's a new registration method
				// that we can't map to
				// so we fail
				var diagnostic = Diagnostic.Create(
					Diagnostics.RegistrationKindUnknown,
					root.GetLocation()
				);
				context.ReportDiagnostic( diagnostic );
				return;
			}

			var dependencyRegistration = dependencyRegistrationExpresion.GetRegistration( 
				method,
				root.ArgumentList.Arguments,
				context.SemanticModel
			);
			if( dependencyRegistration == null ) {
				/* This can happen in the following scenarios:
				 * 1) ObjectScope is passed in as a variable and we can't extract it.
				 * 2) Some require argument is missing (compile error).
				 * We fail because we can't analyze it.
				 */
				var diagnostic = Diagnostic.Create(
					Diagnostics.RegistrationKindUnknown,
					root.GetLocation()
				);
				context.ReportDiagnostic( diagnostic );
				return;
			}

			InspectRegistration( dependencyRegistration, immutabilityCtx, context );
		}

		private void InspectRegistration(
			DependencyRegistration registration,
			ImmutabilityContext immutabilityCtx,
			SyntaxNodeAnalysisContext ctx
		) {
			if( registration.ObjectScope == ObjectScope.Singleton ) {
				var typesToInspect = GetTypesRequiredToBeImmutableForSingletonRegistration( registration );
				foreach( var type in typesToInspect ) {
					if( type.IsNullOrErrorType() ) {
						continue;
					}

					// We require full immutability here,
					// because we don't know if it's a concrete type
					//
					// TODO: could make this better by exposing the minimum
					// scope required for each type

					var query = new ImmutabilityQuery(
						ImmutableTypeKind.Total,
						type
					);

					if( !immutabilityCtx.IsImmutable( query, () => null, out var _ ) ) {
						var diagnostic = GetUnsafeSingletonDiagnostic(
							ctx.Compilation.Assembly,
							ctx.Node,
							type
						);

						ctx.ReportDiagnostic( diagnostic );
					}
				}
			}

			// ensure webrequest isn't marked Singleton
			var isMarkedSingleton = registration.DependencyType.IsTypeMarkedSingleton();
			if( isMarkedSingleton && registration.ObjectScope != ObjectScope.Singleton ) {
				var diagnostic = Diagnostic.Create(
					Diagnostics.AttributeRegistrationMismatch,
					ctx.Node.GetLocation(),
					registration.DependencyType.GetFullTypeNameWithGenericArguments()
				);
				ctx.ReportDiagnostic( diagnostic );
			}

			if( TryGetInstantiatedTypeForRegistration( registration, out ITypeSymbol instantiatedType ) ) {
				if( !TryGetInjectableConstructor( instantiatedType, out IMethodSymbol injectableConstructor ) ) {
					var diagnostic = Diagnostic.Create(
						Diagnostics.DependencyRegistraionMissingPublicConstructor,
						ctx.Node.GetLocation(),
						instantiatedType.GetFullTypeNameWithGenericArguments()
					);
					ctx.ReportDiagnostic( diagnostic );
				}
			}
		}

		private static Diagnostic GetUnsafeSingletonDiagnostic(
			IAssemblySymbol assembly,
			SyntaxNode syntax,
			ITypeSymbol type
		) {
			DiagnosticSeverity sev;

			switch( assembly.Name ) {
				// To make the cleanup not Sisyphean, the diagnostic is
				// currently ignored in these assemblies:
				case "D2L":
				case "D2L.AW.UserInteractionConsumer":
				case "D2L.Custom.SessionCourseCopy":
				case "D2L.Custom.SpecialAccessAPIs":
				case "D2L.Custom.UserSyncTool":
				case "D2L.eP.Domain":
				case "D2L.LE.IntelligentAgents":
				case "D2L.LE.LO":
				case "D2L.LP":
				case "D2L.LP.AppLoader":
				case "D2L.LP.Diagnostics.Web":
				case "D2L.LP.OrgUnits.WorkQueue":
				case "D2L.LP.Services.Framework":
				case "D2L.LP.Tools.Extensibility":
				case "D2L.LP.Web":
					sev = DiagnosticSeverity.Info;
					break;
				default:
					sev = DiagnosticSeverity.Error;
					break;
			}

			return Diagnostic.Create(
				Diagnostics.UnsafeSingletonRegistration,
				syntax.GetLocation(),
				effectiveSeverity: sev,
				additionalLocations: ImmutableArray<Location>.Empty,
				properties: ImmutableDictionary<string, string>.Empty,
				type.GetFullTypeNameWithGenericArguments()
			);
		}

		private ImmutableArray<ITypeSymbol> GetTypesRequiredToBeImmutableForSingletonRegistration( DependencyRegistration registration ) {
			// if we have a concrete type, use it
			if( !registration.ConcreteType.IsNullOrErrorType() ) {
				return ImmutableArray.Create( registration.ConcreteType );
			}

			// if we have a dynamically generated objectfactory, use its constructor arguments
			if( !registration.DynamicObjectFactoryType.IsNullOrErrorType() ) {
				ImmutableArray<ITypeSymbol> dependencies;
				if( TryGetDependenciesFromConstructor( registration.DynamicObjectFactoryType, out dependencies ) ) {
					return dependencies;
				}
				// this is a dynamic object factory, but either 
				// (1) there is no public constructor, or 
				// (2) one of the parameter's types doesn't exist
				// in all cases, fall back to dependency type
			}

			// otherwise use the dependency type
			return ImmutableArray.Create( registration.DependencyType );
		}

		/// <summary>
		/// Gets the type that will be actually instantiated by DI for the registration.
		/// This is the factory type for registrations like RegisterFactory, or the concrete type otherwise
		/// </summary>
		/// <param name="registration"></param>
		/// <param name="instantiatedType"></param>
		/// <returns></returns>
		private bool TryGetInstantiatedTypeForRegistration( DependencyRegistration registration, out ITypeSymbol instantiatedType ) {
			if( registration.IsInstanceRegistration ) {
				instantiatedType = null;
				return false;
			}

			ITypeSymbol type = registration.FactoryType ?? registration.ConcreteType;

			if( type.IsNullOrErrorType() ) {
				instantiatedType = null;
				return false;
			}

			// hacks
			// handle RegisterSubInterface<IFoo, IFooBar>():
			// IFooBar is in the registration as "concrete type" and don't want to deal with that refactoring
			if( type.TypeKind == TypeKind.Interface ) {
				instantiatedType = null;
				return false;
			}

			// ignore registry usage in registry extensions we don't know about
			// public static void Foo<T, U>( this IDependencyRegistry @this, ObjectScope scope ) where U : T {
			//   // Can't find constructor for U!
			//   @this.Register<T, U>( scope );
			// }
			if( type.TypeKind == TypeKind.TypeParameter ) {
				instantiatedType = null;
				return false;
			}

			// Register( typof( IFoo<> ), typeof( Foo<> ), ObjectScope.Singleton )
			// Turn Foo<> into Foo<T>
			if( type is INamedTypeSymbol namedType ) {
				type = namedType.ConstructedFrom;
			}

			instantiatedType = type;
			return true;
		}

		private bool TryGetInjectableConstructor( ITypeSymbol type, out IMethodSymbol injectableConstructor ) {
			injectableConstructor = type.GetMembers()
				.Where( m => m.Kind == SymbolKind.Method )
				.Cast<IMethodSymbol>()
				.Where( m => m.MethodKind == MethodKind.Constructor && m.DeclaredAccessibility == Accessibility.Public )
				.FirstOrDefault();

			if( injectableConstructor == null ) {
				return false;
			}

			return true;
		}

		private bool TryGetDependenciesFromConstructor( ITypeSymbol type, out ImmutableArray<ITypeSymbol> dependencies  ) {
			if( !TryGetInjectableConstructor( type, out IMethodSymbol ctor ) ) {
				dependencies = ImmutableArray<ITypeSymbol>.Empty;
				return false;
			}

			dependencies = ctor.Parameters
				.Where( Attributes.Dependency.IsDefined )
				.Select( p => p.Type )
				.ToImmutableArray();
			return true;
		}

		private bool IsExpressionInClassInIgnoreList( InvocationExpressionSyntax expr, SemanticModel semanticModel ) {
			var structOrClass = GetClassOrStructContainingExpression( expr, semanticModel );
			if( structOrClass.IsNullOrErrorType() ) {
				// we failed to pull out the class/struct this invocation is being called from
				// so don't ignore it
				return false;
			}

			var className = structOrClass.GetFullTypeNameWithGenericArguments();
			if( s_blessedClasses.Contains( className ) ) {
				return true;
			}

			return false;
		}

		private ITypeSymbol GetClassOrStructContainingExpression(
			InvocationExpressionSyntax expr,
			SemanticModel semanticModel
		) {
			foreach( var ancestor in expr.Ancestors() ) {
				if( ancestor is StructDeclarationSyntax ) {
					return semanticModel.GetDeclaredSymbol( ancestor as StructDeclarationSyntax );
				} else if( ancestor is ClassDeclarationSyntax ) {
					return semanticModel.GetDeclaredSymbol( ancestor as ClassDeclarationSyntax );
				}
			}
			return null;
		}
	}
}


<|start_filename|>src/D2L.CodeStyle.Annotations/Contract/NotNullAttribute.cs<|end_filename|>
﻿using System;

namespace D2L.CodeStyle.Annotations.Contract {

	/// <summary>
	/// Indicates that a paramater may not be called with `null`
	/// </summary>
	[AttributeUsage( AttributeTargets.Parameter, AllowMultiple = false )]
	public sealed class NotNullAttribute : Attribute {
	}
}


<|start_filename|>src/D2L.CodeStyle.Analyzers/Immutability/ReadOnlyParameterAnalyzer.cs<|end_filename|>
﻿using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using System.Collections.Immutable;
using System.Linq;

namespace D2L.CodeStyle.Analyzers.Immutability {

	[DiagnosticAnalyzer( LanguageNames.CSharp )]
	internal sealed class ReadOnlyParameterAnalyzer : DiagnosticAnalyzer {

		public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(
			Diagnostics.ReadOnlyParameterIsnt
		);

		public override void Initialize( AnalysisContext context ) {
			context.EnableConcurrentExecution();
			context.ConfigureGeneratedCodeAnalysis( GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics );
			context.RegisterCompilationStartAction( RegisterAnalysis );
		}

		private static void RegisterAnalysis( CompilationStartAnalysisContext context ) {

			Compilation compilation = context.Compilation;

			INamedTypeSymbol readOnlyAttribute = compilation.GetTypeByMetadataName( "D2L.CodeStyle.Annotations.ReadOnlyAttribute" );

			if( readOnlyAttribute == null ) {
				return;
			}

			context.RegisterSyntaxNodeAction(
				ctx => AnalyzeParameter(
					ctx: ctx,
					readOnlyAttribute: readOnlyAttribute,
					syntax: ctx.Node as ParameterSyntax
				),
				SyntaxKind.Parameter
			);
		}

		private static void AnalyzeParameter(
			SyntaxNodeAnalysisContext ctx,
			INamedTypeSymbol readOnlyAttribute,
			ParameterSyntax syntax
		) {
			SemanticModel model = ctx.SemanticModel;

			IParameterSymbol parameter = model.GetDeclaredSymbol( syntax, ctx.CancellationToken );

			if( !IsMarkedReadOnly( readOnlyAttribute, parameter ) ) {
				return;
			}

			if( parameter.RefKind != RefKind.None ) {
				/**
				 * public void Foo( [ReadOnly] in int foo )
				 * public void Foo( [ReadOnly] ref int foo )
				 * public void Foo( [ReadOnly] out int foo )
				 */
				ctx.ReportDiagnostic( Diagnostic.Create(
					Diagnostics.ReadOnlyParameterIsnt,
					syntax.GetLocation(),
					"is an in/ref/out parameter"
				) );
			}

			IMethodSymbol method = parameter.ContainingSymbol as IMethodSymbol;
			BlockSyntax methodBody = ( method.DeclaringSyntaxReferences[0].GetSyntax( ctx.CancellationToken ) as BaseMethodDeclarationSyntax ).Body;

			if( methodBody == null ) {
				/**
				 * Method declaration on an interface.
				 */
				return;
			}

			DataFlowAnalysis dataflow = model.AnalyzeDataFlow( methodBody );
			if( dataflow.WrittenInside.Contains( parameter ) ) {
				/**
				 * public void Foo( [ReadOnly] int foo ) {
				 *   foo = 1; // write
				 *   void Inline() { foo = 1; // write }
				 *   var lambda = () => foo = 1; // write
				 *   SomeRefFunc( ref foo ); // pass by ref, potential for write
				 * }
				 */
				ctx.ReportDiagnostic( Diagnostic.Create(
					Diagnostics.ReadOnlyParameterIsnt,
					syntax.GetLocation(),
					"is assigned to and/or passed by reference"
				) );
			}
		}

		private static bool IsMarkedReadOnly(
			INamedTypeSymbol readOnlyAttribute,
			IParameterSymbol parameterSymbol
		) {
			foreach( AttributeData attribute in parameterSymbol.GetAttributes() ) {
				if( IsReadOnlyAttribute( readOnlyAttribute, attribute.AttributeClass ) ) {
					return true;
				}
			}

			return false;
		}

		private static bool IsReadOnlyAttribute(
			INamedTypeSymbol readOnlyAttribute,
			INamedTypeSymbol type
		) {
			if( type == null ) {
				return false;
			}

			if( type.Equals( readOnlyAttribute, SymbolEqualityComparer.Default ) ) {
				return true;
			}

			return IsReadOnlyAttribute( readOnlyAttribute, type.BaseType );
		}
	}
}


<|start_filename|>src/D2L.CodeStyle.Analyzers/AnnotationsContext.cs<|end_filename|>
﻿using System.Collections.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis;

namespace D2L.CodeStyle.Analyzers {
	internal sealed class AnnotationsContext {

		public static bool IsAvailable( Compilation compilation ) => Build.MandatoryReferencesAnalyzer.HasAnnotationsReference( compilation );

		public static bool TryCreate( Compilation compilation, out AnnotationsContext ctx ) {
			if( IsAvailable( compilation ) ) {
				ctx = new AnnotationsContext( compilation );
				return true;
			}

			ctx = null;
			return false;
		}

		private AnnotationsContext( Compilation compilation ) {
			Statics = (
				Audited: GetAttr( compilation, "D2L.CodeStyle.Annotations.Statics+Audited" ),
				Unaudited: GetAttr( compilation, "D2L.CodeStyle.Annotations.Statics+Unaudited" )
			);
			Objects = (
				Immutable: GetAttr( compilation, "D2L.CodeStyle.Annotations.Objects+Immutable" ),
				ImmutableBaseClass: GetAttr( compilation, "D2L.CodeStyle.Annotations.Objects+ImmutableBaseClassAttribute" ),
				ConditionallyImmutable: GetAttr( compilation, "D2L.CodeStyle.Annotations.Objects+ConditionallyImmutable" ),
				OnlyIf: GetAttr( compilation, "D2L.CodeStyle.Annotations.Objects+ConditionallyImmutable+OnlyIf" )
			);
			Mutability = (
				Audited: GetAttr( compilation, "D2L.CodeStyle.Annotations.Mutability+AuditedAttribute" ),
				Unaudited: GetAttr( compilation, "D2L.CodeStyle.Annotations.Mutability+UnauditedAttribute" )
			);
		}

		internal (Attr Audited, Attr Unaudited) Statics { get; }
		internal (Attr Immutable, Attr ImmutableBaseClass, Attr ConditionallyImmutable, Attr OnlyIf) Objects { get; }
		internal (Attr Audited, Attr Unaudited) Mutability { get; }

		private static Attr GetAttr( Compilation compilation, string metadataName )
			=> new( compilation.GetTypeByMetadataName( metadataName ) );

		internal sealed class Attr {

			public Attr( INamedTypeSymbol symbol ) => Symbol = symbol;

			public INamedTypeSymbol Symbol { get; }

			internal ImmutableArray<AttributeData> GetAll( ISymbol s ) {
				return s.GetAttributes().Where( Matches ).ToImmutableArray();
			}

			internal bool IsDefined( ISymbol s ) {
				return s.GetAttributes().Any( Matches );
			}

			private bool Matches( AttributeData a ) => SymbolEqualityComparer.Default.Equals( a.AttributeClass, Symbol );

		}
	}
}


<|start_filename|>src/D2L.CodeStyle.Annotations/DangerousPropertyUsage/AuditedAttribute.cs<|end_filename|>
﻿using System;

namespace D2L.CodeStyle.Annotations {

	public static partial class DangerousPropertyUsage {

		/// <summary>
		/// Indicates usages of a dangerous property have been declared as safe
		/// </summary>
		[AttributeUsage(
			validOn: AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property,
			AllowMultiple = true,
			Inherited = false
		)]
		public sealed class AuditedAttribute : Attribute {

			/// <summary>
			/// Indicates usages of a dangerous property have been declared as safe
			/// </summary>
			/// <param name="declaringType">The type that declares the dangerous property.</param>
			/// <param name="propertyName">The name of the dangerous property.</param>
			/// <param name="owner">The user who last reviewed this property usage.</param>
			/// <param name="auditedDate">The last time this property usage was reviewed.</param>
			/// <param name="rationale">A brief explaination of why this property usage is safe.</param>
			public AuditedAttribute(
					Type declaringType,
					string propertyName,
					string owner,
					string auditedDate,
					string rationale
				) {

				DeclaringType = declaringType;
				PropertyName = propertyName;
				Owner = owner;
				AuditedDate = auditedDate;
				Rationale = rationale;
			}

			public Type DeclaringType { get; }
			public string PropertyName { get; }
			public string Owner { get; }
			public string AuditedDate { get; }
			public string Rationale { get; }
		}
	}
}


<|start_filename|>src/D2L.CodeStyle.Analyzers/Immutability/ImmutableTypeKind.cs<|end_filename|>
﻿using System;

namespace D2L.CodeStyle.Analyzers.Immutability {
	/// <summary>
	/// Our "immutability" is a property of values. However, we can also
	/// generalize this to types as a whole to varying degrees.
	/// </summary>
	[Flags]
	internal enum ImmutableTypeKind {
		/// <summary>
		/// We have nothing to say about the immutability of this type.
		/// </summary>
		None = 0,

		/// <summary>
		/// A type T has immutable instances if "new T( ... )" always produces
		/// an immutable value.
		/// </summary>
		Instance = 1,


		/// <summary>
		/// A type T is totally immutable if all objects of type T are
		/// immutable. This is a stronger condition than instance immutability.
		/// </summary>
		Total = 2 | Instance,
	}
}


<|start_filename|>src/D2L.CodeStyle.Analyzers/ApiUsage/DependencyInjection/Domain/FullyGenericRegisterExpression.cs<|end_filename|>
﻿using System.Collections.Immutable;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;

namespace D2L.CodeStyle.Analyzers.ApiUsage.DependencyInjection.Domain {
	// void Register<TDependencyType, TConcreteType>( ObjectScope scope )
	// void RegisterPlugin<TDependencyType, TConcreteType>( ObjectScope scope )
	// void RegisterFactory<TDependencyType, TFactoryType>( ObjectScope scope )
	// void RegisterPluginFactory<TDependencyType, TFactoryType>( ObjectScope scope )
	internal sealed class FullyGenericRegisterExpression : DependencyRegistrationExpression {

		private static readonly ImmutableHashSet<string> s_validNames = ImmutableHashSet.Create(
			"Register",
			"RegisterPlugin",
			"RegisterFactory",
			"RegisterPluginFactory"
		);

		internal override bool CanHandleMethod( IMethodSymbol method ) {
			return s_validNames.Contains( method.Name )
				&& method.IsGenericMethod 
				&& method.TypeArguments.Length == 2 
				&& method.Parameters.Length == 1;
		}

		internal override DependencyRegistration GetRegistration( IMethodSymbol method, SeparatedSyntaxList<ArgumentSyntax> arguments, SemanticModel semanticModel ) {
			if( arguments.Count != 1 ) {
				return null;
			}

			if( method.Name.Contains( "Factory" ) ) {
				return GetFactoryRegistration( method, arguments, semanticModel );
			}

			return GetNonFactoryRegistration( method, arguments, semanticModel );
		}

		private DependencyRegistration GetNonFactoryRegistration( IMethodSymbol method, SeparatedSyntaxList<ArgumentSyntax> arguments, SemanticModel semanticModel ) {
			ObjectScope scope;
			if( !TryGetObjectScope( arguments[0], semanticModel, out scope ) ) {
				return null;
			}

			return DependencyRegistration.NonFactory( 
				scope, 
				method.TypeArguments[0], 
				method.TypeArguments[1] 
			);
		}

		private DependencyRegistration GetFactoryRegistration( IMethodSymbol method, SeparatedSyntaxList<ArgumentSyntax> arguments, SemanticModel semanticModel ) {
			ObjectScope scope;
			if( !TryGetObjectScope( arguments[0], semanticModel, out scope ) ) {
				return null;
			}

			ITypeSymbol concreteType = GetConstructedTypeOfIFactory(
				semanticModel,
				method.TypeArguments[0],
				method.TypeArguments[1]
			);

			return DependencyRegistration.Factory(
				scope,
				dependencyType: method.TypeArguments[0],
				factoryType: method.TypeArguments[1],
				concreteType: concreteType
			);
		}
	}
}


<|start_filename|>tests/D2L.CodeStyle.Analyzers.Test/Specs/EventTypesAnalyzer.cs<|end_filename|>
﻿
// analyzer: D2L.CodeStyle.Analyzers.ApiUsage.Events.EventTypesAnalyzer

namespace D2L.LP.Distributed.Events.Domain {

	using System;

	[AttributeUsage( validOn: AttributeTargets.Class, AllowMultiple = false, Inherited = false )]
	public sealed class EventAttribute : Attribute { }
}

namespace Tests {

	using System;
	using D2L.LP.Distributed.Events.Domain;
	using static D2L.CodeStyle.Annotations.Objects;

	[Event]
	public sealed class/* EventTypeMissingImmutableAttribute(Tests.MutableEvent) */ MutableEvent /**/{ }

	[Event]
	[Immutable]
	public abstract class/* EventTypeNotSealed(Tests.AbstractEvent) */ AbstractEvent /**/{ }

	[Event]
	[Immutable]
	public class/* EventTypeNotSealed(Tests.NonSealedEvent) */ NonSealedEvent /**/{ }

	[Event]
	[Immutable]
	public sealed class ImmutableEvent { }

	[System.Diagnostics.Tracing.Event]
	public sealed class WrongEventAttribute { }

	[Wacky.Event]
	public sealed class InvalidAttributeType { }
}


<|start_filename|>src/D2L.CodeStyle.Analyzers/Diagnostics.cs<|end_filename|>
﻿using Microsoft.CodeAnalysis;

namespace D2L.CodeStyle.Analyzers {
	public static class Diagnostics {
		// Retired:
		// D2L0002 (UnsafeStatic): "Ensure that static field is safe in undifferentiated servers."
		// D2L0003 (ImmutableClassIsnt): "Classes marked as immutable should be immutable.",

		public static readonly DiagnosticDescriptor RpcContextFirstArgument = new DiagnosticDescriptor(
			id: "D2L0004",
			title: "RPCs must take an IRpcContext, IRpcPostContext or IRpcPostContextBase as their first argument",
			messageFormat: "RPCs must take an IRpcContext, IRpcPostContext or IRpcPostContextBase as their first argument",
			category: "Correctness",
			defaultSeverity: DiagnosticSeverity.Error,
			isEnabledByDefault: true,
			description: "RPCs must take an IRpcContext, IRpcPostContext or IRpcPostContextBase as their first argument"
		);

		public static readonly DiagnosticDescriptor RpcArgumentSortOrder = new DiagnosticDescriptor(
			id: "D2L0005",
			title: "Dependency-injected arguments in RPC methods must preceed other parameters (other than the first context argument)",
			messageFormat: "Dependency-injected arguments in RPC methods must preceed other parameters (other than the first context argument)",
			category: "Correctness",
			defaultSeverity: DiagnosticSeverity.Error,
			isEnabledByDefault: true,
			description: "Dependency-injected arguments in RPC methods must preceed other parameters (other than the first context argument)"
		);

		public static readonly DiagnosticDescriptor UnsafeSingletonRegistration = new DiagnosticDescriptor(
			id: "D2L0006",
			title: "Ensure that a singleton is safe in undifferentiated servers.",
			messageFormat: "The type '{0}' is not safe to register as a singleton, because it is not marked with [Immutable].",
			category: "Safety",
			defaultSeverity: DiagnosticSeverity.Info,
			isEnabledByDefault: true,
			description: "Singletons should be marked with the [Immutable] attribute."
		);

		// Retired:
		// D2L0007 (UnnecessaryStaticAnnotation): "Unnecessary static annotations should be removed to keep the code base clean",
		// D2L0008 (ConflictingStaticAnnotation): "Statics.Audited and Statics.Unaudited are mutually exclusive",

		public static readonly DiagnosticDescriptor OldAndBrokenLocatorIsObsolete = new DiagnosticDescriptor(
			id: "D2L0009",
			title: "OldAndBrokenServiceLocator should be avoided.  Use dependency injection instead.",
			messageFormat: "OldAndBrokenServiceLocator should be avoided.  Use dependency injection instead.",
			category: "Correctness",
			defaultSeverity: DiagnosticSeverity.Error,
			isEnabledByDefault: true,
			description: "OldAndBrokenServiceLocator should be avoided.  Use dependency injection instead."
		);

		public static readonly DiagnosticDescriptor NullPassedToNotNullParameter = new DiagnosticDescriptor(
			id: "D2L0010",
			title: "Parameter cannot be passed with a null value.",
			messageFormat: "Parameter \"{0}\" cannot be passed a null value",
			category: "Safety",
			defaultSeverity: DiagnosticSeverity.Error,
			isEnabledByDefault: true,
			description: "The method being called has declared that this parameter cannot receive null, but a null value is being passed."
		);

		public static readonly DiagnosticDescriptor SingletonRegistrationTypeUnknown = new DiagnosticDescriptor(
			id: "D2L0011",
			title: "Unable to resolve the concrete or plugin type for this registration.",
			messageFormat: "Unable to determine the concrete or plugin type for this registration; please make sure to reference the type's assembly.",
			category: "Safety",
			defaultSeverity: DiagnosticSeverity.Error,
			isEnabledByDefault: true,
			description: "Singleton registrations must be known at compile-time; please make sure to reference the type's assembly."
		);

		public static readonly DiagnosticDescriptor RegistrationKindUnknown = new DiagnosticDescriptor(
			id: "D2L0012",
			title: "Unable to determine the kind of dependency registration attempted.",
			messageFormat: "The attempted DI registration is not known to our analysis or there was an error analyzing it. This is mostly likely because the ObjectScope is being passed as a variable, or this is a new kind of registration and needs to be handled.",
			category: "Safety",
			defaultSeverity: DiagnosticSeverity.Error,
			isEnabledByDefault: true,
			description: "All DI registrations must be known to static analyzers, to allow for thorough analysis."
		);

		public static readonly DiagnosticDescriptor ClassShouldBeSealed = new DiagnosticDescriptor(
			id: "D2L0013",
			title: "Non-public class should be sealed because it doesn't have any subtypes.",
			messageFormat: "Non-public class should be sealed because it doesn't have any subtypes.",
			category: "Style",
			defaultSeverity: DiagnosticSeverity.Info,
			isEnabledByDefault: true,
			description: "Non-public class should be sealed because it doesn't have any subtypes."
		);

		// Retired:
		// D2L0014 (SingletonIsntImmutable): "Classes marked as a singleton should be immutable.",

		public static readonly DiagnosticDescriptor SingletonLocatorMisuse = new DiagnosticDescriptor(
			id: "D2L0017",
			title: "Can only use OldAndBrokenSingletonLocator to inject interfaces with the [Singleton] attribute",
			messageFormat: "Cannot use OldAndBrokenSingletonLocator to inject {0} because it lacks the [Singleton] attribute",
			category: "Correctness",
			defaultSeverity: DiagnosticSeverity.Error,
			isEnabledByDefault: true,
			description: "Can only use OldAndBrokenSingletonLocator to inject interfaces with the [Singleton] attribute"
		);

		public static readonly DiagnosticDescriptor DangerousMethodsShouldBeAvoided = new DiagnosticDescriptor(
			id: "D2L0018",
			title: "Avoid using dangerous methods",
			messageFormat: "Should not use {0} because it's considered dangerous",
			category: "Safety",
			defaultSeverity: DiagnosticSeverity.Error,
			isEnabledByDefault: true,
			description: "Avoid using of dangerous methods"
		);

		public static readonly DiagnosticDescriptor AttributeRegistrationMismatch = new DiagnosticDescriptor(
			id: "D2L0019",
			title: "Singleton attribute cannot appear on non-singleton object scopes.",
			messageFormat: "The type '{0}' is marked as [Singleton] but is registered with a conflicting object scope.",
			category: "Safety",
			defaultSeverity: DiagnosticSeverity.Error,
			isEnabledByDefault: true,
			description: "Non-Singletons should not be marked with the [Singleton] attribute."
		);

		public static readonly DiagnosticDescriptor InvalidLaunchDarklyFeatureDefinition = new DiagnosticDescriptor(
			id: "D2L0021",
			title: "Launch Darkly feature definitions are limited to support types",
			messageFormat: "Invalid feature flag value type: {0}",
			category: "Correctness",
			defaultSeverity: DiagnosticSeverity.Error,
			isEnabledByDefault: true,
			description: "Must be one of: bool, int, float, string"
		);

		public static readonly DiagnosticDescriptor ObsoleteJsonParamBinder = new DiagnosticDescriptor(
			id: "D2L0025",
			title: "Use the new JsonConvertParameterBinder instead",
			messageFormat: "Should not use JsonParamBinder because it is obsolete",
			category: "Correctness",
			defaultSeverity: DiagnosticSeverity.Error,
			isEnabledByDefault: true,
			description: "JsonParamBinder uses the custom D2L JSON framework, so use JsonConvertParameterBinder (which uses Newtonsoft.Json) instead."
		);

		// Retired:
		// D2L0026 (ImmutableGenericAttributeInWrongAssembly): "Cannot apply ImmutableGeneric for the given type.",
		// D2L0027 (ImmutableGenericAttributeAppliedToNonGenericType): "Cannot apply ImmutableGeneric for a non-generic type.",
		// D2L0028 (ImmutableGenericAttributeAppliedToOpenGenericType): "Cannot apply ImmutableGeneric for an open generic type.",

		public static readonly DiagnosticDescriptor DontUseImmutableArrayConstructor = new DiagnosticDescriptor(
			id: "D2L0029",
			title: "Don't use the default constructor for ImmutableArray<T>",
			messageFormat: "The default constructor for ImmutableArray<T> doesn't correctly initialize the object and leads to runtime errors. Use ImmutableArray<T>.Empty for empty arrays, ImmutableArray.Create() for simple cases and ImmutableArray.Builder<T> for more complicated cases.",
			category: "Correctness",
			defaultSeverity: DiagnosticSeverity.Error,
			isEnabledByDefault: true,
			description: "The default constructor for ImmutableArray<T> doesn't correctly initialize the object and leads to runtime errors. Use ImmutableArray<T>.Empty for empty arrays, ImmutableArray.Create() for simple cases and ImmutableArray.Builder<T> for more complicated cases."
		);

		public static readonly DiagnosticDescriptor UnnecessaryMutabilityAnnotation = new DiagnosticDescriptor(
			id: "D2L0030",
			title: "Unnecessary Mutability.(Un)Audited Attribute",
			messageFormat: "There is a Mutability.Audited or Mutability.Unaudited attribute on an immutable member. Remove the unnecessary attribute.",
			category: "Cleanliness",
			defaultSeverity: DiagnosticSeverity.Error,
			isEnabledByDefault: true
		);

		public static readonly DiagnosticDescriptor TooManyUnnamedArgs = new DiagnosticDescriptor(
			id: "D2L0032",
			title: "Name arguments for readability.",
			messageFormat: "There are more than {0} unnamed arguments. Use named arguments for readability.",
			category: "Readability",
			defaultSeverity: DiagnosticSeverity.Error,
			isEnabledByDefault: true,
			description: "When a lot of arguments are used, they should be named for readability."
		);

		public static readonly DiagnosticDescriptor LiteralArgShouldBeNamed = new DiagnosticDescriptor(
			id: "D2L0033",
			title: "Literal arguments should be named for readability.",
			messageFormat: "The argument for the {0} parameter is a literal expression. It's often hard to tell what the parameter for the argument is at the call-site in this case. Please use a named argument for readability.",
			category: "Readability",
			defaultSeverity: DiagnosticSeverity.Info, // TODO: Change to Error once fixed in LMS
			isEnabledByDefault: true,
			description: "Literal arguments should be named for readability."
		);

		public static readonly DiagnosticDescriptor ArgumentsWithInterchangableTypesShouldBeNamed = new DiagnosticDescriptor(
			id: "D2L0034",
			title: "Arguments that map to parameters with interchangable types should be named.",
			messageFormat: "The parameters {0} and {1} have interchangable types. There is a risk of not passing arguments to the right parameters. Please use named arguments for readability.",
			category: "Readability",
			defaultSeverity: DiagnosticSeverity.Info,
			isEnabledByDefault: true,
			description: "Arguments that map to parameters with interchangable types should be named."
		);

		// Retired:
		// D2L0035 (SingletonDependencyHasCustomerState): "Singleton holding a dependency containing customer state.",
		// D2L0036 (PublicClassHasHiddenCustomerState): "Missing CustomerState attribute."",
		// D2L0037 (GenericArgumentImmutableMustBeApplied): "Missing immutability attribute."

		// Retired:
		// D2L0038 (GenericArgumentTypeMustBeImmutable): "Declared type is not immutable.",

		public static readonly DiagnosticDescriptor DangerousPropertiesShouldBeAvoided = new DiagnosticDescriptor(
			id: "D2L0039",
			title: "Avoid using dangerous properties",
			messageFormat: "Should not use {0} because it's considered dangerous",
			category: "Safety",
			defaultSeverity: DiagnosticSeverity.Error,
			isEnabledByDefault: true,
			description: "Avoid using of dangerous properties"
		);

		public static readonly DiagnosticDescriptor MissingTransitiveImmutableAttribute = new DiagnosticDescriptor(
			id: "D2L0040",
			title: "Missing an explicit transitive [Immutable] attribute",
			messageFormat: "{0} should be [Immutable]{1} because the {2} {3} is.",
			category: "",
			defaultSeverity: DiagnosticSeverity.Error,
			isEnabledByDefault: true,
			description: "The implications of [Immutable] apply transitively to derived classes and interface implementations. We require that [Immutable] is explicity applied transitively for clarity and simplicity."
		);

		public static readonly DiagnosticDescriptor EventHandlerDisallowed = new DiagnosticDescriptor(
			id: "D2L0041",
			title: "Disallowed Event Handler",
			messageFormat: "Event handlers of type {0} have been disallowed",
			category: "Safety",
			defaultSeverity: DiagnosticSeverity.Error,
			isEnabledByDefault: true,
			description: "This event type no longer supports event handlers."
		);

		public static readonly DiagnosticDescriptor MustReferenceAnnotations = new DiagnosticDescriptor(
			id: "D2L0042",
			title: "To use D2L.CodeStyle.Analyzers you must also reference the assembly D2L.CodeStyle.Annotations",
			messageFormat: "To use D2L.CodeStyle.Analyzers you must also reference the assembly D2L.CodeStyle.Annotations",
			category: "Build",
			defaultSeverity: DiagnosticSeverity.Error,
			isEnabledByDefault: true,
			description: "To use D2L.CodeStyle.Analyzers you must also reference the assembly D2L.CodeStyle.Annotations"
		);

		public static readonly DiagnosticDescriptor EventTypeMissingEventAttribute = new DiagnosticDescriptor(
			id: "D2L0043",
			title: "Event Type Missing [Event] Attribute",
			description: "All event types must be marked with [Event] attribute.",
			messageFormat: "Event type {0} must be marked with [Event] attribute.",
			category: "Correctness",
			defaultSeverity: DiagnosticSeverity.Error,
			isEnabledByDefault: true
		);

		public static readonly DiagnosticDescriptor EventHandlerTypeMissingEventAttribute = new DiagnosticDescriptor(
			id: "D2L0044",
			title: "Event Handler Type Missing [EventHandler] Attribute",
			description: "All event handler types must be marked with [EventHandler] attribute.",
			messageFormat: "Event handler type {0} must be marked with [EventHandler] attribute.",
			category: "Correctness",
			defaultSeverity: DiagnosticSeverity.Error,
			isEnabledByDefault: true
		);

		public static readonly DiagnosticDescriptor EventTypeMissingImmutableAttribute = new DiagnosticDescriptor(
			id: "D2L0045",
			title: "Event Type Missing [Immutable] Attribute",
			messageFormat: "{0} must be marked [Immutable] because all event types must be immutable.",
			category: "Correctness",
			defaultSeverity: DiagnosticSeverity.Error,
			isEnabledByDefault: true,
			description: "We require that [Immutable] be explicity applied to all event types."
		);

    public static readonly DiagnosticDescriptor DependencyRegistraionMissingPublicConstructor = new DiagnosticDescriptor(
			id: "D2L0046",
			title: "Dependency Registration Missing Public Constructor",
			messageFormat: "{0} must have a public constructor if it is to be registered for DI.",
			category: "Correctness",
			defaultSeverity: DiagnosticSeverity.Error,
			isEnabledByDefault: true,
			description: "All injectable types need a public constructor in order to be activated."
    );

		public static readonly DiagnosticDescriptor IncludeDefaultValueInOverrideForReadability = new DiagnosticDescriptor(
			id: "D2L0047",
			title: "The parameter {0} has a default value in {1}, but not here. This causes inconsistent behaviour and reduces readability. Please repeat the default value here explicitly.",
			messageFormat: "The parameter {0} has a default value in {1}, but not here. This causes inconsistent behaviour and reduces readability. Please repeat the default value here explicitly.",
			category: "Language",
			defaultSeverity: DiagnosticSeverity.Error,
			isEnabledByDefault: true,
			description: "The parameter {0} has a default value in {1}, but not here. This causes inconsistent behaviour and reduces readability. Please repeat the default value here explicitly."
		);

		public static readonly DiagnosticDescriptor DontIntroduceNewDefaultValuesInOverrides = new DiagnosticDescriptor(
			id: "D2L0048",
			title: "The parameter {0} does not have a default value in the original version of this method in {1}, but does here. This causes inconsistent behaviour. Please remove the default (or add it everywhere.)",
			messageFormat: "The parameter {0} does not have a default value in the original version of this method in {1}, but does here. This causes inconsistent behaviour. Please remove the default (or add it everywhere.)",
			category: "Language",
			defaultSeverity: DiagnosticSeverity.Error,
			isEnabledByDefault: true,
			description: "The parameter {0} does not have a default value in the original version of this method in {1}, but does here. This causes inconsistent behaviour. Please remove the default (or add it everywhere.)"
		);

		public static readonly DiagnosticDescriptor DefaultValuesInOverridesShouldBeConsistent = new DiagnosticDescriptor(
			id: "D2L0049",
			title: "The parameter {0} has a default value of {1} here, but {2} in its original definition in {3}. This causes inconsistent behaviour. Please use the same defualt value everywhere.",
			messageFormat: "The parameter {0} has a default value of {1} here, but {2} in its original definition in {3}. This causes inconsistent behaviour. Please use the same defualt value everywhere.",
			category: "Language",
			defaultSeverity: DiagnosticSeverity.Error,
			isEnabledByDefault: true,
			description: "The parameter {0} has a default value of {1} here, but {2} in its original definition in {3}. This causes inconsistent behaviour. Please use the same defualt value everywhere."
		);

		public static readonly DiagnosticDescriptor LoggingContextRunAwaitable = new DiagnosticDescriptor(
			id: "D2L0050",
			title: "Use RunAsync for awaitable actions.",
			messageFormat: "Use RunAsync for awaitable actions.",
			category: "Correctness",
			defaultSeverity: DiagnosticSeverity.Error,
			isEnabledByDefault: true,
			description: "Use RunAsync for awaitable actions."
		);

		public static readonly DiagnosticDescriptor BannedConfig = new DiagnosticDescriptor(
			id: "D2L0051",
			title: "Fetching the config variable \"{0}\" has been deprecated. {1}",
			messageFormat: "Fetching the config variable \"{0}\" has been deprecated. {1}",
			category: "Correctness",
			defaultSeverity: DiagnosticSeverity.Error,
			isEnabledByDefault: true,
			description: "Fetching the config variable \"{0}\" has been deprecated. {1}"
		);

		public static readonly DiagnosticDescriptor ContentPhysicalPathUsages = new DiagnosticDescriptor(
			id: "D2L0052",
			title: "Use the ContentPath property.",
			messageFormat: "Use the ContentPath property instead.",
			category: "Storageable",
			defaultSeverity: DiagnosticSeverity.Error,
			isEnabledByDefault: true,
			description: "Use the ContentPath property instead."
		);

		public static readonly DiagnosticDescriptor StatelessFuncIsnt = new DiagnosticDescriptor(
			id: "D2L0053",
			title: "StatelessFunc cannot hold state",
			messageFormat: "StatelessFunc cannot hold state: {0}",
			category: "Safety",
			defaultSeverity: DiagnosticSeverity.Error,
			isEnabledByDefault: true,
			description: "StatelessFunc is to be used to hold Func private members, and need to be undiff safe."
		);

		public static readonly DiagnosticDescriptor ReadOnlyParameterIsnt = new DiagnosticDescriptor(
			id: "D2L0054",
			title: "Parameter is not readonly",
			messageFormat: "Parameter is not readonly: {0}",
			category: "Safety",
			defaultSeverity: DiagnosticSeverity.Error,
			isEnabledByDefault: true,
			description: "[ReadOnly] paramaters must not be assigned to or passed by non-readonly reference."
		);

		public static readonly DiagnosticDescriptor AwaitedTaskNotConfigured = new DiagnosticDescriptor(
			id: "D2L0055",
			title: "Awaited task is not configured",
			messageFormat: "Awaited task is not configured",
			category: "Correctness",
			defaultSeverity: DiagnosticSeverity.Error,
			isEnabledByDefault: true,
			description: "Awaited task should have 'continueOnCapturedContext' configured (preferably with 'false')."
		);

		public static readonly DiagnosticDescriptor StructShouldBeReadonly = new DiagnosticDescriptor(
			id: "D2L0056",
			title: "Struct should be readonly",
			messageFormat: "Struct '{0}' should be marked as readonly",
			description: "A struct without mutable fields or properties should be marked readonly. A non-readonly struct other weird mutability behaviours and performance pitfalls.",
			category: "Correctness",
			defaultSeverity: DiagnosticSeverity.Error,
			isEnabledByDefault: true
		);

		public static readonly DiagnosticDescriptor UnnecessaryAllowedListEntry = new DiagnosticDescriptor(
			id: "D2L0057",
			title: "Unnecessarily listed in an analyzer allowed list",
			messageFormat: "The entry for '{0}' in '{1}' is unnecessary.",
			category: "Cleanliness",
			defaultSeverity: DiagnosticSeverity.Error,
			isEnabledByDefault: true,
			description: "Unnecessarily listed in an analyzer allowed list."
		 );

		public static readonly DiagnosticDescriptor NamedArgumentsRequired = new DiagnosticDescriptor(
			id: "D2L0058",
			title: "Named arguments are required for readability.",
			messageFormat: "Named arguments are required for this call. Use named arguments for readability.",
			category: "Readability",
			defaultSeverity: DiagnosticSeverity.Error,
			isEnabledByDefault: true,
			description: "This call requires arguments be named for readability."
		);

		public static readonly DiagnosticDescriptor InvalidSerializerType = new DiagnosticDescriptor(
			id: "D2L0059",
			title: "Serializers must implement D2L.LP.Serialization.ITrySerializer.",
			messageFormat: "'{0}' does not implement D2L.LP.Serialization.ITrySerializer.",
			category: "Correctness",
			defaultSeverity: DiagnosticSeverity.Error,
			isEnabledByDefault: true,
			description: "This [Serializer] attribute requires the provided type to implement D2L.LP.Serialization.ITrySerializer."
		);

		public static readonly DiagnosticDescriptor EventTypeNotSealed = new DiagnosticDescriptor(
			id: "D2L0060",
			title: "Event Type Not Sealed",
			messageFormat: "{0} must be sealed because you can register event handlers based on inheritance.",
			category: "Correctness",
			defaultSeverity: DiagnosticSeverity.Error,
			isEnabledByDefault: true,
			description: "We require that event types be sealed."
		);

		public static readonly DiagnosticDescriptor MutableBaseType = new DiagnosticDescriptor(
			id: "D2L0061",
			title: "Base class for immutable type must be [Immutable] (or [ImmutableBaseClass])",
			messageFormat: "{0}'s base class is {1} which is missing [Immutable] (or, more weakly, [ImmutableBaseClass]).",
			category: "Immutability",
			defaultSeverity: DiagnosticSeverity.Error,
			isEnabledByDefault: true
		);

		public static readonly DiagnosticDescriptor ArraysAreMutable = new DiagnosticDescriptor(
			id: "D2L0062",
			title: "Arrays are mutable and thus can't be held by an immutable type.",
			messageFormat: "{0}[]'s are mutable even if {0}'s are immutable because array items can be substituted. Prefer an immutable container type such as ImmutableArray<{0}>.",
			category: "Immutability",
			defaultSeverity: DiagnosticSeverity.Error,
			isEnabledByDefault: true
		);

		public static readonly DiagnosticDescriptor DynamicObjectsAreMutable = new DiagnosticDescriptor(
			id: "D2L0063",
			title: "Dynamic objects are mutable and thus can't be held by an immutable type.",
			messageFormat: "Dynamic objects are mutable and thus can't be held by an immutable type.",
			category: "Immutability",
			defaultSeverity: DiagnosticSeverity.Error,
			isEnabledByDefault: true
		);

		public static readonly DiagnosticDescriptor TypeParameterIsNotKnownToBeImmutable = new DiagnosticDescriptor(
			id: "D2L0064",
			title: "Type parameter is not known to be immutable.",
			messageFormat: "The type parameter {0} is not known to be immutable. Add [Immutable] to it.",
			category: "Immutability",
			defaultSeverity: DiagnosticSeverity.Error,
			isEnabledByDefault: true
		);

		public static readonly DiagnosticDescriptor UnexpectedTypeKind = new DiagnosticDescriptor(
			id: "D2L0065",
			title: "Unsupported type kind inside an immutable type declaration.",
			messageFormat: "The type kind {0} is unexpected/unhandled by the immutability analyzer, and for safety we are emitting an error. This is a bug in the analyzer.",
			category: "Immutability",
			defaultSeverity: DiagnosticSeverity.Error,
			isEnabledByDefault: true,
			helpLinkUri: "https://github.com/Brightspace/D2L.CodeStyle/issues/new?title=Unexpected%20type%20kind%20in%20immutability%20analysis&labels=bug"
		);

		public static readonly DiagnosticDescriptor NonImmutableTypeHeldByImmutable = new DiagnosticDescriptor(
			id: "D2L0066",
			title: "Type may be mutable.",
			messageFormat: "The {0} \"{1}\" is missing the [Immutable]{2} attribute, so it isn't safe to be held by an immutable type",
			category: "Immutability",
			defaultSeverity: DiagnosticSeverity.Error,
			isEnabledByDefault: true
		);

		public static readonly DiagnosticDescriptor MemberIsNotReadOnly = new DiagnosticDescriptor(
			id: "D2L0067",
			title: "Member must be readonly inside immutable type.",
			messageFormat: "The {0} {1} must be readonly because {2} is immutable.",
			category: "Immutability",
			defaultSeverity: DiagnosticSeverity.Error,
			isEnabledByDefault: true
		);

		public static readonly DiagnosticDescriptor UnexpectedMemberKind = new DiagnosticDescriptor(
			id: "D2L0068",
			title: "Unsupported member type in immutable type declaration.",
			messageFormat: "The member {0} is of type {1} which is not supported by the immutability analyzer. This is a bug.",
			category: "Immutability",
			defaultSeverity: DiagnosticSeverity.Error,
			isEnabledByDefault: true,
			helpLinkUri: "https://github.com/Brightspace/D2L.CodeStyle/issues/new?title=Unexpected%20member%20kind%20in%20immutability%20analysis&labels=bug"
		);

		public static readonly DiagnosticDescriptor DelegateTypesPossiblyMutable = new DiagnosticDescriptor(
			id: "D2L0069",
			title: "Delegates members are not allowed in immutable types.",
			messageFormat: "Delegate fields and properties can close over mutable state. For safety, we don't allow these in immutable types.",
			category: "Immutability",
			defaultSeverity: DiagnosticSeverity.Error,
			isEnabledByDefault: true
		);

		public static readonly DiagnosticDescriptor EventMemberMutable = new DiagnosticDescriptor(
			id: "D2L0070",
			title: "Event members are not allowed in immtuable types.",
			messageFormat: "Event members hold registered event handlers. We consider this to be a source of mutability.",
			category: "Immutability",
			defaultSeverity: DiagnosticSeverity.Error,
			isEnabledByDefault: true
		);

		public static readonly DiagnosticDescriptor UnexpectedConditionalImmutability = new DiagnosticDescriptor(
			id: "D2L0071",
			title: "The [ConditionallyImmutable.OnlyIf] attribute cannot be used with methods.",
			messageFormat: "The [ConditionallyImmutable.OnlyIf] attribute is only valid on parameters of named types.",
			category: "Immutability",
			defaultSeverity: DiagnosticSeverity.Error,
			isEnabledByDefault: true
		);

		public static readonly DiagnosticDescriptor ConflictingImmutability = new DiagnosticDescriptor(
			id: "D2L0072",
			title: "Conflicting immutability attributes.",
			messageFormat: "The [{0}] and [{1}] attributes cannot be used on the same {2}.",
			category: "Immutability",
			defaultSeverity: DiagnosticSeverity.Error,
			isEnabledByDefault: true
		);

		public static readonly DiagnosticDescriptor InvalidAuditType = new DiagnosticDescriptor(
			id: "D2L0073",
			title: "Wrong type of auditing was used.",
			messageFormat: "A {0} {1} should be audited using the [{2}] attributes.",
			category: "Immutability",
			defaultSeverity: DiagnosticSeverity.Error,
			isEnabledByDefault: true
		);

		public static readonly DiagnosticDescriptor NonConstantPassedToConstantParameter = new DiagnosticDescriptor(
			id: "D2L0074",
			title: "Constant parameter cannot be passed a non-constant value.",
			messageFormat: "The \"{0}\" parameter is marked with the [Constant] attribute, and so it must be passed a compile-time constant value.",
			category: "Safety",
			defaultSeverity: DiagnosticSeverity.Error,
			isEnabledByDefault: true,
			description: "The method being called has declared that this parameter must receive a constant, but a non-constant value is being passed."
		);

		public static readonly DiagnosticDescriptor InvalidConstantType = new DiagnosticDescriptor(
			id: "D2L0075",
			title: "Invalid data type marked as [Constant].",
			messageFormat: "The [Constant] attribute cannot be used on \"{0}\" types, because they cannot be compile-time constants.",
			category: "Correctness",
			defaultSeverity: DiagnosticSeverity.Error,
			isEnabledByDefault: true
		);

		public static readonly DiagnosticDescriptor InconsistentMethodAttributeApplication = new DiagnosticDescriptor(
			id: "D2L0076",
			title: "Attribute is applied inconsistently to method definitions.",
			messageFormat: "The [{0}] attribute is applied inconsistently between \"{1}\" and its source definition \"{2}\". This wiil cause inconsistent behaviour.",
			category: "Correctness",
			defaultSeverity: DiagnosticSeverity.Error,
			isEnabledByDefault: true
		);

		public static readonly DiagnosticDescriptor AnonymousFunctionsMayCaptureMutability = new DiagnosticDescriptor(
			id: "D2L0077",
			title: "Anonymous functions may capture mutable state and thus can't be assigned to members of an immutable type.",
			messageFormat: "Anonymous functions may capture mutable state and thus can't be assigned to members of an immutable type. Try making the function static, or moving the assignment to an initializer.",
			category: "Immutability",
			defaultSeverity: DiagnosticSeverity.Error,
			isEnabledByDefault: true
		);

		public static readonly DiagnosticDescriptor RpcContextMarkedDependency = new DiagnosticDescriptor(
			id: "D2L0078",
			title: "RPC context argument should not be marked [Dependency]",
			messageFormat: "RPC context argument should not be marked [Dependency]",
			category: "Correctness",
			defaultSeverity: DiagnosticSeverity.Error,
			isEnabledByDefault: true
		);

		public static readonly DiagnosticDescriptor UnknownImmutabilityAssignmentKind = new DiagnosticDescriptor(
			id: "D2L0079",
			title: "ImmutabilityAnalyzer does not understand this assignment expression",
			messageFormat: "ImmutabilityAnalyzer does not understand this assignment expression due to: {0}.",
			category: "Correctness",
			defaultSeverity: DiagnosticSeverity.Error,
			isEnabledByDefault: true,
			description: "The analyzer does not understand the result of this assignment, and as such cannot determine if it is safe."
		);
	}
}


<|start_filename|>tests/D2L.CodeStyle.Analyzers.Test/TestBase.cs<|end_filename|>
﻿using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using NUnit.Framework;
using System.Collections.Immutable;
using System.Linq;
using D2L.CodeStyle.Analyzers.Test.Verifiers;

namespace D2L.CodeStyle.Analyzers {

	internal sealed class TestSymbol<T> where T : ISymbol {
		internal readonly T Symbol;
		internal readonly Compilation Compilation;
		public TestSymbol( T symbol, Compilation compilation ) {
			Symbol = symbol;
			Compilation = compilation;
		}
	}

	internal static class RoslynSymbolFactory {

		internal static string TestAssemblyName = DiagnosticVerifier.TestProjectName;
		internal static string RootNamespace = "D2L";
		internal static string RootClass = "Fake";

		internal static CSharpCompilation Compile( string source ) {
			var tree = CSharpSyntaxTree.ParseText( source );
			var compilation = CSharpCompilation.Create(
				assemblyName: TestAssemblyName,
				syntaxTrees: new[] { tree },
				references: new[] {
					MetadataReference.CreateFromFile( typeof( object ).Assembly.Location ),
					MetadataReference.CreateFromFile( typeof( ImmutableArray ).Assembly.Location )
				}
			);
			return compilation;
		}

		internal static TestSymbol<ITypeSymbol> Type( string text ) {
			var source = $"using System; namespace {RootNamespace} {{ {text} }}";
			var compilation = Compile( source );

			var toReturn = compilation.GetSymbolsWithName(
				predicate: n => true,
				filter: SymbolFilter.Type
			).OfType<ITypeSymbol>().FirstOrDefault();
			Assert.IsNotNull( toReturn );
			Assert.AreNotEqual( TypeKind.Error, toReturn.TypeKind );

			return new TestSymbol<ITypeSymbol>( toReturn, compilation );
		}

		internal static TestSymbol<IFieldSymbol> Field( string text ) {
			var type = Type( "sealed class " + RootClass + " { " + text + "; }" );

			var toReturn = type.Symbol.GetMembers().OfType<IFieldSymbol>().FirstOrDefault();
			Assert.IsNotNull( toReturn );
			Assert.AreNotEqual( TypeKind.Error, toReturn.Type.TypeKind );

			return new TestSymbol<IFieldSymbol>( toReturn, type.Compilation );
		}

		internal static TestSymbol<IPropertySymbol> Property( string text ) {
			var type = Type( "sealed class " + RootClass + " { " + text + "; }" );

			var toReturn = type.Symbol.GetMembers().OfType<IPropertySymbol>().FirstOrDefault();
			Assert.IsNotNull( toReturn );
			Assert.AreNotEqual( TypeKind.Error, toReturn.Type.TypeKind );

			return new TestSymbol<IPropertySymbol>( toReturn, type.Compilation );
		}
	}

}


<|start_filename|>src/D2L.CodeStyle.Analyzers/Helpers/AllowedTypeList.cs<|end_filename|>
﻿using System;
using System.Collections.Immutable;
using System.IO;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Text;

namespace D2L.CodeStyle.Analyzers.Helpers {

	internal sealed class AllowedTypeList {

		public static AllowedTypeList CreateFromAnalyzerOptions(
			string allowedListFileName,
			AnalyzerOptions analyzerOptions
		) {
			return new AllowedTypeList(
				allowedListFileName: allowedListFileName,
				allowedList: LoadAllowedList( allowedListFileName, analyzerOptions.AdditionalFiles )
			);
		}

		private readonly string m_allowedListFileName;
		private readonly ImmutableHashSet<string> m_allowedList;

		private AllowedTypeList(
			string allowedListFileName,
			ImmutableHashSet<string> allowedList
		) {
			m_allowedListFileName = allowedListFileName;
			m_allowedList = allowedList;
		}

		public bool Contains( INamedTypeSymbol entry ) {
			string entryString = FormatEntry( entry );
			bool contains = m_allowedList.Contains( entryString );
			return contains;
		}

		public delegate void ReportDiagnostic( Diagnostic diagnostic );
		public void ReportEntryAsUnnecesary(
			INamedTypeSymbol entry,
			Location location,
			ReportDiagnostic report
		) {
			report( Diagnostic.Create(
				descriptor: Diagnostics.UnnecessaryAllowedListEntry,
				location: location,
				FormatEntry( entry ),
				m_allowedListFileName
			) );
		}

		private static string FormatEntry( INamedTypeSymbol entry ) {
			return $"{ entry.ToString() }, { entry.ContainingAssembly.ToDisplayString( SymbolDisplayFormat.MinimallyQualifiedFormat ) }";
		}

		private static ImmutableHashSet<string> LoadAllowedList(
			string allowedListFileName,
			ImmutableArray<AdditionalText> additionalFiles
		) {
			ImmutableHashSet<string>.Builder allowedList = ImmutableHashSet.CreateBuilder(
				StringComparer.Ordinal
			);

			AdditionalText allowedListFile = additionalFiles.FirstOrDefault(
				file => Path.GetFileName( file.Path ) == allowedListFileName
			);

			if( allowedListFile == null ) {
				return allowedList.ToImmutableHashSet();
			}

			SourceText allowedListText = allowedListFile.GetText();

			foreach( TextLine line in allowedListText.Lines ) {
				allowedList.Add( line.ToString().Trim() );
			}

			return allowedList.ToImmutableHashSet();
		}

	}
}


<|start_filename|>src/D2L.CodeStyle.Analyzers/ApiUsage/DependencyInjection/Domain/MarkInterfaceAsPluginExpresssion.cs<|end_filename|>
﻿using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;

namespace D2L.CodeStyle.Analyzers.ApiUsage.DependencyInjection.Domain {
	// void MarkInterfaceAsPlugin<TDepdendencyType>( this IDependencyRegistry registry, ObjectScope scope )
	//	where TDepdendencyType : class
	internal sealed class MarkInterfaceAsPluginExpression : DependencyRegistrationExpression {
		internal override bool CanHandleMethod( IMethodSymbol method ) {
			return method.Name == "MarkInterfaceAsPlugin"
				&& method.IsGenericMethod
				&& method.TypeArguments.Length == 1
				&& method.Parameters.Length == 1;
		}

		internal override DependencyRegistration GetRegistration( IMethodSymbol method, SeparatedSyntaxList<ArgumentSyntax> arguments, SemanticModel semanticModel ) {
			if( arguments.Count != 1 ) {
				return null;
			}

			ObjectScope scope;
			if( !TryGetObjectScope( arguments[ 0 ], semanticModel, out scope ) ) {
				return null;
			}
			return DependencyRegistration.Marker(
				scope: scope,
				dependencyType: method.TypeArguments[ 0 ]
			);
		}
	}
}


<|start_filename|>tests/D2L.CodeStyle.Analyzers.Test/Specs/RpcAnalyzer.NoDependencyAttribute.cs<|end_filename|>
﻿// analyzer: D2L.CodeStyle.Analyzers.ApiUsage.RpcAnalyzer

using System;
namespace D2L.Web {
	public interface IRpcContext { }
	public interface IRpcPostContext { }
	public class RpcAttribute : Attribute { }
}

namespace D2L.Web.RequestContext {
	public interface IRpcPostContextBase { }
}

namespace D2L {
	class WeirdAttribute : Attribute { }
	class Examples {
		// This shouldn't crash even though DependencyAttribute isn't defined
		[D2L.Web.Rpc]
		public static void BasicRpc(
			D2L.Web.IRpcContext context,
			[Weird] int x
		) { }
	}
}


<|start_filename|>src/D2L.CodeStyle.TestAnalyzers/ServiceLocator/CustomTestServiceLocatorAnalyzer.cs<|end_filename|>
﻿using System.Collections.Immutable;
using System.IO;
using System.Linq;
using D2L.CodeStyle.TestAnalyzers.Common;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Text;

namespace D2L.CodeStyle.TestAnalyzers.ServiceLocator {

	[DiagnosticAnalyzer( LanguageNames.CSharp )]
	public sealed class CustomTestServiceLocatorAnalyzer : DiagnosticAnalyzer {

		private const string TestServiceLocatorFactoryType
			= "D2L.LP.Extensibility.Activation.Domain.TestServiceLocatorFactory";

		private const string AllowedListFileName = "CustomTestServiceLocatorAllowedList.txt";

		public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
			=> ImmutableArray.Create( Diagnostics.CustomServiceLocator, Diagnostics.UnnecessaryAllowedListEntry );

		private readonly bool _excludeKnownProblems;

		public CustomTestServiceLocatorAnalyzer() : this( true ) { }

		public CustomTestServiceLocatorAnalyzer( bool excludeKnownProblemFixtures ) {
			_excludeKnownProblems = excludeKnownProblemFixtures;
		}

		public override void Initialize( AnalysisContext context ) {
			context.EnableConcurrentExecution();
			context.ConfigureGeneratedCodeAnalysis( GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics );
			context.RegisterCompilationStartAction(
				RegisterServiceLocatorAnalyzer
			);
		}

		public void RegisterServiceLocatorAnalyzer(
			CompilationStartAnalysisContext context
		) {
			INamedTypeSymbol factoryType = context.Compilation
				.GetTypeByMetadataName( TestServiceLocatorFactoryType );

			if( factoryType == null || factoryType.Kind == SymbolKind.ErrorType ) {
				return;
			}

			ImmutableHashSet<string> allowedClasses = GetAllowedList(
				context.Options.AdditionalFiles
			);

			context.RegisterSyntaxNodeAction(
				ctx => PreventCustomLocatorUsage(
					ctx,
					factoryType,
					allowedClasses
				),
				SyntaxKind.InvocationExpression
			);

			context.RegisterSymbolAction(
				ctx => PreventUnnecessaryAllowedListing(
					ctx,
					factoryType,
					allowedClasses
				),
				SymbolKind.NamedType
			);
		}

		// Prevent static usage of TestServiceLocator.Create() methods.
		private void PreventCustomLocatorUsage(
			SyntaxNodeAnalysisContext context,
			INamedTypeSymbol disallowedType,
			ImmutableHashSet<string> allowedClasses
		) {
			if( !( context.Node is InvocationExpressionSyntax invocationExpression ) ) {
				return;
			}

			if( !IsTestServiceLocatorFactoryCreate(
				context.SemanticModel,
				disallowedType,
				invocationExpression
			) ) {
				return;
			}

			// Check whether any parent classes are on our allowed list. Checking
			// all of the parents lets us handle partial classes more easily.
			var parentClasses = context.Node.Ancestors().Where(
				a => a.IsKind( SyntaxKind.ClassDeclaration )
			);

			var parentSymbols = parentClasses.Select(
				c => context.SemanticModel.GetDeclaredSymbol( c )
			);

			bool isAllowed = parentSymbols.Any(
				s => IsClassAllowed( allowedClasses, s )
			);

			if( isAllowed ) {
				return;
			}

			context.ReportDiagnostic(
				Diagnostic.Create(
					Diagnostics.CustomServiceLocator,
					context.Node.GetLocation()
				)
			);
		}

		private void PreventUnnecessaryAllowedListing(
			SymbolAnalysisContext context,
			INamedTypeSymbol factoryType,
			ImmutableHashSet<string> allowedClasses
		) {
			if( !( context.Symbol is INamedTypeSymbol namedType ) ) {
				return;
			}

			if( !IsClassAllowed( allowedClasses, namedType ) ) {
				return;
			}

			Location diagnosticLocation = null;
			foreach( var syntaxRef in namedType.DeclaringSyntaxReferences ) {
				var syntax = syntaxRef.GetSyntax( context.CancellationToken );

				diagnosticLocation = diagnosticLocation ?? syntax.GetLocation();

				SemanticModel model = context.Compilation.GetSemanticModel( syntax.SyntaxTree );

				var testServiceLocatorFactoryCreates = syntax
					.DescendantNodes()
					.OfType<InvocationExpressionSyntax>()
					.Where( i => IsTestServiceLocatorFactoryCreate(
						model,
						factoryType,
						i
					) );

				if( testServiceLocatorFactoryCreates.Any() ) {
					return;
				}
			}

			if( diagnosticLocation != null ) {
				context.ReportDiagnostic(
					Diagnostic.Create(
						Diagnostics.UnnecessaryAllowedListEntry,
						diagnosticLocation,
						GetAllowedListName( namedType ), AllowedListFileName
					)
				);
			}
		}

		private static bool IsTestServiceLocatorFactoryCreate(
			SemanticModel model,
			INamedTypeSymbol factoryType,
			InvocationExpressionSyntax invocationExpression
		) {
			SymbolInfo symbolInfo = model.GetSymbolInfo( invocationExpression );

			IMethodSymbol method = symbolInfo.Symbol as IMethodSymbol;

			if( method == null ) {
				if( symbolInfo.CandidateSymbols == null ) {
					return false;
				}

				if( symbolInfo.CandidateSymbols.Length != 1 ) {
					return false;
				}

				// This happens on method groups, such as
				// Func<IServiceLocator> fooFunc = TestServiceLocatorFactory.Create( ... );
				method = symbolInfo.CandidateSymbols.First() as IMethodSymbol;

				if( method == null ) {
					return false;
				}
			}

			// If we're a Create method on a class that isn't
			// TestServiceLocatorFactory, we're safe.
			if( !IsTestServiceLocatorFactory(
					actualType: method.ContainingType,
					disallowedType: factoryType
				)
			) {
				return false;
			}

			// If we're not a Create method, we're safe.
			if( !IsCreateMethod( method ) ) {
				return false;
			}

			return true;
		}

		private static bool IsTestServiceLocatorFactory(
			INamedTypeSymbol actualType,
			INamedTypeSymbol disallowedType
		) {
			if( actualType == null ) {
				return false;
			}

			bool isLocatorFactory = actualType.Equals( disallowedType, SymbolEqualityComparer.Default );
			return isLocatorFactory;
		}

		private static bool IsCreateMethod(
			IMethodSymbol method
		) {
			return method.Name.Equals( "Create" )
				&& method.Parameters.Length > 0;
		}

		private bool IsClassAllowed(
			ImmutableHashSet<string> allowedClasses,
			ISymbol classSymbol
		) {
			bool isAllowed = _excludeKnownProblems
				&& allowedClasses.Contains( GetAllowedListName( classSymbol ) );

			return isAllowed;
		}

		private static string GetAllowedListName( ISymbol classSymbol ) =>
			classSymbol.ToString()
			+ ", "
			+ classSymbol.ContainingAssembly.ToDisplayString( SymbolDisplayFormat.MinimallyQualifiedFormat )
		;

		private static ImmutableHashSet<string> GetAllowedList(
			ImmutableArray<AdditionalText> additionalFiles
		) {
			ImmutableHashSet<string>.Builder allowedClasses = ImmutableHashSet.CreateBuilder<string>();

			AdditionalText allowedListFile = additionalFiles.FirstOrDefault(
				file => Path.GetFileName( file.Path ) == AllowedListFileName
			);

			if( allowedListFile == null ) {
				return allowedClasses.ToImmutableHashSet();
			}

			SourceText allowedListText = allowedListFile.GetText();

			foreach( TextLine line in allowedListText.Lines ) {
				allowedClasses.Add( line.ToString().Trim() );
			}

			return allowedClasses.ToImmutableHashSet();
		}

	}
}


<|start_filename|>src/D2L.CodeStyle.Analyzers/ApiUsage/DependencyInjection/Domain/RegisterPluginForExtensionPointExpression.cs<|end_filename|>
﻿using System;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;

namespace D2L.CodeStyle.Analyzers.ApiUsage.DependencyInjection.Domain {
	// 	public static void RegisterPlugin<TExtensionPoint, TDependencyType, TConcreteType>(
	//			this IDependencyRegistry @this,
	//			ObjectScope scope
	//		)
	//		where TConcreteType : TDependencyType
	//		where TExtensionPoint : IExtensionPoint<TDependencyType>;
	//	public static void RegisterPluginFactory<TExtensionPoint, TDependencyType, TFactoryType>(
	//			this IDependencyRegistry @this,
	//			ObjectScope scope
	//		)
	//		where TFactoryType : IFactory<TDependencyType>
	//		where TExtensionPoint : IExtensionPoint<TDependencyType>;
	internal sealed class RegisterPluginForExtensionPointExpression : DependencyRegistrationExpression {
		internal override bool CanHandleMethod( IMethodSymbol method ) {
			return ( method.Name == "RegisterPlugin" || method.Name == "RegisterPluginFactory" )
				&& method.IsExtensionMethod
				&& method.Parameters.Length == 1
				&& method.TypeArguments.Length == 3;
		}

		internal override DependencyRegistration GetRegistration( IMethodSymbol method, SeparatedSyntaxList<ArgumentSyntax> arguments, SemanticModel semanticModel ) {
			if( arguments.Count != 1 ) {
				return null;
			}

			ObjectScope scope;
			if( !TryGetObjectScope( arguments[0], semanticModel, out scope ) ) {
				return null;
			}

			if( method.Name.Contains( "Factory" ) ) {

				ITypeSymbol concreteType = GetConstructedTypeOfIFactory(
					semanticModel,
					method.TypeArguments[0],
					method.TypeArguments[1]
				);

				return DependencyRegistration.Factory( 
					scope, 
					method.TypeArguments[1], 
					method.TypeArguments[2],
					concreteType
				);
			} else {
				return DependencyRegistration.NonFactory( 
					scope, 
					method.TypeArguments[1], 
					method.TypeArguments[2] 
				);
			}
		}
	}
}


<|start_filename|>src/D2L.CodeStyle.Annotations/DangerousPropertyUsage/UnauditedAttribute.cs<|end_filename|>
﻿using System;

namespace D2L.CodeStyle.Annotations {

	public static partial class DangerousPropertyUsage {

		/// <summary>
		/// Indicates usages of a dangerous property are still unaudited
		/// </summary>
		[AttributeUsage(
			validOn: AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property,
			AllowMultiple = true,
			Inherited = false
		)]
		public sealed class UnauditedAttribute : Attribute {

			/// <summary>
			/// Indicates usages of a dangerous property are still unaudited
			/// </summary>
			/// <param name="declaringType">The type that declares the dangerous property.</param>
			/// <param name="propertyName">The name of the dangerous property.</param>
			public UnauditedAttribute(
					Type declaringType,
					string propertyName
				) {

				DeclaringType = declaringType;
				PropertyName = propertyName;
			}

			public Type DeclaringType { get; }
			public string PropertyName { get; }
		}
	}
}


<|start_filename|>src/D2L.CodeStyle.TestAnalyzers/Extensions/Microsoft.CodeAnalysis.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.CodeAnalysis;

namespace D2L.CodeStyle.TestAnalyzers.Extensions {
	public static class RoslynExtensions {

		// Copied from the non-test assembly because we do not reference it.

		public static bool IsNullOrErrorType( this ITypeSymbol symbol ) {

			if( symbol == null ) {
				return true;
			}

			if( symbol.Kind == SymbolKind.ErrorType ) {
				return true;
			}

			if( symbol.TypeKind == TypeKind.Error ) {
				return true;
			}

			return false;
		}

		public static bool IsNullOrErrorType( this ISymbol symbol ) {

			if( symbol == null ) {
				return true;
			}

			if( symbol.Kind == SymbolKind.ErrorType ) {
				return true;
			}

			return false;
		}

	}
}


<|start_filename|>tests/D2L.CodeStyle.Analyzers.Test/Specs/SerializerAttributeAnalyzer.cs<|end_filename|>
﻿// analyzer: D2L.CodeStyle.Analyzers.ApiUsage.Serialization.SerializerAttributeAnalyzer

using System;
using D2L.LP.Serialization;

namespace D2L.LP.Serialization {
	public sealed class SerializerAttribute : Attribute {
		public SerializableAttribute( Type type ) { }
	}
	public interface ISerializer { }
	public interface ITrySerializer { }
}

namespace SpecTests {

	[Serializer( typeof( Nested.TrySerializer ) )]
	public sealed class Nested {
		private sealed class TrySerializer : ITrySerializer { }
	}

	[Serializer( typeof( NestedGeneric<>.TrySerializer ) )]
	public sealed class NestedGeneric<T> {
		private sealed class TrySerializer : ITrySerializer { }
	}

	[Serializer( typeof( ExternalSerializer ) )]
	public sealed class External { }
	public sealed class ExternalSerializer : ISerializer { }

	[Serializer( /* InvalidSerializerType(System.String) */ typeof( string ) /**/ )]
	public sealed class InvalidSerializerType { }

	[Serializer( /* InvalidSerializerType(SpecTests.InvalidGenericSerializerType.Comparer) */ typeof( InvalidGenericSerializerType<>.Comparer ) /**/ )]
	public sealed class InvalidGenericSerializerType<T> {
		private sealed class Comparer { }
	}

	[Serializer( /* InvalidSerializerType(null) */ null /**/ )]
	public sealed class NullSerializerType { }

	[Serializer( /* InvalidSerializerType(123) */ 123 /**/ )]
	public sealed class InvalidSerializerTypeArgument { }

	[Serializer]
	public sealed class IncompleteAttribute_WithoutParentheses { }

	[Serializer()]
	public sealed class IncompleteAttribute_WithParentheses { }

	[System.ComponentModel.Category( "Not related" )]
	public sealed class RandomAttribute { }

	[Invalid]
	public sealed class InvalidAttribute { }

	[]
	public sealed class JustSomeBrackets { }
}


<|start_filename|>tests/D2L.CodeStyle.Analyzers.Test/Specs/ConstantAttributeAnalyzer.cs<|end_filename|>
﻿// analyzer: D2L.CodeStyle.Analyzers.ApiUsage.ConstantAttributeAnalyzer

using System;

namespace SpecTests
{
	using D2L.CodeStyle.Annotations.Contract;

	public sealed class Logger {
		public static void Error( [Constant] string message ) { }
	}

	public sealed class WrappedLogger {
		public static void Error( [Constant] string message ) {
			Logger.Error( message );
		}
		public static void OtherError( string message ) {
			Logger.Error( /* NonConstantPassedToConstantParameter(message) */ message /**/ );
		}
	}

	public sealed class Types
	{
		public static void SomeMethodWithConstantParameter<T>( [Constant] T param1 ) { }
		public static void SomeMethodWithParameter<T>( T param1 ) { }
		public static void SomeMethodWithOneConstantParameter<T>( [Constant] T param1, T param2 ) { }
		public static void SomeMethodWithOneOtherConstantParameter<T>( T param1, [Constant] T param2 ) { }
		public static void SomeMethodWithTwoConstantParameters<T>( [Constant] T param1, [Constant] T param2 ) { }

		public interface IInterface { }
		public class SomeClassImplementingInterface : IInterface { }
		public static void SomeMethodWithInterfaceParameter( /* InvalidConstantType(Interface) */ [Constant] IInterface @interface /**/ ) { }
		public static void SomeMethodWithOneInterfaceParameter<T>(IInterface param1, [Constant] T param2) { }
	}

	public sealed class Tests
	{
		void Method()
		{
			#region Invalid type tests
			const Types.SomeClassImplementingInterface interfaceClass = new Types.SomeClassImplementingInterface { };
			Types.SomeMethodWithConstantParameter<Types.IInterface>( /* NonConstantPassedToConstantParameter(param1) */ interfaceClass /**/ );
			Types.SomeMethodWithOneInterfaceParameter<Types.IInterface>( interfaceClass, /* NonConstantPassedToConstantParameter(param2) */ interfaceClass /**/ );
			#endregion

			#region Logger tests
			const string CONSTANT_MESSAGE = "Organization {0} is not constant.";
			int orgId = 0;

			Logger.Error( CONSTANT_MESSAGE );
			Logger.Error( "Organization 1 is constant." );
			Logger.Error( /* NonConstantPassedToConstantParameter(message) */ string.Format(CONSTANT_MESSAGE, orgId) /**/ );
			Logger.Error( /* NonConstantPassedToConstantParameter(message) */ $"Organization {orgId} is not constant." /**/ );
			Logger.Error( /* NonConstantPassedToConstantParameter(message) */ $"Organization 0 is not guaranteed to be constant." /**/ );
			#endregion

			#region String tests
			string variableStr = "This is a variable message";
			const string CONSTANT_STR = "This is a constant message";

			Types.SomeMethodWithConstantParameter<string>( "This is a constant message" );
			Types.SomeMethodWithConstantParameter<string>( CONSTANT_STR );
			Types.SomeMethodWithConstantParameter<string>( CONSTANT_STR + "This is a constant message" );
			Types.SomeMethodWithConstantParameter<string>( /* NonConstantPassedToConstantParameter(param1) */ CONSTANT_STR + variableStr /**/ );
			Types.SomeMethodWithConstantParameter<string>( /* NonConstantPassedToConstantParameter(param1) */ variableStr /**/ );

			Types.SomeMethodWithParameter<string>( "This is a constant message" );
			Types.SomeMethodWithParameter<string>( CONSTANT_STR );
			Types.SomeMethodWithParameter<string>( CONSTANT_STR + "This is a constant message" );
			Types.SomeMethodWithParameter<string>( CONSTANT_STR + variableStr );
			Types.SomeMethodWithParameter<string>( variableStr );

			Types.SomeMethodWithOneConstantParameter<string>( "This is a constant message", "This is a constant message" );
			Types.SomeMethodWithOneConstantParameter<string>( CONSTANT_STR, CONSTANT_STR );
			Types.SomeMethodWithOneConstantParameter<string>( CONSTANT_STR + "This is a constant message", CONSTANT_STR + "This is a constant message" );
			Types.SomeMethodWithOneConstantParameter<string>( CONSTANT_STR, variableStr );
			Types.SomeMethodWithOneConstantParameter<string>( /* NonConstantPassedToConstantParameter(param1) */ variableStr /**/, CONSTANT_STR );
			Types.SomeMethodWithOneConstantParameter<string>( /* NonConstantPassedToConstantParameter(param1) */ variableStr /**/, variableStr );

			Types.SomeMethodWithOneOtherConstantParameter<string>( "This is a constant message", "This is a constant message" );
			Types.SomeMethodWithOneOtherConstantParameter<string>( CONSTANT_STR, CONSTANT_STR );
			Types.SomeMethodWithOneOtherConstantParameter<string>( CONSTANT_STR + "This is a constant message", CONSTANT_STR + "This is a constant message" );
			Types.SomeMethodWithOneOtherConstantParameter<string>( CONSTANT_STR, /* NonConstantPassedToConstantParameter(param2) */ variableStr /**/ );
			Types.SomeMethodWithOneOtherConstantParameter<string>( variableStr, CONSTANT_STR );
			Types.SomeMethodWithOneOtherConstantParameter<string>( variableStr, /* NonConstantPassedToConstantParameter(param2) */ variableStr /**/ );

			Types.SomeMethodWithTwoConstantParameters<string>( "This is a constant message", "This is a constant message" );
			Types.SomeMethodWithTwoConstantParameters<string>( CONSTANT_STR, CONSTANT_STR );
			Types.SomeMethodWithTwoConstantParameters<string>( CONSTANT_STR + "This is a constant message", CONSTANT_STR + "This is a constant message" );
			Types.SomeMethodWithTwoConstantParameters<string>( CONSTANT_STR, /* NonConstantPassedToConstantParameter(param2) */ variableStr /**/ );
			Types.SomeMethodWithTwoConstantParameters<string>( /* NonConstantPassedToConstantParameter(param1) */ variableStr /**/, CONSTANT_STR );
			Types.SomeMethodWithTwoConstantParameters<string>( /* NonConstantPassedToConstantParameter(param1) */ variableStr /**/, /* NonConstantPassedToConstantParameter(param2) */ variableStr /**/ );
			#endregion

			#region Number tests
			int variableInt = 5;
			const int CONSTANT_INT = 29;

			Types.SomeMethodWithConstantParameter<int>( 29 );
			Types.SomeMethodWithConstantParameter<int>( CONSTANT_INT );
			Types.SomeMethodWithConstantParameter<int>( CONSTANT_INT + 29 );
			Types.SomeMethodWithConstantParameter<int>( /* NonConstantPassedToConstantParameter(param1) */ CONSTANT_INT + variableInt /**/ );
			Types.SomeMethodWithConstantParameter<int>( /* NonConstantPassedToConstantParameter(param1) */ variableInt /**/ );

			Types.SomeMethodWithParameter<int>( 29 );
			Types.SomeMethodWithParameter<int>( CONSTANT_INT );
			Types.SomeMethodWithParameter<int>( CONSTANT_INT + 29 );
			Types.SomeMethodWithParameter<int>( CONSTANT_INT + variableInt );
			Types.SomeMethodWithParameter<int>( variableInt );

			Types.SomeMethodWithOneConstantParameter<int>( 29, 29 );
			Types.SomeMethodWithOneConstantParameter<int>( CONSTANT_INT, CONSTANT_INT );
			Types.SomeMethodWithOneConstantParameter<int>( CONSTANT_INT + 29, CONSTANT_INT + 29 );
			Types.SomeMethodWithOneConstantParameter<int>( CONSTANT_INT, variableInt );
			Types.SomeMethodWithOneConstantParameter<int>( /* NonConstantPassedToConstantParameter(param1) */ variableInt /**/, CONSTANT_INT );
			Types.SomeMethodWithOneConstantParameter<int>( /* NonConstantPassedToConstantParameter(param1) */ variableInt /**/, variableInt );

			Types.SomeMethodWithOneOtherConstantParameter<int>( 29, 29 );
			Types.SomeMethodWithOneOtherConstantParameter<int>( CONSTANT_INT, CONSTANT_INT );
			Types.SomeMethodWithOneOtherConstantParameter<int>( CONSTANT_INT + 29, CONSTANT_INT + 29 );
			Types.SomeMethodWithOneOtherConstantParameter<int>( CONSTANT_INT, /* NonConstantPassedToConstantParameter(param2) */ variableInt /**/ );
			Types.SomeMethodWithOneOtherConstantParameter<int>( variableInt, CONSTANT_INT );
			Types.SomeMethodWithOneOtherConstantParameter<int>( variableInt, /* NonConstantPassedToConstantParameter(param2) */ variableInt /**/ );

			Types.SomeMethodWithTwoConstantParameters<int>( 29, 29 );
			Types.SomeMethodWithTwoConstantParameters<int>( CONSTANT_INT, CONSTANT_INT );
			Types.SomeMethodWithTwoConstantParameters<int>( CONSTANT_INT + 29, CONSTANT_INT + 29 );
			Types.SomeMethodWithTwoConstantParameters<int>( CONSTANT_INT, /* NonConstantPassedToConstantParameter(param2) */ variableInt /**/ );
			Types.SomeMethodWithTwoConstantParameters<int>( /* NonConstantPassedToConstantParameter(param1) */ variableInt /**/, CONSTANT_INT );
			Types.SomeMethodWithTwoConstantParameters<int>( /* NonConstantPassedToConstantParameter(param1) */ variableInt /**/, /* NonConstantPassedToConstantParameter(param2) */ variableInt /**/ );
			#endregion

			#region Boolean tests
			bool variableBool = true;
			const bool CONSTANT_BOOL = false;

			Types.SomeMethodWithConstantParameter<bool>( false );
			Types.SomeMethodWithConstantParameter<bool>( CONSTANT_BOOL );
			Types.SomeMethodWithConstantParameter<bool>( CONSTANT_BOOL || false );
			Types.SomeMethodWithConstantParameter<bool>( /* NonConstantPassedToConstantParameter(param1) */ CONSTANT_BOOL || variableBool /**/ );
			Types.SomeMethodWithConstantParameter<bool>( /* NonConstantPassedToConstantParameter(param1) */ variableBool /**/ );

			Types.SomeMethodWithParameter<bool>( false );
			Types.SomeMethodWithParameter<bool>( CONSTANT_BOOL );
			Types.SomeMethodWithParameter<bool>( CONSTANT_BOOL || false );
			Types.SomeMethodWithParameter<bool>( CONSTANT_BOOL || variableBool );
			Types.SomeMethodWithParameter<bool>( variableBool );

			Types.SomeMethodWithOneConstantParameter<bool>( false, false );
			Types.SomeMethodWithOneConstantParameter<bool>( CONSTANT_BOOL, CONSTANT_BOOL );
			Types.SomeMethodWithOneConstantParameter<bool>( CONSTANT_BOOL || false, CONSTANT_BOOL || false );
			Types.SomeMethodWithOneConstantParameter<bool>( CONSTANT_BOOL, variableBool );
			Types.SomeMethodWithOneConstantParameter<bool>( /* NonConstantPassedToConstantParameter(param1) */ variableBool /**/, CONSTANT_BOOL );
			Types.SomeMethodWithOneConstantParameter<bool>( /* NonConstantPassedToConstantParameter(param1) */ variableBool /**/, variableBool );

			Types.SomeMethodWithOneOtherConstantParameter<bool>( false, false );
			Types.SomeMethodWithOneOtherConstantParameter<bool>( CONSTANT_BOOL, CONSTANT_BOOL );
			Types.SomeMethodWithOneOtherConstantParameter<bool>( CONSTANT_BOOL || false, CONSTANT_BOOL || false );
			Types.SomeMethodWithOneOtherConstantParameter<bool>( CONSTANT_BOOL, /* NonConstantPassedToConstantParameter(param2) */ variableBool /**/ );
			Types.SomeMethodWithOneOtherConstantParameter<bool>( variableBool, CONSTANT_BOOL );
			Types.SomeMethodWithOneOtherConstantParameter<bool>( variableBool, /* NonConstantPassedToConstantParameter(param2) */ variableBool /**/ );

			Types.SomeMethodWithTwoConstantParameters<bool>( false, false );
			Types.SomeMethodWithTwoConstantParameters<bool>( CONSTANT_BOOL, CONSTANT_BOOL );
			Types.SomeMethodWithTwoConstantParameters<bool>( CONSTANT_BOOL || false, CONSTANT_BOOL || false );
			Types.SomeMethodWithTwoConstantParameters<bool>( CONSTANT_BOOL, /* NonConstantPassedToConstantParameter(param2) */ variableBool /**/ );
			Types.SomeMethodWithTwoConstantParameters<bool>( /* NonConstantPassedToConstantParameter(param1) */ variableBool /**/, CONSTANT_BOOL );
			Types.SomeMethodWithTwoConstantParameters<bool>( /* NonConstantPassedToConstantParameter(param1) */ variableBool /**/, /* NonConstantPassedToConstantParameter(param2) */ variableBool /**/ );
			#endregion
		}
	}
}


<|start_filename|>tests/D2L.CodeStyle.Analyzers.Test/Specs/SingletonLocatorAnalyzer.cs<|end_filename|>
﻿// analyzer: D2L.CodeStyle.Analyzers.ApiUsage.ServiceLocator.SingletonLocatorAnalyzer

using System;
using D2L.CodeStyle.Analyzers.ServiceLocator;
using D2L.LP.Extensibility.Activation.Domain;
using static D2L.LP.Extensibility.Activation.Domain.SingletonLocator;

namespace D2L.LP.Extensibility.Activation.Domain {
	public static class SingletonLocator {
		public static T Get<T>() where T : class {
			return default( T );
		}
	}

	public sealed class SingletonAttribute : Attribute { }

	public interface IPlugins<out T> : System.Collections.Generic.IEnumerable<T> { }
	public interface IPlugins<TExtensionPoint, out T> : System.Collections.Generic.IEnumerable<T> { }

	public interface IPlugins<TExtensionPoint, TOther, out T> : System.Collections.Generic.IEnumerable<T> { }
}

namespace SingletonSpecTests {
	[Singleton]
	public interface IMarkedSingleton { }

	public interface INotMarkedSingleton {
		internal void SomeOtherMethod() { }
	}

	public interface ISomeExtensionPoint { }

	public interface IInterface { }

	public sealed class BadClass {

		public BadClass() { }

		public void UsesSingletonLocatorUnmarked_ViaFunc() {
			Func<INotMarkedSingleton> problemFunc = /* SingletonLocatorMisuse(SingletonSpecTests.INotMarkedSingleton) */ SingletonLocator.Get<INotMarkedSingleton> /**/;
			INotMarkedSingleton loadedIndirectly = problemFunc();
		}

		public void UsesSingletonLocatorUnmarked_ViaLazy() {
			Lazy<INotMarkedSingleton> problemLazy = () => /* SingletonLocatorMisuse(SingletonSpecTests.INotMarkedSingleton) */ SingletonLocator.Get<INotMarkedSingleton> /**/;
			INotMarkedSingleton loadedLazily = problemLazy.Value;
		}

		public void UsesSingletonLocatorUnmarked() {
			INotMarkedSingleton problem = /* SingletonLocatorMisuse(SingletonSpecTests.INotMarkedSingleton) */ SingletonLocator.Get<INotMarkedSingleton>() /**/;
		}

		public void UsesSingletonLocatorMarked() {
			IMarkedSingleton ok = SingletonLocator.Get<IMarkedSingleton>();
		}

		public void UsesOtherMethodOnLocator() {
			string harmless = SingletonLocator.ToString();
		}

		public void UnmarkedLocatorInChain() {
			/* SingletonLocatorMisuse(SingletonSpecTests.INotMarkedSingleton) */ SingletonLocator.Get<INotMarkedSingleton>() /**/.SomeOtherMethod();
		}

		public void UsingStatic() {
			/* SingletonLocatorMisuse(SingletonSpecTests.INotMarkedSingleton) */ Get<INotMarkedSingleton>() /**/;
		}

		public void UsesSingletonLocatorUnmarkedPlugins() {
			INotMarkedSingleton problem = /* SingletonLocatorMisuse(SingletonSpecTests.INotMarkedSingleton) */ SingletonLocator.Get<IPlugins<INotMarkedSingleton>>() /**/;
		}

		public void UsesSingletonLocatorUnmarkedPluginsForExtensionPoint() {
			INotMarkedSingleton problem = /* SingletonLocatorMisuse(SingletonSpecTests.INotMarkedSingleton) */ SingletonLocator.Get<IPlugins< ISomeExtensionPoint, INotMarkedSingleton >>() /**/;
		}

		public void UsesSingletonLocatorUnmarkedUnknownPlugins() {
			INotMarkedSingleton problem = /* SingletonLocatorMisuse(D2L.LP.Extensibility.Activation.Domain.IPlugins) */ SingletonLocator.Get<IPlugins<ISomeExtensionPoint, IInterface, INotMarkedSingleton>>() /**/;
		}

		public void UsesSingletonLocatorMarkedPlugins() {
			IMarkedSingleton ok = SingletonLocator.Get<IPlugins<IMarkedSingleton>>();
		}

		public void UsesSingletonLocatorMarkedPluginsForExtensionPoint() {
			IMarkedSingleton ok = SingletonLocator.Get<IPlugins<ISomeExtensionPoint, IMarkedSingleton>>();
		}

		public void UsesSingletonLocatorMarkedUnknownPlugins() {
			IMarkedSingleton problem = /* SingletonLocatorMisuse(D2L.LP.Extensibility.Activation.Domain.IPlugins) */ SingletonLocator.Get<IPlugins<ISomeExtensionPoint, IInterface, IMarkedSingleton>>() /**/;
		}
	}
}


<|start_filename|>src/D2L.CodeStyle.Analyzers/ApiUsage/RpcAnalyzer.cs<|end_filename|>
﻿using System.Collections.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;

namespace D2L.CodeStyle.Analyzers.ApiUsage {
	[DiagnosticAnalyzer( LanguageNames.CSharp )]
	internal sealed class RpcAnalyzer : DiagnosticAnalyzer {
		public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(
			Diagnostics.RpcContextFirstArgument,
			Diagnostics.RpcArgumentSortOrder,
			Diagnostics.RpcContextMarkedDependency
		);

		public override void Initialize( AnalysisContext context ) {
			context.EnableConcurrentExecution();
			context.ConfigureGeneratedCodeAnalysis( GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics );
			context.RegisterCompilationStartAction( RegisterRpcAnalyzer );
		}

		public static void RegisterRpcAnalyzer( CompilationStartAnalysisContext context ) {
			// Cache some important type lookups
			var rpcAttributeType = context.Compilation.GetTypeByMetadataName( "D2L.Web.RpcAttribute" );
			var rpcContextType = context.Compilation.GetTypeByMetadataName( "D2L.Web.IRpcContext" );
			var rpcPostContextType = context.Compilation.GetTypeByMetadataName( "D2L.Web.IRpcPostContext" );
			var rpcPostContextBaseType = context.Compilation.GetTypeByMetadataName( "D2L.Web.RequestContext.IRpcPostContextBase" );
			var dependencyAttributeType = context.Compilation.GetTypeByMetadataName( "D2L.LP.Extensibility.Activation.Domain.DependencyAttribute" );

			// If any of those type lookups failed then presumably D2L.Web is
			// not referenced and we don't need to register our analyzer.

			if ( rpcAttributeType == null || rpcAttributeType.Kind == SymbolKind.ErrorType ) {
				return;
			}

			if ( rpcContextType == null || rpcContextType.Kind == SymbolKind.ErrorType ) {
				return;
			}

			if ( rpcPostContextType == null || rpcPostContextType.Kind == SymbolKind.ErrorType ) {
				return;
			}

			if ( rpcPostContextBaseType == null || rpcPostContextBaseType.Kind == SymbolKind.ErrorType ) {
				return;
			}

			// intentionally not checking dependencyAttributeType: still want
			// the analyzer to run if that is not in scope.

			context.RegisterSyntaxNodeAction(
				ctx => AnalyzeMethod(
					ctx,
					rpcAttributeType: rpcAttributeType,
					rpcContextType: rpcContextType,
					rpcPostContextType: rpcPostContextType,
					rpcPostContextBaseType: rpcPostContextBaseType,
					dependencyAttributeType: dependencyAttributeType
				),
				SyntaxKind.MethodDeclaration
			);
		}

		private static void AnalyzeMethod(
			SyntaxNodeAnalysisContext context,
			INamedTypeSymbol rpcAttributeType,
			INamedTypeSymbol rpcContextType,
			INamedTypeSymbol rpcPostContextType,
			INamedTypeSymbol rpcPostContextBaseType,
			INamedTypeSymbol dependencyAttributeType
		) {
			var method = context.Node as MethodDeclarationSyntax;

			if ( method == null ) {
				return;
			}

			if ( !CheckThatMethodIsRpc( context, method, rpcAttributeType ) ) {
				return;
			}

			CheckThatFirstArgumentIsIRpcContext(
				context,
				method,
				rpcContextType: rpcContextType,
				rpcPostContextType: rpcPostContextType,
				rpcPostContextBaseType: rpcPostContextBaseType,
				dependencyAttributeType: dependencyAttributeType
			);

			// dependencyAttributeType may be null if that DLL isn't
			// referenced. In that case don't do some of these checks.
			if ( dependencyAttributeType != null ) {
				CheckThatDependencyArgumentsAreSortedCorrectly(
					context,
					method.ParameterList.Parameters,
					dependencyAttributeType
				);

				// other things to check:
				// - appropriate use of [Dependency]
			}
		}

		private static bool CheckThatMethodIsRpc(
			SyntaxNodeAnalysisContext context,
			MethodDeclarationSyntax method,
			INamedTypeSymbol rpcAttributeType
		) {
			bool isRpc = method
				.AttributeLists
				.SelectMany( al => al.Attributes )
				.Any( attr => IsAttribute( rpcAttributeType, attr, context.SemanticModel ) );

			return isRpc;
		}

		private static void CheckThatFirstArgumentIsIRpcContext(
			SyntaxNodeAnalysisContext context,
			MethodDeclarationSyntax method,
			INamedTypeSymbol rpcContextType,
			INamedTypeSymbol rpcPostContextType,
			INamedTypeSymbol rpcPostContextBaseType,
			INamedTypeSymbol dependencyAttributeType
		) {
			var ps = method.ParameterList.Parameters;

			if( ps.Count == 0 ) {
				context.ReportDiagnostic(
					Diagnostic.Create( Diagnostics.RpcContextFirstArgument, method.ParameterList.GetLocation() )
				);
				return;
			}

			var firstParam = method.ParameterList.Parameters[0];
			var firstParamType = context.SemanticModel.GetSymbolInfo( firstParam.Type ).Symbol;

			var firstParamIsReasonableType =
				rpcContextType.Equals( firstParamType, SymbolEqualityComparer.Default ) ||
				rpcPostContextType.Equals( firstParamType, SymbolEqualityComparer.Default ) ||
				rpcPostContextBaseType.Equals( firstParamType, SymbolEqualityComparer.Default );

			if( !firstParamIsReasonableType ) {
				context.ReportDiagnostic(
					Diagnostic.Create( Diagnostics.RpcContextFirstArgument, firstParam.GetLocation() )
				);
			} else if( dependencyAttributeType != null
				&& IsMarkedDependency( dependencyAttributeType, firstParam, context.SemanticModel )
			) {
				context.ReportDiagnostic(
					Diagnostic.Create( Diagnostics.RpcContextMarkedDependency, firstParam.GetLocation() )
				);
			}
		}

		private static void CheckThatDependencyArgumentsAreSortedCorrectly(
			SyntaxNodeAnalysisContext context,
			SeparatedSyntaxList<ParameterSyntax> ps,
			INamedTypeSymbol dependencyAttributeType
		) {
			bool doneDependencies = false;
			foreach( var param in ps.Skip( 1 ) ) {
				var isDep = IsMarkedDependency( dependencyAttributeType, param, context.SemanticModel );

				if( !isDep && !doneDependencies ) {
					doneDependencies = true;
				} else if( isDep && doneDependencies ) {
					context.ReportDiagnostic( Diagnostic.Create( Diagnostics.RpcArgumentSortOrder, param.GetLocation() ) );
				}
			}
		}

		private static bool IsMarkedDependency(
			INamedTypeSymbol dependencyAttributeType,
			ParameterSyntax parameter,
			SemanticModel model
		) => parameter
			.AttributeLists
			.SelectMany( al => al.Attributes )
			.Any( attr => IsAttribute( dependencyAttributeType, attr, model ) );

		private static bool IsAttribute(
			INamedTypeSymbol expectedType,
			AttributeSyntax attr,
			SemanticModel model
		) {
			var attributeConstructorType = model.GetSymbolInfo( attr ).Symbol;

			// Note: symbol corresponds to the constructor for the attribute,
			// so we need to look at symbol.ContainingType
			return expectedType.Equals( attributeConstructorType?.ContainingType, SymbolEqualityComparer.Default );
		}
	}
}


<|start_filename|>tests/D2L.CodeStyle.TestAnalyzers.Tests/ParallelizableTests/ParallelizableTestsAnalyzerTests.cs<|end_filename|>
﻿using System.Collections.Generic;
using System.Collections.Immutable;
using D2L.CodeStyle.TestAnalyzers.Test.Verifiers;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using NUnit.Framework;

namespace D2L.CodeStyle.TestAnalyzers.ParallelizableTests {
	[TestFixture]
	public class ParallelizableTestsAnalyzerTests : DiagnosticVerifier {

		private static readonly MetadataReference NUnitReference = MetadataReference.CreateFromFile( typeof( TestAttribute ).Assembly.Location );
		protected override DiagnosticAnalyzer GetCSharpDiagnosticAnalyzer() {
			return new ParallelizableTestsAnalyzer( new Dictionary<string, string> { { "System.DateTime", "System.DateTime" } }.ToImmutableDictionary() );
		}

		protected override MetadataReference[] GetAdditionalReferences() {
			return new[] { NUnitReference };
		}

		[Test]
		public void EmptyDocument_NoDiag() {
			const string test = @"";

			VerifyCSharpDiagnostic( test );
		}

		[Test]
		public void DocumentWithTest_UsesConstructorOnOffendingType_Diag() {
			const string test = @"
    using System;

    namespace test {
        class Tests {

            public void Test() {
                var time = new DateTime();
            }

        }
    }";
			var expected = new DiagnosticResult {
				Id = ParallelizableTestsAnalyzer.DiagnosticId,
				Message = string.Format( ParallelizableTestsAnalyzer.MessageFormat, "System.DateTime", "System.DateTime" ),
				Severity = DiagnosticSeverity.Warning,
				Locations =
					new[] {
							new DiagnosticResultLocation("Test0.cs", 8, 28)
						}
			};

			VerifyCSharpDiagnostic( test, expected );
		}

		[Test]
		public void DocumentWithTest_UsesMethodOnOffendingType_Diag() {
			const string test = @"
    using System;

    namespace test {
        class Tests {

            public void Test( DateTime d ) {
                var time = d.GetHashCode();
            }

        }
    }";
			var expected = new DiagnosticResult {
				Id = ParallelizableTestsAnalyzer.DiagnosticId,
				Message = string.Format( ParallelizableTestsAnalyzer.MessageFormat, "System.DateTime", "System.DateTime" ),
				Severity = DiagnosticSeverity.Warning,
				Locations =
					new[] {
							new DiagnosticResultLocation("Test0.cs", 8, 28)
						}
			};

			VerifyCSharpDiagnostic( test, expected );
		}

		[Test]
		public void DocumentWithTest_UsesMemberOnOffendingType_Diag() {
			const string test = @"
    using System;

    namespace test {
        class Tests {

            public void Test() {
                var time = DateTime.Now;
            }

        }
    }";
			var expected = new DiagnosticResult {
				Id = ParallelizableTestsAnalyzer.DiagnosticId,
				Message = string.Format( ParallelizableTestsAnalyzer.MessageFormat, "System.DateTime", "System.DateTime" ),
				Severity = DiagnosticSeverity.Warning,
				Locations =
					new[] {
							new DiagnosticResultLocation("Test0.cs", 8, 28)
						}
			};

			VerifyCSharpDiagnostic( test, expected );
		}

	}
}

<|start_filename|>src/D2L.CodeStyle.Analyzers/ApiUsage/LaunchDarkly/FeatureDefinitionAnalyzer.cs<|end_filename|>
﻿using System.Collections.Immutable;
using D2L.CodeStyle.Analyzers.Extensions;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;

namespace D2L.CodeStyle.Analyzers.ApiUsage.LaunchDarkly {
	[DiagnosticAnalyzer( LanguageNames.CSharp )]
	internal sealed class FeatureDefinitionAnalyzer : DiagnosticAnalyzer {

		public const string FeatureDefinitionFullName = "D2L.LP.LaunchDarkly.FeatureDefinition`1";

		private static readonly ImmutableHashSet<string> ValidTypes = ImmutableHashSet.Create<string>(
				"int",
				"bool",
				"string",
				"float"
			);

		public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(
				Diagnostics.InvalidLaunchDarklyFeatureDefinition
			);

		public override void Initialize( AnalysisContext context ) {

			context.EnableConcurrentExecution();
			context.ConfigureGeneratedCodeAnalysis( GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics );
			context.RegisterCompilationStartAction( RegisterAnalysis );
		}

		private void RegisterAnalysis( CompilationStartAnalysisContext context ) {

			Compilation compilation = context.Compilation;

			INamedTypeSymbol featureDefinitionType = compilation.GetTypeByMetadataName( FeatureDefinitionFullName );
			if( featureDefinitionType.IsNullOrErrorType() ) {
				return;
			}

			context.RegisterSyntaxNodeAction(
					c => AnalyzeSimpleBaseType( c, featureDefinitionType ),
					SyntaxKind.SimpleBaseType
				);
		}

		private void AnalyzeSimpleBaseType(
				SyntaxNodeAnalysisContext context,
				INamedTypeSymbol featureDefinitionType
			) {

			SimpleBaseTypeSyntax baseTypeSyntax = (SimpleBaseTypeSyntax)context.Node;
			SymbolInfo baseTypeSymbol = context.SemanticModel.GetSymbolInfo( baseTypeSyntax.Type );

			INamedTypeSymbol baseSymbol = ( baseTypeSymbol.Symbol as INamedTypeSymbol );
			if( baseSymbol.IsNullOrErrorType() ) {
				return;
			}

			ISymbol originalSymbol = baseSymbol.OriginalDefinition;
			if( originalSymbol.IsNullOrErrorType() ) {
				return;
			}

			if( !originalSymbol.Equals( featureDefinitionType, SymbolEqualityComparer.Default ) ) {
				return;
			}

			ISymbol valueTypeSymbol = baseSymbol.TypeArguments[ 0 ];
			if( valueTypeSymbol.IsNullOrErrorType() ) {
				return;
			}

			string valueType = valueTypeSymbol.ToDisplayString();
			if( ValidTypes.Contains( valueType ) ) {
				return;
			}

			Diagnostic diagnostic = Diagnostic.Create(
					Diagnostics.InvalidLaunchDarklyFeatureDefinition,
					baseTypeSyntax.GetLocation(),
					valueType
				);

			context.ReportDiagnostic( diagnostic );
		}
	}
}


<|start_filename|>src/D2L.CodeStyle.Analyzers/Language/StructShouldBeReadonlyAnalyzer.Codefix.cs<|end_filename|>
﻿using System.Collections.Immutable;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;

namespace D2L.CodeStyle.Analyzers.Language {
	partial class StructShouldBeReadonlyAnalyzer {

		[ExportCodeFixProvider(
			LanguageNames.CSharp,
			Name = nameof( AddReadonlyModifierCodefix )
		)]
		public sealed class AddReadonlyModifierCodefix : CodeFixProvider {

			public override ImmutableArray<string> FixableDiagnosticIds
				=> ImmutableArray.Create( Diagnostics.StructShouldBeReadonly.Id );

			public override FixAllProvider GetFixAllProvider()
				=> WellKnownFixAllProviders.BatchFixer;

			public override async Task RegisterCodeFixesAsync(
				CodeFixContext ctx
			) {
				var root = await ctx.Document
					.GetSyntaxRootAsync( ctx.CancellationToken )
					.ConfigureAwait( false );

				foreach( var diagnostic in ctx.Diagnostics ) {
					var span = diagnostic.Location.SourceSpan;

					SyntaxNode syntax = root.FindNode( span, getInnermostNodeForTie: true );
					if( !( syntax is StructDeclarationSyntax declaration ) ) {
						continue;
					}

					ctx.RegisterCodeFix(
						CodeAction.Create(
							title: "Add readonly modifier",
							ct => AddReadonlyModifier(
								doc: ctx.Document,
								root: root,
								declaration: declaration,
								ct: ct
							)
						),
						diagnostic
					);
				}
			}

			private static readonly SyntaxToken READONLY_SYNTAX
				= SyntaxFactory.Token( SyntaxKind.ReadOnlyKeyword );
			private async static Task<Document> AddReadonlyModifier(
				Document doc,
				SyntaxNode root,
				StructDeclarationSyntax declaration,
				CancellationToken ct
			) {
				SyntaxTokenList modifiers = declaration.Modifiers;
				int insertAt = GetModifierInsertLocation( modifiers );
				modifiers = modifiers.Insert( insertAt, READONLY_SYNTAX );

				root = root.ReplaceNode( declaration, declaration.WithModifiers( modifiers ) );
				doc = doc.WithSyntaxRoot( root );
				return doc;
			}

			private static int GetModifierInsertLocation(
				SyntaxTokenList modifiers
			) {
				for( int i = 0; i < modifiers.Count; ++i ) {
					SyntaxToken modifier = modifiers[i];

					switch( modifier.Kind() ) {
						case SyntaxKind.InternalKeyword:
						case SyntaxKind.PrivateKeyword:
						case SyntaxKind.PublicKeyword:
							return i + 1;
						case SyntaxKind.PartialKeyword:
						case SyntaxKind.RefKeyword:
							return i;
					}
				}

				return modifiers.Count;
			}
		}
	}
}

<|start_filename|>src/D2L.CodeStyle.TestAnalyzers/NUnit/TestCaseSourceStringsAnalyzer.CodeFix.cs<|end_filename|>
﻿using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using D2L.CodeStyle.TestAnalyzers.Common;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;

namespace D2L.CodeStyle.TestAnalyzers.NUnit {
	partial class TestCaseSourceStringsAnalyzer {
		[ExportCodeFixProvider( LanguageNames.CSharp )]
		public sealed class CodeFix : CodeFixProvider {

			private const string TITLE = "nameofify";

			public sealed override ImmutableArray<string> FixableDiagnosticIds => ImmutableArray.Create( Diagnostics.TestCaseSourceStrings.Id );

			public override FixAllProvider GetFixAllProvider() {
				return WellKnownFixAllProviders.BatchFixer;
			}

			public sealed override async Task RegisterCodeFixesAsync( CodeFixContext context ) {
				var root = await context.Document.GetSyntaxRootAsync( context.CancellationToken ).ConfigureAwait( false );

				foreach( var diagnostic in context.Diagnostics ) {
					var span = diagnostic.Location.SourceSpan;

					var stringArg = root.FindToken( span.Start ).Parent;

					context.RegisterCodeFix( CodeAction.Create(
						title: TITLE,
						createChangedDocument: cancellationToken => NameofIfyAsync( context.Document, stringArg, cancellationToken )
					), diagnostic);
				}
			}

			private async Task<Document> NameofIfyAsync( Document document, SyntaxNode arg, CancellationToken cancellationToken ) {
				// Get the name of the identifier from the string literal
				var identifier = arg.ToFullString().Trim( ' ', '"' );

				var newNode = SyntaxFactory.InvocationExpression( SyntaxFactory.IdentifierName( "nameof" ) )
					.WithArgumentList(
						SyntaxFactory
							.ArgumentList(
								SyntaxFactory.SingletonSeparatedList( SyntaxFactory.Argument( SyntaxFactory.IdentifierName( SyntaxFactory.Identifier(
									SyntaxFactory.TriviaList(),
									identifier,
									// Add a trailing space after the argument
									SyntaxFactory.TriviaList( SyntaxFactory.Space )
								) ) ) ) )
							// Add a leading space before the argument
							.WithOpenParenToken( SyntaxFactory.Token( SyntaxFactory.TriviaList(), SyntaxKind.OpenParenToken, SyntaxFactory.TriviaList( SyntaxFactory.Space ) ) )
					);

				var oldRoot = await document.GetSyntaxRootAsync( cancellationToken ).ConfigureAwait( false );
				var newRoot = oldRoot.ReplaceNode( arg, newNode );

				return document.WithSyntaxRoot( newRoot );
			}
		}
	}
}


<|start_filename|>src/D2L.CodeStyle.Analyzers/Immutability/ImmutableDefinitionChecker.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using D2L.CodeStyle.Analyzers.Extensions;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;

namespace D2L.CodeStyle.Analyzers.Immutability {
	internal sealed class ImmutableDefinitionChecker {

		internal delegate void DiagnosticSink( Diagnostic diagnostic );

		private readonly Compilation m_compilation;
		private readonly DiagnosticSink m_diagnosticSink;
		private readonly ImmutabilityContext m_context;
		private readonly AnnotationsContext m_annotationsContext;

		public ImmutableDefinitionChecker(
			Compilation compilation,
			DiagnosticSink diagnosticSink,
			ImmutabilityContext context,
			AnnotationsContext annotationsContext
		) {
			m_compilation = compilation;
			m_diagnosticSink = diagnosticSink;
			m_context = context;
			m_annotationsContext = annotationsContext;
		}

		/// <remarks>
		/// Check that a type declaration (class, struct, record) always
		/// produces immutable values.
		/// </remarks>
		public bool CheckDeclaration( INamedTypeSymbol type ) {
			var result = true;

			var members = type.GetMembers()
				// Exclude static members.
				// Immutability is a property of values, [Immutable] (etc.) are
				// judgements about the immutability of values of some type.
				// Static fields/properties are global variables scoped to
				// particular types, but are not a factor in the immutability
				// of the values (instances) of that type.
				.Where( m => !m.IsStatic );

			if( type.TypeKind == TypeKind.Class ) {
				// Check that the base class is immutable for classes
				var baseClassOk = m_context.IsImmutable(
					new ImmutabilityQuery(
						ImmutableTypeKind.Instance,
						type.BaseType
					),
					() => GetLocationOfBaseClass( type ),
					out var diag
				);

				if( !baseClassOk ) {
					m_diagnosticSink( diag );
					result = false;
					// We can keep looking for more errors after this.
				}
			}

			foreach( var member in members ) {
				if( !CheckMember( member ) ) {
					result = false;
				}
			}

			if( !CheckForDestructedAssignmentsInConstructors( type ) ) {
				result = false;
			}

			return result;
		}

		/// <remarks>
		/// Check that a member (e.g. field or property) always produces immutable
		/// values.
		/// </remarks>
		public bool CheckMember( ISymbol member ) {
			if( MutabilityAuditor.CheckAudited(
				m_annotationsContext,
				member,
				m_diagnosticSink,
				out var location
			) ) {
				// If they have one of the auditing attributes, run the
				// checks anyway and error if they are unnecessary
				if( CheckMember( diagnosticSink: _ => { }, member ) ) {

					m_diagnosticSink(
						Diagnostic.Create(
							Diagnostics.UnnecessaryMutabilityAnnotation,
							location
						)
					);
				}

				// Audit annotations means this counts as immutable always
				return true;
			}

			return CheckMember( m_diagnosticSink, member );
		}

		private bool CheckMember( DiagnosticSink diagnosticSink, ISymbol member ) {
			switch( member.Kind ) {
				case SymbolKind.Field:
					return CheckField( diagnosticSink, member as IFieldSymbol );
				case SymbolKind.Property:
					return CheckProperty( diagnosticSink, member as IPropertySymbol );

				// These member types never contribute to mutability:
				case SymbolKind.Method:
				case SymbolKind.NamedType:
					return true;

				case SymbolKind.Event:
					diagnosticSink(
						Diagnostic.Create(
							Diagnostics.EventMemberMutable,
							GetLocationOfMember( member )
						)
					);

					return false;

				// By default raise an alarm (in case we missed something, or
				// if there are new unsupported language features.)
				default:
					m_diagnosticSink(
						Diagnostic.Create(
							Diagnostics.UnexpectedMemberKind,
							GetLocationOfMember( member ),
							member.Name,
							member.Kind
						)
					);

					return false;
			}
		}

		private bool CheckField( DiagnosticSink diagnosticSink, IFieldSymbol field ) {
			if ( field.IsImplicitlyDeclared ) {
				// These correspond to auto-properties. That case gets handled
				// in CheckProperty instead.
				return true;
			}

			var decl = field.DeclaringSyntaxReferences.Single()
				.GetSyntax() as VariableDeclaratorSyntax;

			var type = decl.FirstAncestorOrSelf<VariableDeclarationSyntax>().Type;

			// Get all possible assignments of the field
			var assignments = GetAssignments( field, decl.Initializer?.Value );

			return CheckFieldOrProperty(
				diagnosticSink,
				member: field,
				type: field.Type,
				isReadOnly: field.IsReadOnly,
				typeSyntax: type,
				nameSyntax: decl.Identifier,
				assignments
			);
		}


		private bool CheckProperty( DiagnosticSink diagnosticSink, IPropertySymbol prop ) {
			if ( prop.IsIndexer ) {
				// Indexer properties are just glorified method syntax and
				// don't hold state.
				// https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/indexers/
				return true;
			}

			if ( prop.IsImplicitlyDeclared ) {
				// records have implicitly declared properties like
				// EqualityContract which are OK but don't have a
				// PropertyDeclarationSyntax etc.
				return true;
			}

			var propInfo = GetPropertyStuff(
				prop,
				prop.DeclaringSyntaxReferences.Single().GetSyntax()
			);

			if ( !propInfo.IsAutoImplemented ) {
				// Properties that are auto-implemented have an implicit
				// backing field that may be mutable. Otherwise, properties are
				// just sugar for getter/setter methods and don't themselves
				// contribute to mutability.
				return true;
			}

			// Get all possible assignments of the property
			var assignments = GetAssignments( prop, propInfo.Initializer );

			return CheckFieldOrProperty(
				diagnosticSink,
				member: prop,
				type: prop.Type,
				isReadOnly: propInfo.IsReadOnly,
				typeSyntax: propInfo.TypeSyntax,
				nameSyntax: propInfo.Identifier,
				assignments
			);
		}

		private bool CheckFieldOrProperty(
			DiagnosticSink diagnosticSink,
			ISymbol member,
			ITypeSymbol type,
			bool isReadOnly,
			TypeSyntax typeSyntax,
			SyntaxToken nameSyntax,
			IEnumerable<AssignmentInfo> assignments
		) {
			if ( !isReadOnly ) {
				diagnosticSink(
					Diagnostic.Create(
						Diagnostics.MemberIsNotReadOnly,
						nameSyntax.GetLocation(),
						member.Kind,
						member.Name,
						member.ContainingType.Name
					)
				);

				// Note: we're going to go looking for other errors.
				// There shouldn't be any "return true" below, it should
				// always be conditional on isReadOnly.
			}

			// If our field or property is a type that is always immutable then
			// we can stop looking (all that matters is that we are readonly).
			if( m_context.IsImmutable(
				new ImmutabilityQuery(
					ImmutableTypeKind.Total,
					type
				),
				typeSyntax.GetLocation,
				out var typeDiagnostic
			) ) {
				return isReadOnly;
			}

			// Our field or property could hold mutable values, but it's safe
			// as long as we always assign immutable values to it.

			// Usually that would be difficult, but if we're readonly its a lot
			// easier: we only have to look in our constructors (this applies
			// even to protected readonly; these can't be set in derived
			// constructors.)

			// TODO: we don't really need this, but it has been our current
			// behaviour... if we have a readonly field with no writes that's
			// worth a diagnostic on its own (there are built in ones for that)
			// but technically it doesn't add mutability.
			// If we remove this we can rename typeDiagnostic to _ again.
			if ( !assignments.Any() ) {
				diagnosticSink( typeDiagnostic );
				return false;
			}

			var allAssignmentsAreOfImmutableValues = true;

			foreach( var assignment in assignments ) {
				var kind = GetQueryForAssignment( assignment, out var query, out var diagnostic );

				switch( kind ) {
					case AssignmentQueryKind.NothingToCheck:
						continue;

					case AssignmentQueryKind.Hopeless:
						diagnosticSink( diagnostic );
						allAssignmentsAreOfImmutableValues = false;
						continue;

					case AssignmentQueryKind.ImmutabilityQuery:
						if( m_context.IsImmutable(
							query,
							() => assignment.Expression.GetLocation(),
							out diagnostic
						) ) {
							continue;
						}

						diagnosticSink( diagnostic );

						// We're going to keep looking at all writes to surface as many
						// relevant diagnostics as possible.
						allAssignmentsAreOfImmutableValues = false;
						continue;
				}
			}

			return isReadOnly && allAssignmentsAreOfImmutableValues;
		}

		private bool CheckForDestructedAssignmentsInConstructors( INamedTypeSymbol type ) {
			bool result = true;

			var constructors = type
				.Constructors
				.SelectMany( c => c.DeclaringSyntaxReferences.Select( r => r.GetSyntax() ) );

			foreach( var constructor in constructors ) {
				var assignments = constructor
					.DescendantNodes()
					.OfType<AssignmentExpressionSyntax>();

				foreach( var assignment in assignments ) {
					if( assignment.Left is not TupleExpressionSyntax ) {
						continue;
					}

					m_diagnosticSink( Diagnostic.Create(
						Diagnostics.UnknownImmutabilityAssignmentKind,
						location: assignment.Left.GetLocation(),
						"Deconstructed assignment"
					) );
					result = false;
				}
			}

			return result;
		}

		private readonly struct AssignmentInfo {
			private AssignmentInfo(
				bool isInitializer,
				ExpressionSyntax expression,
				ITypeSymbol assignedType
			) {
				IsInitializer = isInitializer;
				Expression = expression;
				AssignedType = assignedType;
			}

			public bool IsInitializer { get; }
			public ExpressionSyntax Expression { get; }
			public ITypeSymbol AssignedType { get; }

			public static AssignmentInfo Create(
				SemanticModel model,
				bool isInitializer,
				ExpressionSyntax expression
			) {
				TypeInfo typeInfo = model.GetTypeInfo( expression );

				// Type can be null in the case of an implicit conversion where the
				// expression alone doesn't have a type. For example:
				//   int[] foo = { 1, 2, 3 };
				ITypeSymbol assignedType = typeInfo.Type ?? typeInfo.ConvertedType;

				return new AssignmentInfo(
					isInitializer: isInitializer,
					expression: expression,
					assignedType: assignedType
				);
			}
		}


		/// <summary>
		/// A readonly field or property may be re-assigned in constructors.
		/// This method finds all assignments of the field/property.
		/// </summary>
		/// <param name="memberSymbol">The field/property to search for</param>
		/// <param name="initializer">The initialization with the declaration</param>
		/// <returns>An immutable list of assignments</returns>
		private IEnumerable<AssignmentInfo> GetAssignments(
			ISymbol memberSymbol,
			ExpressionSyntax initializer
		) {
			// Retrieve a list of assignment expressions from any constructors
			// within the class
			var assignmentExpressions = ( memberSymbol.ContainingSymbol as INamedTypeSymbol )!
				.Constructors
				.Where( constructorSymbol => !constructorSymbol.IsImplicitlyDeclared )
				.Where( constructorSymbol => constructorSymbol.IsStatic == memberSymbol.IsStatic )
				.Select( constructorSymbol => constructorSymbol.DeclaringSyntaxReferences.Single() )
				.Select( sr => sr.GetSyntax() )
				.SelectMany( constructorSyntax => constructorSyntax.DescendantNodes() )
				.OfType<AssignmentExpressionSyntax>()
				.Where( assignmentSyntax => IsAnAssignmentTo( assignmentSyntax, memberSymbol ) )
				.Select( assignmentSyntax => assignmentSyntax.Right )
				.Select( expr => AssignmentInfo.Create(
					model: m_compilation.GetSemanticModel( expr.SyntaxTree ),
					isInitializer: false,
					expression: expr
				) );

			if ( initializer != null ) {
				assignmentExpressions = assignmentExpressions.Append(
					AssignmentInfo.Create(
						model: m_compilation.GetSemanticModel( initializer.SyntaxTree ),
						isInitializer: true,
						expression: initializer
					)
				);
			}

			return assignmentExpressions;
		}

		private bool IsAnAssignmentTo(
			AssignmentExpressionSyntax assignmentSyntax,
			ISymbol memberSymbol
		) {
			var semanticModel = m_compilation.GetSemanticModel( assignmentSyntax.SyntaxTree );

			var leftSideSymbol = semanticModel.GetSymbolInfo( assignmentSyntax.Left )
				.Symbol;

			return SymbolEqualityComparer.Default.Equals(
				memberSymbol,
				leftSideSymbol
			);
		}

		private enum AssignmentQueryKind {
			NothingToCheck,
			ImmutabilityQuery,
			Hopeless
		}

		/// <summary>
		/// For a field/property assignment, figure out what needs to be checked for it.
		/// </summary>
		/// <param name="assignment">The assignment syntax for the field/property (possibly null)</param>
		/// <param name="query">The query to do (if the return value is AssignmentQueryKind.ImmutabilityQuery.)</param>
		/// <param name="diagnostic">A diagnostic to report (if the return value is AssignmentQueryKind.Hopeless.)</param>
		/// <returns>The details about what to check for this assignment</returns>
		private AssignmentQueryKind GetQueryForAssignment(
			AssignmentInfo assignment,
			out ImmutabilityQuery query,
			out Diagnostic diagnostic
		) {
			// When we have an assignment we use it to narrow our check, e.g.
			//
			//   private readonly object m_lock = new object();
			//
			// is safe even though object (the type of the field) in general is not.

			query = default;
			diagnostic = null;

			// Easy cases
			switch( assignment.Expression.Kind() ) {
				case SyntaxKind.NullLiteralExpression:
					// Sometimes people explicitly (but needlessly) assign null in
					// an initializer... this is safe.
					return AssignmentQueryKind.NothingToCheck;

				case SyntaxKind.SimpleLambdaExpression:
				case SyntaxKind.ParenthesizedLambdaExpression:
					if ( assignment.IsInitializer ) {
						// Lambda initializers for readonly members are safe
						// because they can only close over other members,
						// which will be checked independently, or static
						// members of another class, which are also analyzed.
						return AssignmentQueryKind.NothingToCheck;
					}

					// But for assignments inside a constructor we're in more
					// trouble. They could capture arbitrary state from their
					// lexical scope.

					// static functions can't capture state.
					if ( assignment.Expression.IsStaticFunction() ) {
						return AssignmentQueryKind.NothingToCheck;
					}

					// In general we must panic.
					diagnostic = Diagnostic.Create(
						Diagnostics.AnonymousFunctionsMayCaptureMutability,
						assignment.Expression.GetLocation()
					);

					return AssignmentQueryKind.Hopeless;

				case SyntaxKind.InvocationExpression:

					// Some methods are known to have return values that are
					// immutable (such as Enumerable.Empty()).
					// These should be considered immutable by the Analyzer.
					SemanticModel semanticModel = m_compilation.GetSemanticModel( assignment.Expression.SyntaxTree );
					if( semanticModel
						.GetSymbolInfo( assignment.Expression )
						.Symbol is not IMethodSymbol methodSymbol
					) {
						break;
					}

					if( m_context.IsReturnValueKnownToBeImmutable( methodSymbol ) ) {
						return AssignmentQueryKind.NothingToCheck;
					}

					break;
			}

			// If nothing above was caught, then fallback to querying.

			if( assignment.Expression is BaseObjectCreationExpressionSyntax _ ) {
				// When we have a new T() we don't need to worry about the value
				// being anything other than an instance of T.
				query = new ImmutabilityQuery(
					ImmutableTypeKind.Instance,
					type: assignment.AssignedType
				);
			} else {
				// In general we need to handle subtypes.
				query = new ImmutabilityQuery(
					ImmutableTypeKind.Total,
					type: assignment.AssignedType
				);
			}

			return AssignmentQueryKind.ImmutabilityQuery;
		}

		private static Location GetLocationOfMember( ISymbol s ) =>s
			.DeclaringSyntaxReferences.Single()
			.GetSyntax()
			.FirstAncestorOrSelf<MemberDeclarationSyntax>()
			.GetLocation();

		private Location GetLocationOfBaseClass( ITypeSymbol type ) {
			// Consider the following valid code:
			//
			// class Base {}
			// interface IA { }
			// interface IB { }
			//
			// partial class Foo { }
			// partial class Foo : Base { }
			// partial class Foo : Base, IA { }
			// partial class Foo : IB { }
			//
			// Our goal is to get one of the mentions of Base after "Foo :" to
			// place the diagnostic. There may be multiple, but for valid code
			// it will be the first type in the list for one of the decls.
			// (interfaces always go after the base class, if it is listed.)

			var candidates = type.DeclaringSyntaxReferences
				.Select( r => r.GetSyntax() )
				.Cast<TypeDeclarationSyntax>()
				.Where( r => r.BaseList != null )
				// Take _at most_ the first item from each BaseList.Types
				.SelectMany( r => r.BaseList.Types.Take( 1 ) );

			// Find the first candidate that is a class type.
			foreach( var candidate in candidates ) {
				var model = m_compilation.GetSemanticModel(
					candidate.SyntaxTree
				);

				var candidateInfo = model.GetTypeInfo( candidate.Type );

				if ( candidateInfo.Type == null ) {
					continue;
				}

				if ( candidateInfo.Type.TypeKind == TypeKind.Class ) {
					return candidate.GetLocation();
				}
			}

			// If we couldn't find a candidate just use the first class decl
			// as the diagnostic target. I'm not sure this can happen.
			return type.DeclaringSyntaxReferences.First()
				.GetSyntax()
				.GetLocation();
		}

		/// <summary>
		/// Return info about a property, wether its the classic kind or one
		/// from a concise record declaration.
		/// </summary>
		private (
			TypeSyntax TypeSyntax,
			SyntaxToken Identifier,
			ExpressionSyntax Initializer,
			bool IsAutoImplemented,
			bool IsReadOnly
		) GetPropertyStuff( IPropertySymbol symbol, SyntaxNode syntax ) {
			bool isInitOnly = symbol.SetMethod != null && symbol.SetMethod.IsInitOnly;

			// init-only is as good as readonly for the rest of the analyzer.
			bool isReadOnly = isInitOnly || symbol.IsReadOnly;

			return syntax switch {
				PropertyDeclarationSyntax prop => (
					prop.Type,
					prop.Identifier,

					// Only return the initializer syntax for readonly
					// properties. init-only properties can be overwritten by
					// callers (rather than just constructors) which are well
					// outside the scope of our analysis.
					isInitOnly ? null : prop.Initializer?.Value,

					prop.IsAutoImplemented(),
					isReadOnly
				),
				ParameterSyntax param => (
					param.Type,
					param.Identifier,

					// We never care about initializers for these types of
					// properties because they are always init-only and can
					// be overwritten by any caller.
					Initializer: null,

					IsAutoImplemented: true,
					isReadOnly
				),
				_ => throw new NotImplementedException()
			};
		}
	}
}


<|start_filename|>tests/D2L.CodeStyle.Analyzers.Test/Specs/DependencyRegistrationsAnalyzer.cs<|end_filename|>
﻿// analyzer: D2L.CodeStyle.Analyzers.ApiUsage.DependencyInjection.DependencyRegistrationsAnalyzer

using System;
using System.Collections.Generic;
using D2L.CodeStyle.Annotations;
using D2L.LP.Extensibility.Activation.Domain;

// copied from: http://search.dev.d2l/source/raw/Lms/core/lp/framework/core/D2L.LP.Foundation/LP/Extensibility/Activation/Domain/IDependencyRegistry.cs
// and: http://search.dev.d2l/source/raw/Lms/core/lp/framework/core/D2L.LP.Foundation/LP/Extensibility/Activation/Domain/ObjectScope.cs
namespace D2L.LP.Extensibility.Activation.Domain {
	public sealed class SingletonAttribute : Attribute { }
	public sealed class DependencyAttribute : Attribute { }
	public enum ObjectScope {
		AlwaysCreateNewInstance = 0,
		Singleton = 1,
		WebRequest = 3
	}
	public abstract class ExtensionPointDescriptor {
		public abstract string Name { get; }
	}

	public interface IExtensionPoint<T> { }
	public interface IFactory<out TDependencyType> { }
	public interface IFactory<out TDependencyType, T> { }
	public interface IDependencyRegistry {

		void Register<TDependencyType>(
				TDependencyType instance
			);

		void Register<TDependencyType, TConcreteType>(
				ObjectScope scope
			) where TConcreteType : TDependencyType;

		void Register( Type dependencyType, Type concreteType, ObjectScope scope );

		void RegisterFactory<TDependencyType, TFactoryType>(
				ObjectScope scope
			) where TFactoryType : IFactory<TDependencyType>;

		void RegisterParentAwareFactory<TDependencyType, TFactoryType>()
			where TFactoryType : IFactory<TDependencyType, Type>;

		void RegisterPlugin<TDependencyType>(
				TDependencyType instance
			);

		void RegisterPlugin<TDependencyType, TConcreteType>(
				ObjectScope scope
			) where TConcreteType : TDependencyType;

		void RegisterPluginFactory<TDependencyType, TFactoryType>(
				ObjectScope scope
			) where TFactoryType : IFactory<TDependencyType>;

		void MarkInterfaceAsPlugin<TDependencyType>( ObjectScope scope )
			where TDependencyType : class;

		void UnhandledRegisterMethod();
	}
	public static class ExtensionMethods {
		// from http://search.dev.d2l/source/xref/Lms/core/lp/framework/core/D2L.LP.Foundation/LP/Extensibility/Activation/Domain/DynamicObjectFactories/DynamicObjectFactoryRegistryExtensions.cs
		public static void RegisterDynamicObjectFactory<TOutput, TConcrete, TArg>(
				this IDependencyRegistry registry,
				ObjectScope scope
			) where TConcrete : class, TOutput {
		}

		public static void RegisterDynamicObjectFactory<TOutput, TConcrete, TArg0, TArg1>(
				this IDependencyRegistry registry,
				ObjectScope scope
			) where TConcrete : class, TOutput {
		}

		// from: http://search.dev.d2l/source/xref/Lms/core/lp/framework/core/D2L.LP.Foundation/LP/Extensibility/Activation/Domain/IDependencyRegistryConfigurePluginsExtensions.cs
		public static void ConfigurePlugins<TPlugin>(
				this IDependencyRegistry registry,
				ObjectScope scope
			) {
		}

		public static void ConfigureOrderedPlugins<TPlugin, TComparer>(
				this IDependencyRegistry registry,
				ObjectScope scope
			) where TComparer : IComparer<TPlugin>, new() {
		}

		// from: http://search.dev.d2l/source/xref/Lms/core/lp/framework/core/D2L.LP.Foundation/LP/Extensibility/Activation/Domain/DependencyRegistryExtensionPointExtensions.cs
		public static void RegisterPluginExtensionPoint<TExtensionPoint, T>(
				this IDependencyRegistry @this,
				ObjectScope scope
			) where TExtensionPoint : IExtensionPoint<T> {
		}

		public static void RegisterPlugin<TExtensionPoint, TDependencyType, TConcreteType>(
				this IDependencyRegistry @this,
				ObjectScope scope
			)
			where TConcreteType : TDependencyType
			where TExtensionPoint : IExtensionPoint<TDependencyType> {
		}

		public static void RegisterPluginFactory<TExtensionPoint, TDependencyType, TFactoryType>(
				this IDependencyRegistry @this,
				ObjectScope scope
			)
			where TFactoryType : IFactory<TDependencyType>
			where TExtensionPoint : IExtensionPoint<TDependencyType> {
		}

		// from: http://search.dev.d2l/source/xref/Lms/core/lp/framework/core/D2L.LP/LP/Extensibility/Plugins/DI/LegacyPluginsDependencyLoaderExtensions.cs
		public static void ConfigureInstancePlugins<TPlugin>(
				this IDependencyRegistry registry,
				ObjectScope scope
			) {
		}

		public static void ConfigureInstancePlugins<TPlugin, TExtensionPoint>(
				this IDependencyRegistry registry,
				ObjectScope scope
			) where TExtensionPoint : ExtensionPointDescriptor, new() {
		}

		public static void RegisterSubInterface<TSubInterfaceType, TInjectableSuperInterfaceType>(
				this IDependencyRegistry @this,
				ObjectScope scope
			) where TInjectableSuperInterfaceType : TSubInterfaceType {
		}
	}
}

namespace SpecTests {
	public sealed class SomeTestCases {
		public void DoesntMatter( IDependencyRegistry reg ) {

			//// Marked Singletons are not flagged.
			//reg.Register<ISingleton>( new ImmutableThing() );
			//reg.Register( new ImmutableThing() ); // inferred generic argument of above
			//reg.RegisterPlugin<ISingleton>( new ImmutableThing() );
			//reg.RegisterPlugin( new ImmutableThing() ); // inferred generic argument of above
			//reg.Register<ISingleton, ImmutableThing>( ObjectScope.Singleton );
			//reg.RegisterFactory<ISingleton, ImmutableThingFactory>( ObjectScope.Singleton );
			//reg.RegisterPluginFactory<ISingleton, ImmutableThingFactory>( ObjectScope.Singleton );
			//reg.Register( typeof( ISingleton ), typeof( ImmutableThing ), ObjectScope.Singleton );
			//reg.ConfigurePlugins<ImmutableThing>( ObjectScope.Singleton );
			//reg.ConfigureOrderedPlugins<ImmutableThing, SomeComparer<ImmutableThing>>( ObjectScope.Singleton );
			//reg.ConfigureInstancePlugins<ImmutableThing>( ObjectScope.Singleton );
			//reg.ConfigureInstancePlugins<ImmutableThing, DefaultExtensionPoint<ImmutableThing>>( ObjectScope.Singleton );
			//reg.RegisterPluginExtensionPoint<DefaultExtensionPoint<ImmutableThing>, ImmutableThing>( ObjectScope.Singleton );
			//reg.RegisterPlugin<DefaultExtensionPoint<ISingleton>, ISingleton, ImmutableThing>( ObjectScope.Singleton );
			//reg.RegisterDynamicObjectFactory<ICreatedByDynamicFactory, ThingThatIsCreatedByDynamicObjectFactoryViaImmutableThing, string>( ObjectScope.Singleton );
			//reg.RegisterDynamicObjectFactory<ICreatedByDynamicFactory, ThingThatIsCreatedByDynamicObjectFactoryViaImmutableThing, string, string>( ObjectScope.Singleton );
			//reg.RegisterSubInterface<ISingleton, IImmutableSubSingleton>( ObjectScope.Singleton );

			//// And factory Singletons or singletons where concrete type is not resolved inspect the interface
			//reg.Register<IImmutableSingleton, NonExistentTypeOrInTheMiddleOfTyping>( ObjectScope.Singleton );
			//reg.RegisterPlugin<IImmutableSingleton, NonExistentTypeOrInTheMiddleOfTyping>( ObjectScope.Singleton );
			//reg.Register<IImmutableSingleton>( null );
			//reg.RegisterFactory<IImmutableSingleton, NonExistentTypeOrInTheMiddleOfTyping>( ObjectScope.Singleton );
			//reg.RegisterPluginFactory<IImmutableSingleton, NonExistentTypeOrInTheMiddleOfTyping>( ObjectScope.Singleton );
			//reg.RegisterPluginFactory<DefaultExtensionPoint<IImmutableSingleton>, IImmutableSingleton, NonExistentTypeOrInTheMiddleOfTyping>( ObjectScope.Singleton );

			// Dynamic object factory registrations inspect the concrete object's ctor parameters
			/* UnsafeSingletonRegistration(SpecTests.ISingleton) */ reg.RegisterDynamicObjectFactory<ICreatedByDynamicFactory, ThingThatIsCreatedByDynamicObjectFactoryViaMutableThing, string, string>( ObjectScope.Singleton ) /**/;
		//	/* UnsafeSingletonRegistration(SpecTests.ISingleton) */ reg.RegisterDynamicObjectFactory<ICreatedByDynamicFactory, ThingThatIsCreatedByDynamicObjectFactoryViaMutableThing, string>( ObjectScope.Singleton ) /**/;

		//	// Dynamic object factory registrations that error out inspect IFactory<TDependencyType>
		//	/* UnsafeSingletonRegistration(D2L.LP.Extensibility.Activation.Domain.IFactory<SpecTests.ICreatedByDynamicFactory>) */ reg.RegisterDynamicObjectFactory<ICreatedByDynamicFactory, ThingThatIsSupposedToBeCreatedByDynamicFactoryButDoesntHavePublicConstructor, string>( ObjectScope.Singleton ) /**/;
		//	/* UnsafeSingletonRegistration(D2L.LP.Extensibility.Activation.Domain.IFactory<SpecTests.ICreatedByDynamicFactory>) */ reg.RegisterDynamicObjectFactory<ICreatedByDynamicFactory, ThingThatIsSupposedToBeCreatedByDynamicFactoryButDoesntHavePublicConstructor, string, string>( ObjectScope.Singleton ) /**/;

		//	// Non-Singletons are not flagged.
		//	reg.Register( typeof( INotSingleton ), typeof( DoesntMatter ), ObjectScope.WebRequest );
		//	reg.Register<INotSingleton, DoesntMatter>( ObjectScope.WebRequest );
		//	reg.RegisterPlugin<INotSingleton, DoesntMatter>( ObjectScope.WebRequest );
		//	reg.RegisterFactory<INotSingleton, DoesntMatter>( ObjectScope.WebRequest );
		//	reg.RegisterPluginFactory<INotSingleton, DoesntMatter>( ObjectScope.WebRequest );
		//	reg.RegisterParentAwareFactory<INotSingleton, DoesntMatter>();
		//	reg.ConfigurePlugins<INotSingleton>( ObjectScope.WebRequest );
		//	reg.ConfigureOrderedPlugins<INotSingleton, SomeComparer<DoesntMatter>>( ObjectScope.WebRequest );
		//	reg.ConfigureInstancePlugins<INotSingleton>( ObjectScope.WebRequest );
		//	reg.ConfigureInstancePlugins<INotSingleton, DefaultExtensionPoint<INotSingleton>>( ObjectScope.WebRequest );
		//	reg.RegisterPluginExtensionPoint<DefaultExtensionPoint<INotSingleton>, DoesntMatter>( ObjectScope.WebRequest );
		//	reg.RegisterPlugin<DefaultExtensionPoint<INotSingleton>, INotSingleton, DoesntMatter>( ObjectScope.WebRequest );

		//	// Interfaces marked as singleton cannot have web request registrations.
		//	/* AttributeRegistrationMismatch(SpecTests.ISingleton) */ reg.Register<ISingleton, DoesntMatter>( ObjectScope.WebRequest ) /**/;
		//	/* AttributeRegistrationMismatch(SpecTests.ISingleton) */ reg.Register<ISingleton, DoesntMatter>( ObjectScope.AlwaysCreateNewInstance ) /**/;
		//	/* AttributeRegistrationMismatch(SpecTests.ISingleton) */ reg.Register( typeof( ISingleton ), typeof( DoesntMatter ), ObjectScope.WebRequest ) /**/;
		//	/* AttributeRegistrationMismatch(SpecTests.ISingleton) */ reg.RegisterFactory<ISingleton, DoesntMatter>( ObjectScope.WebRequest ) /**/;
		//	/* AttributeRegistrationMismatch(SpecTests.ISingleton) */ reg.RegisterPlugin<ISingleton, DoesntMatter>( ObjectScope.WebRequest ) /**/;
		//	/* AttributeRegistrationMismatch(SpecTests.ISingleton) */ reg.RegisterPluginFactory<ISingleton, DoesntMatter>( ObjectScope.WebRequest ) /**/;

		//	// DynamicObjectFactory registrations at non-Singleton scope are safe,
		//	// because the implementation is generated, so it will never be marked.
		//	reg.RegisterDynamicObjectFactory<ICreatedByDynamicFactory, ThingThatIsCreatedByDynamicObjectFactoryViaImmutableThing, string>( ObjectScope.WebRequest );
		//	reg.RegisterDynamicObjectFactory<ICreatedByDynamicFactory, ThingThatIsCreatedByDynamicObjectFactoryViaMutableThing, string>( ObjectScope.WebRequest );

		//	// Unhandled registration methods should raise a diagnostic.
		//	/* RegistrationKindUnknown */ reg.UnhandledRegisterMethod() /**/;

		//	// Concrete types should have a public constructor
		//	/* DependencyRegistraionMissingPublicConstructor(SpecTests.ThingWithInternalConstructor) */ reg.Register<IImmutableSingleton, ThingWithInternalConstructor>( ObjectScope.Singleton ) /**/;
		//	/* DependencyRegistraionMissingPublicConstructor(SpecTests.ThingWithPrivateConstructor) */ reg.Register<IImmutableSingleton, ThingWithPrivateConstructor>( ObjectScope.Singleton ) /**/;
		//	reg.Register<IImmutableSingleton, ThingWithInternalAndPublicConstructors>( ObjectScope.Singleton );
		//	reg.Register<IImmutableSingleton, ThingWithPrivateAndPublicConstructors>( ObjectScope.Singleton );
		//	/* DependencyRegistraionMissingPublicConstructor(SpecTests.ThingFactoryWithInternalConstructor) */ reg.RegisterFactory<IImmutableSingleton, ThingFactoryWithInternalConstructor>( ObjectScope.Singleton ) /**/;
		//	/* DependencyRegistraionMissingPublicConstructor(SpecTests.ThingFactoryWithPrivateConstructor) */ reg.RegisterFactory<IImmutableSingleton, ThingFactoryWithPrivateConstructor>( ObjectScope.Singleton ) /**/;
		//	reg.RegisterFactory<IImmutableSingleton, ThingFactoryWithInternalAndPublicConstructors>( ObjectScope.Singleton );
		//	reg.RegisterFactory<IImmutableSingleton, ThingFactoryWithPrivateAndPublicConstructors>( ObjectScope.Singleton );
		//	/* DependencyRegistraionMissingPublicConstructor(SpecTests.ThingWithInternalConstructor) */ reg.RegisterPlugin<IImmutableSingleton, ThingWithInternalConstructor>( ObjectScope.Singleton ) /**/;
		//	/* DependencyRegistraionMissingPublicConstructor(SpecTests.ThingWithPrivateConstructor) */ reg.RegisterPlugin<IImmutableSingleton, ThingWithPrivateConstructor>( ObjectScope.Singleton ) /**/;
		//	/* DependencyRegistraionMissingPublicConstructor(SpecTests.ThingWithInternalConstructor) */ reg.RegisterPlugin<DefaultExtensionPoint<IImmutableSingleton>, IImmutableSingleton, ThingWithInternalConstructor>( ObjectScope.Singleton ) /**/;
		//	/* DependencyRegistraionMissingPublicConstructor(SpecTests.ThingWithPrivateConstructor) */ reg.RegisterPlugin<DefaultExtensionPoint<IImmutableSingleton>, IImmutableSingleton, ThingWithPrivateConstructor>( ObjectScope.Singleton ) /**/;
		//	reg.RegisterPlugin<IImmutableSingleton, ThingWithInternalAndPublicConstructors>( ObjectScope.Singleton );
		//	reg.RegisterPlugin<IImmutableSingleton, ThingWithPrivateAndPublicConstructors>( ObjectScope.Singleton );
		//	reg.RegisterPlugin<DefaultExtensionPoint<IImmutableSingleton>, IImmutableSingleton, ThingWithInternalAndPublicConstructors>( ObjectScope.Singleton );
		//	reg.RegisterPlugin<DefaultExtensionPoint<IImmutableSingleton>, IImmutableSingleton, ThingWithPrivateAndPublicConstructors>( ObjectScope.Singleton );

		//	/* DependencyRegistraionMissingPublicConstructor(SpecTests.ThingFactoryWithInternalConstructor) */ reg.RegisterPluginFactory<IImmutableSingleton, ThingFactoryWithInternalConstructor>( ObjectScope.Singleton ) /**/;
		//	/* DependencyRegistraionMissingPublicConstructor(SpecTests.ThingFactoryWithPrivateConstructor) */ reg.RegisterPluginFactory<IImmutableSingleton, ThingFactoryWithPrivateConstructor>( ObjectScope.Singleton ) /**/;
		//	/* DependencyRegistraionMissingPublicConstructor(SpecTests.ThingFactoryWithInternalConstructor) */ reg.RegisterPluginFactory<DefaultExtensionPoint<IImmutableSingleton>, IImmutableSingleton, ThingFactoryWithInternalConstructor>( ObjectScope.Singleton ) /**/;
		//	/* DependencyRegistraionMissingPublicConstructor(SpecTests.ThingFactoryWithPrivateConstructor) */ reg.RegisterPluginFactory<DefaultExtensionPoint<IImmutableSingleton>, IImmutableSingleton, ThingFactoryWithPrivateConstructor>( ObjectScope.Singleton ) /**/;
		//	reg.RegisterPluginFactory<IImmutableSingleton, ThingFactoryWithInternalAndPublicConstructors>( ObjectScope.Singleton );
		//	reg.RegisterPluginFactory<IImmutableSingleton, ThingFactoryWithPrivateAndPublicConstructors>( ObjectScope.Singleton );
		//	reg.RegisterPluginFactory<DefaultExtensionPoint<IImmutableSingleton>, IImmutableSingleton, ThingFactoryWithInternalAndPublicConstructors>( ObjectScope.Singleton );
		//	reg.RegisterPluginFactory<DefaultExtensionPoint<IImmutableSingleton>, IImmutableSingleton, ThingFactoryWithPrivateAndPublicConstructors>( ObjectScope.Singleton );

		//	/* DependencyRegistraionMissingPublicConstructor(SpecTests.ThingWithInternalConstructor) */ reg.Register( typeof( IImmutableSingleton ), typeof( ThingWithInternalConstructor ), ObjectScope.Singleton ) /**/;
		//	reg.Register( typeof( IComparer<> ), typeof( SomeComparer<> ), ObjectScope.AlwaysCreateNewInstance );
		//}

		//// Registrations in some classes/structs are ignored because they 
		//// are wrappers of other register methods and we don't have enough
		//// to analyze.
		//public static class RegistrationCallsInThisClassAreIgnored {
		//	public static void DoesntMatter<T>( IDependencyRegistry reg ) where T : new() {
		//		reg.Register<T>( new T() );
		//	}
		//}
		//public struct RegistrationCallsInThisStructAreIgnored {
		//	public static void DoesntMatter<T>( IDependencyRegistry reg ) where T : new() {
		//		reg.Register<T>( new T() );
		//	}
		//}

		//public static class OnlyRegistrationMethodsOnIDependencyRegistryMatter {
		//	public static void DoesntMatter( IDependencyRegistry reg ) {
		//		Register<INotSingleton>( null );
		//	}

		//	public static void Register<TDependencyType>(
		//		TDependencyType instance
		//	) {
		//	}
		}
	}

	[Singleton]
	public interface ISingleton { }
	public interface INotSingleton { }

	[Objects.Immutable]
	public interface IImmutableSingleton : ISingleton { }

	[Objects.Immutable]
	public interface IImmutableSubSingleton : IImmutableSingleton { }

	[Objects.Immutable]
	public sealed class ImmutableThing : ISingleton, INotSingleton { }

	public interface ICreatedByDynamicFactory { }

	public interface ISingletonCreatedByDynamicFactory : ISingleton, ICreatedByDynamicFactory {
	}

	public interface INotSingletonCreatedByDynamicFactory : INotSingleton, ICreatedByDynamicFactory {
	}

	public class ThingThatIsCreatedByDynamicObjectFactoryViaImmutableThing : ICreatedByDynamicFactory {
		public ThingThatIsCreatedByDynamicObjectFactoryViaImmutableThing(
			string randomArg,
			[Dependency] ImmutableThing injected
		) { }
	}

	public class ThingThatIsCreatedByDynamicObjectFactoryViaMutableThing : ICreatedByDynamicFactory {
		public ThingThatIsCreatedByDynamicObjectFactoryViaMutableThing(
			string randomArg,
			[Dependency] ISingleton injected
		) { }
	}

	public class ThingThatIsSupposedToBeCreatedByDynamicFactoryButDoesntHavePublicConstructor: ISingletonCreatedByDynamicFactory, INotSingletonCreatedByDynamicFactory {
		private ThingThatIsSupposedToBeCreatedByDynamicFactoryButDoesntHavePublicConstructor(
			string randomArg
		) { }
	}

	public sealed class DefaultExtensionPoint<T> : ExtensionPointDescriptor, IExtensionPoint<T> {
		public override string Name { get; } = "Default";
	}

	public sealed class SomeComparer<T> : IComparer<T> {
		int IComparer<T>.Compare( T x, T y ) {
			throw new NotImplementedException();
		}
	}

	public sealed class ImmutableThingFactory : 
		IFactory<ImmutableThing>, 
		IFactory<ImmutableThing, Type> { }

	[Objects.Immutable]
	internal sealed class ThingWithInternalConstructor : IImmutableSingleton {
		internal ThingWithInternalConstructor() { }
	}

	[Objects.Immutable]
	internal sealed class ThingWithPrivateConstructor : IImmutableSingleton {
		private ThingWithPrivateConstructor() { }
	}

	[Objects.Immutable]
	internal sealed class ThingWithInternalAndPublicConstructors : IImmutableSingleton {
		internal ThingWithInternalAndPublicConstructors() { }
		public ThingWithInternalAndPublicConstructors() { }
	}

	[Objects.Immutable]
	internal sealed class ThingWithPrivateAndPublicConstructors : IImmutableSingleton {
		private ThingWithPrivateAndPublicConstructors() { }
		public ThingWithPrivateAndPublicConstructors() { }
	}

	internal sealed class ThingFactoryWithInternalConstructor : IFactory<IImmutableSingleton> {
		internal ThingFactoryWithInternalConstructor() { }
	}

	internal sealed class ThingFactoryWithPrivateConstructor : IFactory<IImmutableSingleton> {
		private ThingFactoryWithPrivateConstructor() { }
	}

	internal sealed class ThingFactoryWithInternalAndPublicConstructors : IFactory<IImmutableSingleton> {
		internal ThingFactoryWithInternalAndPublicConstructors() { }
		public ThingFactoryWithInternalAndPublicConstructors() { }
	}

	internal sealed class ThingFactoryWithPrivateAndPublicConstructors : IFactory<IImmutableSingleton> {
		internal ThingFactoryWithPrivateAndPublicConstructors() { }
		public ThingFactoryWithPrivateAndPublicConstructors() { }
	}

}


<|start_filename|>src/D2L.CodeStyle.Analyzers/ApiUsage/NotNullAnalyzer.cs<|end_filename|>
﻿using System.Collections.Immutable;
using System.Linq;
using D2L.CodeStyle.Analyzers.Extensions;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;

namespace D2L.CodeStyle.Analyzers.Contract {
	[DiagnosticAnalyzer( LanguageNames.CSharp )]
	internal sealed class NotNullAnalyzer : DiagnosticAnalyzer {

		private const string Namespace = "D2L.CodeStyle.Annotations.Contract.";
		private const string NotNullAttribute = Namespace + "NotNullAttribute";

		public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
			=> ImmutableArray.Create( Diagnostics.NullPassedToNotNullParameter );

		public override void Initialize( AnalysisContext context ) {
			context.EnableConcurrentExecution();
			context.ConfigureGeneratedCodeAnalysis( GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics );
			context.RegisterCompilationStartAction( RegisterNotNullAnalyzer );
		}

		public static void RegisterNotNullAnalyzer(
			CompilationStartAnalysisContext context
		) {
			context.RegisterSyntaxNodeAction(
				ctx => AnalyzeInvocation(
					ctx,
					(InvocationExpressionSyntax)ctx.Node
				),
				SyntaxKind.InvocationExpression
			);

			context.RegisterSyntaxNodeAction(
				ctx => AnalyzeObjectCreation(
					ctx,
					(ObjectCreationExpressionSyntax)ctx.Node
				),
				SyntaxKind.ObjectCreationExpression
			);
		}

		private static void AnalyzeInvocation(
			SyntaxNodeAnalysisContext context,
			InvocationExpressionSyntax invocation
		) {
			AnalyzeInvocationLikeThing(
				context,
				invocation.ArgumentList
			);
		}

		private static void AnalyzeObjectCreation(
			SyntaxNodeAnalysisContext context,
			ObjectCreationExpressionSyntax construction
		) {
			AnalyzeInvocationLikeThing(
				context,
				construction.ArgumentList
			);
		}

		/// <summary>
		/// Checks that some sort of method call respects [NotNull] annotations
		/// from the methods declaration.
		/// </summary>
		private static void AnalyzeInvocationLikeThing(
			SyntaxNodeAnalysisContext context,
			ArgumentListSyntax argumentList
		) {
			// If a constructor takes no arguments, and is populated using
			// object initializer syntax, then the argument list will be null.
			// There's nothing for us to analyze.
			if( argumentList == null ) {
				return;
			}

			// Validate that all arguments that are provided are either not
			// null or associated with a parameter that does not have [NotNull]
			// attribute. This would miss a null being passed to a params 
			// parameter which may be reasonable to always complain about
			// because it's a weird and ugly edge-case.
			SeparatedSyntaxList<ArgumentSyntax> arguments = argumentList.Arguments;
			foreach( var argument in arguments ) {
				// If the argument expression looks safe we don't need to
				// inspect the parameter.
				if ( !ThereIsSufficientConcernThatThisExpressionIsNull( argument.Expression ) ) {
					continue;
				}

				var parameter = argument.DetermineParameter(
					context.SemanticModel,
					allowParams: true
				);

				// This corresponds to some manner of broken code. The
				// analyzer only needs to emit diagnostics (at worst) for
				// otherwise correct code.
				if ( parameter == null ) {
					continue;
				}

				// If the parameter isn't marked [NotNull] we can skip this
				// argument. It might be interesting to complain for null
				// passed to a params argument in the future.
				if ( !SymbolHasAttribute( parameter, NotNullAttribute ) ) {
					continue;
				}

				// We know that the argument looks null enough and the
				// parameter has the [NotNull] attribute. Emit a diagnostic.
				context.ReportDiagnostic( Diagnostic.Create(
					Diagnostics.NullPassedToNotNullParameter,
					argument.GetLocation(),
					parameter.Name
				) );
			}
		}

		private static bool ThereIsSufficientConcernThatThisExpressionIsNull(
			ExpressionSyntax expr
		) {
			var litExpr = expr as LiteralExpressionSyntax;

			// We aren't handling anything this fancy at this point in time
			if ( litExpr == null ) {
				return false;
			}

			return litExpr.Token.Kind() == SyntaxKind.NullKeyword;
		}

		private static bool SymbolHasAttribute(
			ISymbol symbol,
			string attributeClassName
		) {
			// TODO: don't compare type names as strings
			bool hasExpectedAttribute = symbol.GetAttributes().Any(
				x => x.AttributeClass.GetFullTypeName() == attributeClassName
			);

			return hasExpectedAttribute;
		}
	}
}


<|start_filename|>src/D2L.CodeStyle.Annotations/Because.cs<|end_filename|>
﻿// ReSharper disable once CheckNamespace
namespace D2L.CodeStyle.Annotations {
	public enum Because {
		/// <summary>
		/// DEPRECATED: Things marked with this attribute should be recategorized.
		/// </summary>
		ItHasntBeenLookedAt = 1,

		/// <summary>
		/// DEPRECATED: Things marked with this attribute should be recategorized.
		/// This was used for things that are ugly code, possibly ugly enough to
		/// be an undiff problem. We need to better categorize these so that we can
		/// prioritize work that gets us closer to undiff.
		/// </summary>
		ItsSketchy = 2,

		/// <summary>
		/// These are blockers for undiff.
		/// </summary>
		ItsStickyDataOhNooo = 3,

		/// <summary>
		/// These are things which could be analyzed as safe but require improvements
		/// to the analyzer. An alternative is [Audited(...)] when we don't
		/// expect an improvements to the analyzer to pass this variable.
		/// </summary>
		WeNeedToMakeTheAnalyzerConsiderThisSafe = 4,

		/// <summary>
		/// This code is ugly and we don't feel comfortable making it
		/// [Audited(...)] (which is a long-term acceptable bucket) we
		/// are not prioritizing this work for undiff.
		/// </summary>
		ItsUgly = 5,

		/// <summary>
		/// This code won't ship or be enabled for undiff clients. It
		/// constitutes a feature that is not relevant for them, possibly
		/// because there is a newer parallel implementation or it is not
		/// compatible with next-gen hosting.
		/// </summary>
		ItsOnDeathRow = 6
	}
}


<|start_filename|>src/D2L.CodeStyle.Annotations/Contract/RequireNamedArgumentsAttribute.cs<|end_filename|>
﻿using System;

namespace D2L.CodeStyle.Annotations.Contract {

	[AttributeUsage(
		AttributeTargets.Constructor | AttributeTargets.Method,
		AllowMultiple = false
	)]
	public sealed class RequireNamedArgumentsAttribute : Attribute {
	}
}


<|start_filename|>src/D2L.CodeStyle.Annotations/Statics/Audited.cs<|end_filename|>
﻿using System;

// ReSharper disable once CheckNamespace
namespace D2L.CodeStyle.Annotations {
    public static partial class Statics {
        /// <summary>
        /// Indicates that a static variable is safe in a multi-tenant process
        /// </summary>
        [AttributeUsage( validOn: AttributeTargets.Field | AttributeTargets.Property )]
        public sealed class Audited : Attribute {
            /// <summary>
            /// Mark a static variable as safe in a multi-tenant process
            /// </summary>
            /// <param name="owner">The user who last reviewed this variable</param>
            /// <param name="auditedDate">The last time this variable was reviewed</param>
            /// <param name="rationale">A brief explaination of why this variable is safe in a multi-tenant process</param>
            public Audited(
                string owner,
                string auditedDate,
                string rationale
            ) {
                Owner = owner;
                AuditedDate = auditedDate;
                Rationale = rationale;
            }

            public string Owner { get; private set; }
            public string AuditedDate { get; private set; }
            public string Rationale { get; private set; }
        }
    }
}


<|start_filename|>src/D2L.CodeStyle.Analyzers/ApiUsage/ContentPhysicalPaths/ILpContentFilePhysicalPathAnalyzer.cs<|end_filename|>
﻿using System.Collections.Immutable;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;

namespace D2L.CodeStyle.Analyzers.ApiUsage.ContentPhysicalPaths {

	[DiagnosticAnalyzer( LanguageNames.CSharp )]
	internal sealed class ILpContentFilePhysicalPathAnalyzer : DiagnosticAnalyzer {

		private const string TypeName = "D2L.LP.Files.Domain.ILpContentFile";
		private const string PropertyName = "PhysicalPath";

		public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(
				PhysicalPathPropertyAnalysis.DiagnosticDescriptor
			);

		public override void Initialize( AnalysisContext context ) {
			context.EnableConcurrentExecution();
			context.ConfigureGeneratedCodeAnalysis( GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics );

			PhysicalPathPropertyAnalysis analysis = new PhysicalPathPropertyAnalysis(
					TypeName,
					PropertyName,
					AllowedTypes
				);

			analysis.Initialize( context );
		}

		/// <summary>
		/// A list of types that already contain ILpContentFile.PhysicalPath references
		/// </summary>
		private static readonly IImmutableSet<string> AllowedTypes = ImmutableHashSet.Create<string>(
				"D2L.LP.Web.ContentHandling.Security.Default.ContentRequestChecker",
				"D2L.LE.Content.Extensibility.Service.Content.Default.ContentTopic.TopicService",
				"D2L.LE.ToolIntegration.Content.ResourceModifiers.Content.ContentFile",
				"D2L.LE.ToolIntegration.Content.TopicLocationResolvers.Controllers.EditFileController",
				"D2L.WCS.Integrations.Content.AddCapture.Utils.Embed.EmbedCaptureSourceUpdater",
				"D2L.Video.Integrations.Content.AddSubtitle.Utils.SubtitleManager",
				"D2L.Video.Integrations.Content.AddVideo.Utils.Embed.EmbedVideoSourceUpdater"
			);

	}
}


<|start_filename|>src/D2L.CodeStyle.Annotations/Contract/ConstantAttribute.cs<|end_filename|>
﻿using System;

namespace D2L.CodeStyle.Annotations.Contract {

	/// <summary>
	/// Indicates that a parameter must be called with a constant value
	/// </summary>
	[AttributeUsage( AttributeTargets.Parameter, AllowMultiple = false )]
	public sealed class ConstantAttribute : ReadOnlyAttribute {
	}
}


<|start_filename|>src/D2L.CodeStyle.Analyzers/Extensions/Microsoft.CodeAnalysis.CSharp.Syntax.cs<|end_filename|>
﻿using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;

namespace D2L.CodeStyle.Analyzers.Extensions {
	internal static partial class RoslynExtensions {
		public static bool IsAutoImplemented( this PropertyDeclarationSyntax syntax ) {
			// Remember: auto properties are the ones that hold state. Other
			// properties are just sugar around function calls.

			// For expression bodied properties like
			//   public MyType MyProp => expr;
			// expr is invoked for every access so this behaves the same as
			//   public MyType MyProp { get { return expr; } }

			// Conveniently enough expr is stored in the initializer field of
			// the PropertyDeclarationSyntax, so the code for analyzing fields
			// will narrow the inspected type to that of expr.
			if ( syntax.AccessorList == null ) {
				return false;
			}

			// Auto-implemented properties have at least an implicit (no body)
			// get and never have an explicit set.
			foreach( var accessor in syntax.AccessorList.Accessors ) {
				if (accessor.Kind() == SyntaxKind.GetAccessorDeclaration
			 	 || accessor.Kind() == SyntaxKind.SetAccessorDeclaration
				) {
					if ( accessor.Body != null ) {
						return false;
					}
				}
			}

			return true;
		}

		public static bool IsAttributeOfType(
				this SemanticModel model,
				AttributeSyntax attributeSyntax,
				INamedTypeSymbol attributeType
			) {

			TypeInfo typeInfo = model.GetTypeInfo( attributeSyntax );

			ITypeSymbol typeSymbol = typeInfo.Type;
			if( typeSymbol == null ) {
				return false;
			}

			if( !typeSymbol.Equals( attributeType, SymbolEqualityComparer.Default ) ) {
				return false;
			}

			return true;
		}

		public static bool IsFromDocComment( this SyntaxNode node )
			=> node.FirstAncestorOrSelf<DocumentationCommentTriviaSyntax>() != null;

		public static bool IsStaticFunction( this ExpressionSyntax expr ) {
			if ( expr is not AnonymousFunctionExpressionSyntax afes ) {
				return false;
			}

			return afes.Modifiers.Any(
				m => m.IsKind( SyntaxKind.StaticKeyword )
			);
		}
	}
}


<|start_filename|>src/D2L.CodeStyle.Analyzers/CommonFixes/AddAttributeCodeFix.cs<|end_filename|>
﻿using System;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;

namespace D2L.CodeStyle.Analyzers.CommonFixes {
	// BUG: this doesn't consider multiple partial decls so if the attribute
	// has allowMultiple: false this fix will create compile errors if you 
	// run "Fix all" in VS.
	//
	// To fix it maybe we could make this fixer find all decl syntaxes
	// and use some method to pick one (e.g. look at the BaseList syntax,
	// ToString() it and pick the lowest lexicographically) and only suggest a
	// fix on that decl. VS will take care of de-duping these fixes in a
	// "Fix All".

	[ExportCodeFixProvider(
		LanguageNames.CSharp,
		Name = nameof( AddAttributeCodeFix )
	)]
	public sealed class AddAttributeCodeFix : CodeFixProvider {
		public const string USING_STATIC_ARG = "UsingStatic";
		public const string USING_NAMESPACE_ARG = "Using";
		public const string ATTRIBUTE_NAME_ARG = "Attr";

		public override ImmutableArray<string> FixableDiagnosticIds
			=> ImmutableArray.Create(
				Diagnostics.MissingTransitiveImmutableAttribute.Id
			);

		public override FixAllProvider GetFixAllProvider() {
			return WellKnownFixAllProviders.BatchFixer;
		}

		public override async Task RegisterCodeFixesAsync(
			CodeFixContext context
		) {
			var root = await context.Document
				.GetSyntaxRootAsync( context.CancellationToken )
				.ConfigureAwait( false ) as CompilationUnitSyntax;

			foreach( var diagnostic in context.Diagnostics ) {
				var identifierSpan = diagnostic.Location.SourceSpan;

				var decl = root.FindNode( identifierSpan ) as TypeDeclarationSyntax;

				(bool usingStatic, string usingNs, string attrName)
					= GetFixArgs( diagnostic.Properties );

				context.RegisterCodeFix(
					CodeAction.Create(
						title: $"Add [{attrName}]",
						createChangedDocument: ct => Fix(
							context.Document,
							root,
							decl,
							usingStatic: usingStatic,
							usingNs: usingNs,
							attrName: attrName,
							ct
						)
					),
					diagnostic
				);
			}
		}

		private static Task<Document> Fix(
			Document orig,
			CompilationUnitSyntax root,
			TypeDeclarationSyntax decl,
			bool usingStatic,
			string usingNs,
			string attrName,
			CancellationToken ct
		) {
			var newDecl = AddAttribute( decl, attrName );

			var newRoot = root.ReplaceNode( decl, newDecl );

			newRoot = AddUsingIfNecessary( newRoot, usingStatic, usingNs );

			var newDoc = orig.WithSyntaxRoot( newRoot );

			return Task.FromResult( newDoc );
		}

		private static CompilationUnitSyntax AddUsingIfNecessary(
			CompilationUnitSyntax root,
			bool usingStatic,
			string usingNs
		) {
			if( root.Usings.Any( IsTheNecessaryUsingDirective ) ) {
				return root; // nothing to do
			}

			var usingDirective = CreateUsingDirectiveSyntax(
				usingStatic,
				usingNs
			);

			return root.WithUsings( root.Usings.Add( usingDirective ) );

			bool IsTheNecessaryUsingDirective( UsingDirectiveSyntax u ) {
				if ( u.StaticKeyword == null && usingStatic ) {
					return false;
				}

				if ( u.StaticKeyword != null && !usingStatic ) {
					return false;
				}

				// Using a string here is pretty lame but I think it's ok.
				return u.Name.ToString() == usingNs;
			}
		}
		
		private static TypeDeclarationSyntax AddAttribute(
			TypeDeclarationSyntax decl,
			string attrName
		) {
			var attrSyntax = CreateAttributeSyntax( attrName );

			var leadingTrivia = decl.GetLeadingTrivia();

			// Need to strip the trivia first before adding attribute lists
			// because if it doesn't have any attr lists now it won't be able
			// to remove the trivia from whatever the first token is when we
			// add an attribute list.
			decl = decl.WithoutLeadingTrivia();

			// TypeDeclarationSyntax (the base class of these 3 types) doesn't
			// have AddAttributeLists... so we need to copy+paste some code.

			if ( decl is ClassDeclarationSyntax cls ) {
				decl = cls.AddAttributeLists( attrSyntax );

			} else if ( decl is StructDeclarationSyntax st ) {
				decl = st.AddAttributeLists( attrSyntax );

			} else if ( decl is InterfaceDeclarationSyntax iface ) {
				decl = iface.AddAttributeLists( attrSyntax );

			} else {
				throw new NotImplementedException();
			}

			// Re-add any leading trivia
			return decl.WithLeadingTrivia( leadingTrivia );
		}

		// Creates syntax for "[{attrName}]"
		private static AttributeListSyntax CreateAttributeSyntax(
			string attrName
		) {
			return SyntaxFactory.AttributeList(
				SyntaxFactory.SingletonSeparatedList(
					SyntaxFactory.Attribute(
						SyntaxFactory.IdentifierName( attrName )
					)
				)
			);
		}

		// Creates syntax for "using static(?) {usingNs};"
		private static UsingDirectiveSyntax CreateUsingDirectiveSyntax(
			bool usingStatic,
			string usingNs
		) {
			var directive = SyntaxFactory.UsingDirective(
				SyntaxFactory.ParseName( usingNs )
			);

			if( usingStatic ) {
				directive = directive.WithStaticKeyword(
					SyntaxFactory.Token( SyntaxKind.StaticKeyword )
				);
			}

			return directive;
		}

		private static (bool, string, string) GetFixArgs(
			ImmutableDictionary<string, string> properties
		) {
			bool usingStatic = false;

			if ( properties.ContainsKey( USING_STATIC_ARG ) ) {
				usingStatic = bool.Parse( properties[USING_STATIC_ARG] );
			}

			string usingNs = properties[USING_NAMESPACE_ARG];
			string attrName = properties[ATTRIBUTE_NAME_ARG];

			return (usingStatic, usingNs, attrName);
		}
	}
}


<|start_filename|>src/D2L.CodeStyle.Annotations/Mutability/AuditedAttribute.cs<|end_filename|>
﻿using System;

// ReSharper disable once CheckNamespace
namespace D2L.CodeStyle.Annotations {
	public static partial class Mutability {
		/// <summary>
		/// Indicates some mutable state is safe in an otherwise immutable type
		/// </summary>
		[AttributeUsage( validOn: AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Event )]
		public sealed class AuditedAttribute : Attribute {
			/// <summary>
			/// Mark some mutable state as safe in an otherwise immutable type
			/// </summary>
			/// <param name="owner">The user who last reviewed this state</param>
			/// <param name="auditedDate">The last time this variable was reviewed</param>
			/// <param name="rationale">A brief explaination of why this state is safe.</param>
			public AuditedAttribute(
				string owner,
				string auditedDate,
				string rationale
			) {
				Owner = owner;
				AuditedDate = auditedDate;
				Rationale = rationale;
			}

			public string Owner { get; private set; }
			public string AuditedDate { get; private set; }
			public string Rationale { get; private set; }
		}
	}
}


<|start_filename|>src/D2L.CodeStyle.Annotations/Mutability/UnauditedAttribute.cs<|end_filename|>
﻿using System;

// ReSharper disable once CheckNamespace
namespace D2L.CodeStyle.Annotations {
	public static partial class Mutability {
		[AttributeUsage( validOn: AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Event )]
		public sealed class UnauditedAttribute : Attribute {
			public readonly Because m_cuz;
			public readonly UndiffBucket m_bucket;

			public UnauditedAttribute( Because why ) {
				m_cuz = why;
			}

			public UnauditedAttribute( Because why, UndiffBucket bucket ) {
				if( why != Because.ItsStickyDataOhNooo ) {
					throw new ArgumentException( "UndiffBucket is only meaningful for Because.ItsStickyDataOhNooo", nameof( bucket ) );
				}
				m_cuz = why;
				m_bucket = bucket;
			}
		}
	}
}


<|start_filename|>src/D2L.CodeStyle.Analyzers/Build/MandatoryReferencesAnalyzer.cs<|end_filename|>
﻿using System.Collections.Immutable;
using System.IO;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;

namespace D2L.CodeStyle.Analyzers.Build {
	[DiagnosticAnalyzer( LanguageNames.CSharp )]
	public sealed class MandatoryReferencesAnalyzer : DiagnosticAnalyzer {
		public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics =>
			ImmutableArray.Create( Diagnostics.MustReferenceAnnotations );

		public override void Initialize( AnalysisContext ctx ) {
			ctx.EnableConcurrentExecution();
			ctx.ConfigureGeneratedCodeAnalysis( GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics );
			ctx.RegisterCompilationAction( AnalyzeCompilation );
		}

		public static void AnalyzeCompilation(
			CompilationAnalysisContext ctx
		) {
			bool hasAnnotationsReference = HasAnnotationsReference( ctx.Compilation );

			if ( !hasAnnotationsReference ) {
				ctx.ReportDiagnostic(
					Diagnostic.Create(
						Diagnostics.MustReferenceAnnotations,
						Location.None
					)
				);
			}

			// TODO:
			// * How does VS sometimes offer you "Add reference to foo.dll" as
			//   a fix?
			// * Does it look at what other projects in the solution reference
			//   to get a symbol? That'd be  too complicated to implement here.
			// * Can we use their fix somehow?
			// * Should we open up a GitHub issue for this?
		}

		public static bool HasAnnotationsReference( Compilation compilation ) {
			return compilation
				.References
				.Any( IsTheAnnotationsAssembly );
		}

		private static bool IsTheAnnotationsAssembly( MetadataReference mr ) {
			// There are 3 types of MetadataReference in Roslyn currently:
			// * UnresolvedMetadataReference: this will have a Dispaly of
			//   "Unresolved: <name>"
			// * PortableExecutableReference: it will be a path
			// * CompilationReference: it will be the name of the assembly.
			//   This gets used when you use Roslyn "manually", I'm not sure it
			//   comes up in practice...
			// So we're going to assume/require that Annotations is referenced
			// by a path on disk to a DLL.
			var pathOrName = mr.Display;

			string path;

			try {
				path = Path.GetFullPath( pathOrName );
			} catch { // all exceptions
				// if we can't parse the path for some reason, assume this
				// isn't the annotations assembly to be safe.
				return false;
			}

			string filename = Path.GetFileName( path );

			// We're only checking that you reference something with this
			// file name... maybe we could do something better with strong
			// naming? That might make rollying out changes to the
			// annotations a bit more annoying though.
			return filename == "D2L.CodeStyle.Annotations.dll";
		}
	}
}


<|start_filename|>src/D2L.CodeStyle.Analyzers/ApiUsage/Serialization/SerializerAttributeAnalyzer.cs<|end_filename|>
﻿using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using D2L.CodeStyle.Analyzers.Extensions;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;

namespace D2L.CodeStyle.Analyzers.ApiUsage.Serialization {

	[DiagnosticAnalyzer( LanguageNames.CSharp )]
	internal sealed class SerializerAttributeAnalyzer : DiagnosticAnalyzer {

		private static readonly SymbolDisplayFormat TypeDisplayFormat = new SymbolDisplayFormat(
				memberOptions: SymbolDisplayMemberOptions.IncludeContainingType,
				localOptions: SymbolDisplayLocalOptions.IncludeType,
				typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces
			);

		private const string SerializerAttributeFullName = "D2L.LP.Serialization.SerializerAttribute";
		private const string ISerializerFullName = "D2L.LP.Serialization.ISerializer";
		private const string ITrySerializerFullName = "D2L.LP.Serialization.ITrySerializer";

		public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(
			Diagnostics.InvalidSerializerType
		);

		public override void Initialize( AnalysisContext context ) {
			context.EnableConcurrentExecution();
			context.ConfigureGeneratedCodeAnalysis( GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics );
			context.RegisterCompilationStartAction( RegisterScopeBuilderAnalyzer );
		}

		private void RegisterScopeBuilderAnalyzer( CompilationStartAnalysisContext context ) {

			Compilation comp = context.Compilation;
			if( !comp.TryGetTypeByMetadataName( SerializerAttributeFullName, out INamedTypeSymbol serializerAttributeType ) ) {
				return;
			}

			var serializerInterfaceTypes = ImmutableArray.CreateBuilder<INamedTypeSymbol>();
			if( comp.TryGetTypeByMetadataName( ISerializerFullName, out INamedTypeSymbol serializerType ) ) {
				serializerInterfaceTypes.Add( serializerType );
			}
			if( comp.TryGetTypeByMetadataName( ITrySerializerFullName, out INamedTypeSymbol trySerializerType ) ) {
				serializerInterfaceTypes.Add( trySerializerType );
			}

			context.RegisterSyntaxNodeAction(
					c => AnalyzeAttributeSyntax(
						c,
						(AttributeSyntax)c.Node,
						serializerAttributeType,
						serializerInterfaceTypes.ToImmutable()
					),
					SyntaxKind.Attribute
				);
		}

		private void AnalyzeAttributeSyntax(
				SyntaxNodeAnalysisContext context,
				AttributeSyntax attributeSyntax,
				INamedTypeSymbol serializerAttributeType,
				ImmutableArray<INamedTypeSymbol> serializerInterfaceTypes
			) {

			SemanticModel model = context.SemanticModel;
			if( !model.IsAttributeOfType( attributeSyntax, serializerAttributeType ) ) {
				return;
			}

			AttributeArgumentListSyntax argumentList = attributeSyntax.ArgumentList;
			if( argumentList == null ) {
				return;
			}

			SeparatedSyntaxList<AttributeArgumentSyntax> arguments = argumentList.Arguments;
			if( arguments.Count == 0 ) {
				return;
			}

			AttributeArgumentSyntax typeArgumentSyntax = arguments[ 0 ];
			if( !( typeArgumentSyntax.Expression is TypeOfExpressionSyntax typeofSyntax ) ) {

				ReportInvalidSerializerType(
						context,
						typeArgumentSyntax,
						messageArg: typeArgumentSyntax.Expression.ToString()
					);
				return;
			}

			ITypeSymbol serializerType = model.GetTypeInfo( typeofSyntax.Type ).Type;

			if( DoesImplementOneOfSerializerInterfaces(
					serializerType,
					serializerInterfaceTypes
				) ) {
				return;
			}

			ReportInvalidSerializerType(
					context,
					typeArgumentSyntax,
					messageArg: serializerType.ToDisplayString( TypeDisplayFormat )
				);
		}

		private void ReportInvalidSerializerType(
				SyntaxNodeAnalysisContext context,
				AttributeArgumentSyntax typeArgumentSyntax,
				string messageArg
			) {

			Diagnostic diagnostic = Diagnostic.Create(
					descriptor: Diagnostics.InvalidSerializerType,
					location: typeArgumentSyntax.GetLocation(),
					messageArgs: messageArg
				);

			context.ReportDiagnostic( diagnostic );
		}

		private static bool DoesImplementOneOfSerializerInterfaces(
				ITypeSymbol serializerType,
				ImmutableArray<INamedTypeSymbol> serializerInterfaceTypes
			) {

			ImmutableArray<INamedTypeSymbol> interfaces;

			if( serializerType.IsDefinition ) {
				interfaces = serializerType.AllInterfaces;
			} else {
				interfaces = serializerType.OriginalDefinition.AllInterfaces;
			}

			return interfaces.Any( serializerInterfaceTypes.Contains );
		}
	}
}


<|start_filename|>src/D2L.CodeStyle.Analyzers/Language/AwaitedTasksAnalyzer.cs<|end_filename|>
﻿using System.Collections.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Operations;

namespace D2L.CodeStyle.Analyzers.Language {

	[DiagnosticAnalyzer( LanguageNames.CSharp )]
	internal sealed partial class AwaitedTasksAnalyzer : DiagnosticAnalyzer {
		public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
			=> ImmutableArray.Create( Diagnostics.AwaitedTaskNotConfigured );

		public override void Initialize( AnalysisContext context ) {
			context.EnableConcurrentExecution();

			context.ConfigureGeneratedCodeAnalysis(
				GeneratedCodeAnalysisFlags.None
			);

			context.RegisterCompilationStartAction( RegisterAnalyzer );
		}

		private static void RegisterAnalyzer(
			CompilationStartAnalysisContext context
		) {
			ImmutableHashSet<INamedTypeSymbol> configuredTaskTypes = ImmutableHashSet
				.Create(
					context.Compilation.GetTypeByMetadataName( "System.Runtime.CompilerServices.ConfiguredTaskAwaitable" ),
					context.Compilation.GetTypeByMetadataName( "System.Runtime.CompilerServices.ConfiguredTaskAwaitable`1" ),
					context.Compilation.GetTypeByMetadataName( "System.Runtime.CompilerServices.ConfiguredValueTaskAwaitable" ),
					context.Compilation.GetTypeByMetadataName( "System.Runtime.CompilerServices.ConfiguredValueTaskAwaitable`1" )
				)
				.Where( x => x != null && x.Kind != SymbolKind.ErrorType )
				.ToImmutableHashSet();
			if( !configuredTaskTypes.Any() ) {
				return;
			}

			context.RegisterOperationAction(
				ctx => AnalyzeAwait(
					context: ctx,
					configuredTaskTypes: configuredTaskTypes,
					operation: ctx.Operation as IAwaitOperation
				),
				OperationKind.Await
			);
		}

		private static void AnalyzeAwait(
			OperationAnalysisContext context,
			ImmutableHashSet<INamedTypeSymbol> configuredTaskTypes,
			IAwaitOperation operation
		) {
			IOperation rhs = operation.Operation;
			ITypeSymbol awaitedType = rhs.Type.OriginalDefinition;

			if( configuredTaskTypes.Contains( awaitedType ) ) {
				return;
			}

			context.ReportDiagnostic( Diagnostic.Create(
				Diagnostics.AwaitedTaskNotConfigured,
				operation.Syntax.GetLocation()
			) );
		}
	}
}


<|start_filename|>tests/D2L.CodeStyle.Analyzers.Test/Specs/RequireNamedArgumentsAnalyzer.cs<|end_filename|>
﻿// analyzer: D2L.CodeStyle.Analyzers.Language.RequireNamedArgumentsAnalyzer

using D2L.CodeStyle.Annotations.Contract;

namespace D2L {

	public static class Foo {
		private class Thing {
			public int a4;
			public int m_a4;
			public int _a4;
			public Thing nested;
		}

        private static int _a1, _a2, _a3, _a4, _a5, _a6, _a7, _a8, _a9, p; // "placeholder" variables
        private static int @class, @params, @int, @name, @a5;

		public static void _arg0() { }
		public static void _arg1( int a1 ) { }
		public static void _arg2( int a1, int a2 ) { }
		public static void _arg3( int a1, int a2, int a3 ) { }
		public static void _arg4( int a1, int a2, int a3, int a4 ) { }

		// These will shrink as we shrink the max *blush*
		public static void _arg5( int a1, int a2, int a3, int a4, int a5 ) { }
		public static void _arg6( int a1, int a2, int a3, int a4, int a5, int a6 ) { }

        public static int _arg1_ret(int a1) { return 0; }
        public static int _arg2_ret(int a1, int a2) { return 0; }
        public static int _arg5_ret(int a1, int a2, int a3, int a4, int a5) { return 0; }

        public static void funcWithParams( int a, int b, int c, params int[] ps ) { }

        public static void funcWithVerbatims( int @int, int @class, int @params, int @name, int @a5 ) { }

		[RequireNamedArguments]
		public static void funcWithRequiredNamedArgs( int a1, int a2 ) { }

		public delegate void delegate0Args();
		public delegate void delegate1Args( int a1 );
		public delegate void delegate5Args( int a1, int a2, int a3, int a4, int a5 );

		public sealed class SomeClass {
			public SomeClass() { }
			public SomeClass( int a1 ) { }
			public SomeClass( int a1, int a2 ) { }
			public SomeClass( int a1, int a2, int a3, int a4, int a5 ) { }

			[RequireNamedArguments]
			public SomeClass( int a1, int a2, int a3 ) { }
		}

		public static void Test() {
            #region "low" number of args doesn't require naming
            _arg0();
            _arg1( 1 );
            _arg1( _a1 );
			_arg2( _a1, _a2 );
			_arg3( _a1, _a2, _a3 );
			_arg4( _a1, _a2, _a3, _a4 );

            // Named literals
            _arg5( a1: 1, a2: 2, a3: 3, a4: 4, a5: 5 );
            _arg6( a1: 1, a2: 2, a3: 3, a4: 4, a5: 5, a6: 6 );

            // Named literals + variables
            _arg4( _a1, _a2, a3: 3, a4: 4 );
            _arg5( _a1, _a2, a3: 3, a4: 4, _a5 );
            #endregion

            #region diagnostic for too many unnamed args
            /* TooManyUnnamedArgs(5) */ _arg5( 1, 2, 3, 4, 5 ) /**/;
            /* TooManyUnnamedArgs(5) */ _arg6( 1, 2, 3, 4, 5, 6 ) /**/;
            _arg3( /* LiteralArgShouldBeNamed(a1) */ 1 /**/, /* LiteralArgShouldBeNamed(a2) */ 2 /**/, /* LiteralArgShouldBeNamed(a3) */ 3 /**/ );
            _arg3( a1: 1, /* LiteralArgShouldBeNamed(a2) */ 2 /**/, /* LiteralArgShouldBeNamed(a3) */ 3 /**/ );
			#endregion

			#region diagnostic required named args
			funcWithRequiredNamedArgs( a1: 1, a2: 2 );
			/* NamedArgumentsRequired */ funcWithRequiredNamedArgs( a1: 1, 2 ) /**/;
			/* NamedArgumentsRequired */ funcWithRequiredNamedArgs( 1, a2: 2 ) /**/;
			/* NamedArgumentsRequired */ funcWithRequiredNamedArgs( 1, 2 ) /**/;
			#endregion

			#region verbatim identifiers
			funcWithVerbatims( @int, @class, /* LiteralArgShouldBeNamed(@params) */ 3 /**/, p, p );
            /* TooManyUnnamedArgs(5) */ funcWithVerbatims( p, p, p, p, p ) /**/;

            // These should pass:
            funcWithVerbatims( @int, @class, @params, @name, @a5 );
            funcWithVerbatims( @int, p, p, p, p );
            #endregion

            #region all named args is usually preferred if there are lots of args
            _arg6(
				a1: 1,
				a2: 2,
				a3: 3,
				a4: 4,
				a5: 5,
				a6: 6
			);
			#endregion

			#region named args don't count against the unnamed args budget
			_arg5( a1: 1, p, p, p, p );
			_arg6( a1: 1, a2: 2, p, p, p, p );
			#endregion

			#region arguments that are literals with the correct name don't count against the budget
			int a1 = 11;
			int a3 = 13;
			int A1 = 101; // upper case doesn't matter
			_arg5( a1, p, p, p, p );
			_arg5( p, p, a3, p, p );
			_arg6( A1, p, a3, p, p, p );
			#endregion

			#region member accesses can also serve as psuedo-names
			var thing = new Thing();
			_arg5( p, p, p, thing.a4, p );
			_arg5( p, p, p, thing.nested.a4, p );
			_arg5( p, p, p, thing.m_a4, p );
			_arg5( p, p, p, thing.nested._a4, p );
			#endregion

			#region need to have enough named args, though
			/* TooManyUnnamedArgs(5) */ _arg6( a1: 1, 2, 3, 4, 5, 6 ) /**/;
			/* TooManyUnnamedArgs(5) */ _arg6( 1, a2: 2, 3, 4, 5, 6 ) /**/;
			#endregion

			#region params don't count against the unnamed args budget
			funcWithParams( p, p, p );
			funcWithParams( p, p, p, p );
			funcWithParams( p, p, p, p, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26 );
			#endregion

			#region delegates
			((delegate0Args)null)();
			((delegate1Args)null)( 1 );
			/* TooManyUnnamedArgs(5) */ ((delegate5Args)null)( p, p, p, p, p ) /**/;
			((delegate5Args)null)( a1: 1, p, p, p, p );
			#endregion

			#region class constructors should behave the same way
			// these aren't InvocationExpressions like the above but should
			// behave just the same.
			new SomeClass();
			new SomeClass( 1 );
			new SomeClass( p, p );
			/* TooManyUnnamedArgs(5) */ new SomeClass( p, p, p, p, p ) /**/;
			new SomeClass( a1: 1, p, p, p, p );
			new SomeClass( a1: 1, a2: 2, a3: 3 );
			/* NamedArgumentsRequired */ new SomeClass( 1, 2, 3 ) /**/;
			#endregion

			#region expressions should not trigger named argument diagnostics
			// See: https://stackoverflow.com/a/10133102
			System.Linq.Expressions.Expression<Func<int>> expression5args = () => _arg5_ret( p, p, p, p, p );
            System.Linq.Expressions.Expression<Func<int>> expression2args = () => _arg2_ret( p, p );
            System.Linq.Expressions.Expression<Func<int>> expression1args = () => _arg1_ret( p );

            // Do it again with constants to test 'LiteralArgShouldBeNamed'
            System.Linq.Expressions.Expression<Func<int>> expression5args = () => _arg5_ret( 1, 2, 3, 4, 5 );
            System.Linq.Expressions.Expression<Func<int>> expression2args = () => _arg2_ret( 1, 2 );
            System.Linq.Expressions.Expression<Func<int>> expression1args = () => _arg1_ret( 1 );

            // Try with various nested function calls
            System.Linq.Expressions.Expression<Func<int>> nest1 =
                () => _arg2_ret( 1, _arg2_ret( 1, 2 ) );
            System.Linq.Expressions.Expression<Func<int, int>> nest2 =
                (x) => _arg1_ret( _arg2_ret( x, 2 ) );
            System.Linq.Expressions.Expression<Func<int>> nest3 =
                () => _arg2_ret( _arg2_ret( 1, 2 ), _arg1_ret( 1 ) );
            System.Linq.Expressions.Expression<Func<int>> nest4 =
                () => _arg5_ret(
						_arg2_ret( _arg2_ret( 1, _arg2_ret( 1, 2 ) ) , _arg1_ret( 1 ) ),
						_arg5_ret( 1, 2, 3, 4, 5 ),
						_arg2_ret( _arg2_ret( _arg2_ret( _arg5_ret( 1, 2, 3, 4, 5 ), 2 ), 2 ), 2 ),
						4,
						_arg2_ret( 1, 2 )
				);
            System.Linq.Expressions.Expression<Func<int>> nest5 =
                () => _arg2_ret( 1, 2 + _arg2_ret( 1, 2 ) );
            System.Linq.Expressions.Expression<Func<int>> nest6 =
                () => _arg2_ret( 1, _arg2_ret(1, 2) * _arg2_ret( 1, 2 ) );
            #endregion
        }

		public abstract class SomeBaseClass {

			public SomeBaseClass() { }

			public SomeBaseClass( int a1 )
				: this() { }

			public SomeBaseClass( int a1, int a2 )
				: this( a1 ) { }

			[RequireNamedArguments]
			public SomeBaseClass( int a1, int a2, int a3 )
				: this( a1, a2 ) { }

			public SomeBaseClass( int a1, int a2, int a3, int a4 )
				: this( a1: a1, a2: a2, a3: a3 ) { }

			public SomeBaseClass( int a1, int a2, int a3, bool _ )
				: this( a1, a2, a3 ) { }

			public SomeBaseClass( int a1, int a2, int a3, int a4, int a5 )
				: this( a1, a2, a3, a4 ) { }

			public SomeBaseClass( int a1, int a2, int a3, int a4, int a5, int a6 )
				: this( a1, a2, a3, a4, a5 ) { }

			public SomeBaseClass( int b1, int b2, string _ )
				: this( b1 ) { }

			public SomeBaseClass( int b1, int b2, int b3, int b4, string _ )
				: this( b1, b2 ) { }

			public SomeBaseClass( int b1, int b2, int b3, string _ )
				/* NamedArgumentsRequired */ : this( b1, b2, b3 ) /**/{ }

			public SomeBaseClass( int b1, int b2, int b3, int b4, int b5, string _ )
				: this( b1, b2, b3, b4 ) { }

			public SomeBaseClass( int b1, int b2, int b3, int b4, int b5, int b6, long _ )
				: this( a1: b1, a2: b2, a3: b3, a4: b4, a5: b5 ) { }

			public SomeBaseClass( int b1, int b2, int b3, int b4, int b5, int b6, string _ )
				/* TooManyUnnamedArgs(5) */ : this( b1, b2, b3, b4, b5 ) /**/{ }
		}

		public sealed class SomeInheritedClass : SomeBaseClass {

			public SomeInheritedClass()
				: base() { }

			public SomeInheritedClass( int a1, bool _ )
				: base( a1 ) { }

			public SomeInheritedClass( int a1, int a2, bool _ )
				: base( a1, a2 ) { }

			public SomeInheritedClass( int a1, int a2, int a3, bool _ )
				: base( a1: a1, a2: a2, a3: a3 ) { }

			public SomeInheritedClass( int a1, int a2, int a3, byte _ )
				: base( a1, a2, a3 ) { }

			public SomeInheritedClass( int a1, int a2, int a3, int a4, bool _ )
				: base( a1, a2, a3, a4 ) { }

			public SomeInheritedClass( int a1, int a2, int a3, int a4, int a5, bool _ )
				: base( a1, a2, a3, a4, a5 ) { }

			public SomeInheritedClass( int b1, string _ )
				: base( b1 ) { }

			public SomeInheritedClass( int b1, int b2, string _ )
				: base( b1, b2 ) { }

			public SomeInheritedClass( int b1, int b2, int b3, bool _ )
				/* NamedArgumentsRequired */ : base( b1, b2, b3 ) /**/{ }

			public SomeInheritedClass( int b1, int b2, int b3, int b4, string _ )
				: base( b1, b2, b3, b4 ) { }

			public SomeInheritedClass( int b1, int b2, int b3, int b4, int b5, string _ )
				: base( a1: b1, a2: b2, a3: b3, a4: b4, a5: b5 ) { }

			public SomeInheritedClass( int b1, int b2, int b3, int b4, int b5, string _ )
				/* TooManyUnnamedArgs(5) */ : base( b1, b2, b3, b4, b5 ) /**/{ }
		}
	}

	public static class PR659 {
		public interface IFoo { }
		public class Foo : IFoo {
			public Foo( long a, long b ) { }
			static void SomeMethod( long a, long b, out IFoo c ) {
				c = new Foo( a, b );
			}
		}
	}
}


<|start_filename|>src/D2L.CodeStyle.Analyzers/Language/StructShouldBeReadonlyAnalyzer.cs<|end_filename|>
﻿using System.Collections.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;

namespace D2L.CodeStyle.Analyzers.Language {

	[DiagnosticAnalyzer( LanguageNames.CSharp )]
	internal sealed partial class StructShouldBeReadonlyAnalyzer : DiagnosticAnalyzer {
		public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
			=> ImmutableArray.Create( Diagnostics.StructShouldBeReadonly );

		public override void Initialize( AnalysisContext context ) {
			context.EnableConcurrentExecution();

			context.ConfigureGeneratedCodeAnalysis(
				GeneratedCodeAnalysisFlags.None
			);

			context.RegisterCompilationStartAction( RegisterAnalyzer );
		}

		private static void RegisterAnalyzer(
			CompilationStartAnalysisContext context
		) {
			context.RegisterSymbolAction(
				Analyze,
				SymbolKind.NamedType
			);
		}

		private static void Analyze(
			SymbolAnalysisContext context
		) {
			var symbol = (INamedTypeSymbol)context.Symbol;

			if( !symbol.IsDefinition ) {
				return;
			}

			if( symbol.TypeKind != TypeKind.Struct ) {
				return;
			}

			if( symbol.IsImplicitlyDeclared ) {
				return;
			}

			ImmutableArray<SyntaxReference> declarationRefs = symbol.DeclaringSyntaxReferences;

			if( declarationRefs.Length == 0 ) {
				return;
			}

			// This foreach can be replaced if symbol.IsReadOnly when we update
			// to CodeAnalysis 3.1.0 (VS2019)
			foreach( SyntaxReference declarationRef in declarationRefs ) {
				StructDeclarationSyntax declaration = declarationRef
					.GetSyntax( context.CancellationToken ) as StructDeclarationSyntax;

				foreach( SyntaxToken token in declaration.Modifiers ) {
					if( token.IsKind( SyntaxKind.ReadOnlyKeyword ) ) {
						return;
					}
				}
			}

			if( !ShouldBeReadOnly( symbol ) ) {
				return;
			}

			StructDeclarationSyntax firstDeclaration = declarationRefs
				.First()
				.GetSyntax( context.CancellationToken ) as StructDeclarationSyntax;

			Location location = firstDeclaration
				.Identifier
				.GetLocation();

			context.ReportDiagnostic( Diagnostic.Create(
				Diagnostics.StructShouldBeReadonly,
				location,
				symbol.MetadataName
			) );
		}

		private static bool ShouldBeReadOnly( INamedTypeSymbol symbol ) {
			foreach( ISymbol member in symbol.GetMembers() ) {
				switch( member ) {
					case IFieldSymbol field:
						if( !( field.IsReadOnly || field.IsConst ) ) {
							return false;
						}
						break;

					case IPropertySymbol property:
						if( !property.IsReadOnly ) {
							return false;
						}
						break;
				}
			}

			return true;
		}
	}
}


<|start_filename|>tests/D2L.CodeStyle.Analyzers.Test/Specs/ConfigViewerAnalyzer.cs<|end_filename|>
﻿// analyzer: D2L.CodeStyle.Analyzers.ApiUsage.Configs.ConfigViewerAnalyzer

namespace D2L.LP.Configuration.Config.Domain {
	public interface IConfigViewer {
		T GetInstance<T>( string configName );
		T GetOrg<T>( long orgId, string configName );
		T GetOrgUnit<T>( long orgId, long orgUnitId, string configName );
		T GetUser<T>( long orgId, long userId, string configName );
		T GetSession<T>( long orgId, long sessionId, string configName );
		T GetRole<T>( long orgId, long roleId, string configName );
		T GetUserOrgUnit<T>( long orgId, long orgUnitId, long userId, string configName );
	}
}

namespace D2L.CodeStyle.Analyzers.Specs {

	using D2L.LP.Configuration.Config.Domain;

	internal sealed class Foo {

		private readonly IConfigViewer m_configViewer;

		public void BannedConfigs() {

			m_configViewer.GetOrg<string>(
				123,
				/* BannedConfig(d2l.Settings.WebServerName, WebServerName is being moved to Hiera data. Use IUrlFormatter or IWebServerNameProvider instead.) */ "d2l.Settings.WebServerName" /**/
			);

			m_configViewer.GetOrg<string>(
				123,
				/* BannedConfig(d2l.Settings.WebServerName, WebServerName is being moved to Hiera data. Use IUrlFormatter or IWebServerNameProvider instead.) */ "d2l.settings.webservername" /**/
			);

			m_configViewer.GetOrg<string>(
				123,
				/* BannedConfig(d2l.Settings.WebServerName, WebServerName is being moved to Hiera data. Use IUrlFormatter or IWebServerNameProvider instead.) */ "d2l.seTTings.webserVERnamE" /**/
			);

			const string WebServerNameConfig = "d2l.Settings.WebServerName";
			m_configViewer.GetOrg<string>(
				123,
				/* BannedConfig(d2l.Settings.WebServerName, WebServerName is being moved to Hiera data. Use IUrlFormatter or IWebServerNameProvider instead.) */ WebServerNameConfig /**/
			);

			m_configViewer.GetOrg<string>(
				123,
				/* BannedConfig(d2l.Settings.WebServerName, WebServerName is being moved to Hiera data. Use IUrlFormatter or IWebServerNameProvider instead.) */ "d2l." + "Settings." + "WebServerName" /**/
			);

			m_configViewer.GetOrg<string>(
				configName: /* BannedConfig(d2l.Settings.WebServerName, WebServerName is being moved to Hiera data. Use IUrlFormatter or IWebServerNameProvider instead.) */ "d2l.Settings.WebServerName" /**/,
				orgId: 123
			);

			// <T> isn't considered
			m_configViewer.GetOrg<int>(
				123,
				/* BannedConfig(d2l.Settings.WebServerName, WebServerName is being moved to Hiera data. Use IUrlFormatter or IWebServerNameProvider instead.) */ "d2l.Settings.WebServerName" /**/
			);
		}

		public void OkayConfigs() {

			// Only marked on the expected function
			m_configViewer.GetInstance<string>( "d2l.Settings.WebServerName" );

			// Other configs names are okay
			m_configViewer.GetOrg<string>( 123, "foo.Bar.Baz" );

		}

	}
}


<|start_filename|>tests/D2L.CodeStyle.TestAnalyzers.Tests/NUnit/TestCaseSourceStringsAnalyzerTests.cs<|end_filename|>
﻿using D2L.CodeStyle.TestAnalyzers.Common;
using D2L.CodeStyle.TestAnalyzers.Test.Verifiers;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using NUnit.Framework;

namespace D2L.CodeStyle.TestAnalyzers.NUnit {
	[TestFixture]
	internal sealed class TestCaseSourceStringsAnalyzerTests : DiagnosticVerifier {

		private const string PREAMBLE = @"
using NUnit.Framework;
";

		[Test]
		public void NormalMethod_NoDiag() {
			const string test = PREAMBLE + @"
namespace Test {
	class Test {
		public void Test() {}
	}
}";
			AssertNoDiagnostic( test );
		}

		[Test]
		public void TestMethod_NoDiag() {
			const string test = PREAMBLE + @"
namespace Test {
	class Test {
		[Test]
		public void Test() {}
	}
}";
			AssertNoDiagnostic( test );
		}

		[Test]
		public void TestMethod_TestCaseSource_NameOf_NoDiag() {
			const string test = PREAMBLE + @"
namespace Test {
	class Test {
		private static readonly IEnumerable<int> SOURCE = Enumerable.Empty<int>();

		[Test, TestCaseSource( nameof( SOURCE ) )]
		public void Test( int x ) {}
	}
}";
			AssertNoDiagnostic( test );
		}

		[Test]
		public void TestMethod_TestCaseSource_SourceType_NoDiag() {
			const string test = PREAMBLE + @"
namespace Test {
	class SourceClass : IEnumerable {
		public IEnumerator GetEnumerator() { yield break; }
	}

	class Test {
		[Test, TestCaseSource( typeof( SourceClass ) )]
		public void Test( int x ) {}
	}
}";
			AssertNoDiagnostic( test );
		}

		// This case should raise a diagnostic
		[Test]
		public void TestMethod_TestCaseSource_SourceType_StringName_NoDiag() {
			const string test = PREAMBLE + @"
namespace Test {
	class SourceClass {
		public static readonly IEnumerable<int> CASES = Enumerable.Empty<int>();
	}

	class Test {
		[Test, TestCaseSource( typeof( SourceClass ), ""CASES"" )]
		public void Test( int x ) {}
	}
}";
			AssertNoDiagnostic( test );
		}

		[Test]
		public void TestMethod_TestCaseSource_String_Diag() {
			const string test = PREAMBLE + @"
namespace Test {
	class Test {
		private static readonly IEnumerable<int> SOURCE = Enumerable.Empty<int>();

		[Test, TestCaseSource( ""SOURCE"" )]
		public void Test( int x ) {}
	}
}";
			AssertSingleDiagnostic( Diagnostics.TestCaseSourceStrings, test, 8, 26, "SOURCE" );
		}

		private void AssertNoDiagnostic( string file ) {
			VerifyCSharpDiagnostic( file );
		}

		private void AssertSingleDiagnostic( DiagnosticDescriptor diag, string file, int line, int column, params object[] messageArgs ) {
			DiagnosticResult result = new DiagnosticResult {
				Id = diag.Id,
				Message = string.Format( diag.MessageFormat.ToString(), messageArgs ),
				Severity = DiagnosticSeverity.Error,
				Locations = new[] {
					new DiagnosticResultLocation( "Test0.cs", line, column )
				}
			};

			VerifyCSharpDiagnostic( file, result );
		}


		private static readonly MetadataReference NUnitReference = MetadataReference.CreateFromFile( typeof( TestAttribute ).Assembly.Location );

		protected override MetadataReference[] GetAdditionalReferences() => new[] {
			NUnitReference
		};

		protected override DiagnosticAnalyzer GetCSharpDiagnosticAnalyzer() {
			return new TestCaseSourceStringsAnalyzer();
		}
	}
}


<|start_filename|>src/D2L.CodeStyle.Analyzers/ApiUsage/JsonParamBinderAttribute/JsonParamBinderAnalyzerFixer.cs<|end_filename|>
﻿using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;

namespace D2L.CodeStyle.Analyzers.ApiUsage.JsonParamBinderAttribute {

	[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(JsonParamBinderAnalyzerFixer) )]
	public class JsonParamBinderAnalyzerFixer : CodeFixProvider {

		public override FixAllProvider GetFixAllProvider() {
			return WellKnownFixAllProviders.BatchFixer;
		}

		public override async Task RegisterCodeFixesAsync( CodeFixContext context ) {
			Diagnostic diagnostic = context.Diagnostics.First();
			TextSpan sourceSpan = diagnostic.Location.SourceSpan;

			SyntaxNode root = await context.Document.GetSyntaxRootAsync(
				                  context.CancellationToken
			                  ).ConfigureAwait( continueOnCapturedContext: false );

			AttributeSyntax oldAttribute = root.FindToken(sourceSpan.Start)
				.Parent.AncestorsAndSelf().OfType<AttributeSyntax>().First();

			context.RegisterCodeFix( 
				CodeAction.Create( 
					Diagnostics.ObsoleteJsonParamBinder.Title.ToString(),
					ct => SwapAttributes( context.Document, oldAttribute, ct)
				), 
				diagnostic
			);
		}

		private async Task<Document> SwapAttributes( 
			Document document, 
			AttributeSyntax oldAttribute, 
			CancellationToken ct 
		) {
			SyntaxNode root = await document.GetSyntaxRootAsync( ct )
				                  .ConfigureAwait( continueOnCapturedContext: false );

			AttributeSyntax newAttribute = SyntaxFactory.Attribute(
				SyntaxFactory.IdentifierName( "JsonConvertParameterBinder" ) );

			SyntaxNode newRoot = root.ReplaceNode(oldAttribute, newAttribute );

			return document.WithSyntaxRoot( newRoot );
		}

		public override ImmutableArray<string> FixableDiagnosticIds =>
			ImmutableArray.Create<string>( Diagnostics.ObsoleteJsonParamBinder.Id );

	}
}


<|start_filename|>tests/D2L.CodeStyle.Analyzers.Test/Specs/EventHandlersDisallowedListAnalyzer.cs<|end_filename|>
﻿// analyzer: D2L.CodeStyle.Analyzers.ApiUsage.Events.EventHandlersDisallowedListAnalyzer

namespace D2L.LP.Distributed.Events.ExternalPublish.UserInteraction {
	public sealed class UserInteractionEvent { }
}

namespace D2L.LP.Distributed.Events.Handlers {
	public interface IEventHandler<T> { }
	public interface IOrgEventHandler<T> { }
}

namespace UnrelatedImplementation {

	using System.Collections.Generic;

	public sealed class IntComparer : IComparer<int> {
		int IComparer<int>.Compare( int x, int y ) {
			return x.CompareTo( y );
		}
	}
}

namespace AllowedImplementations {

	using D2L.LP.Distributed.Events.Handlers;

	public sealed class StringEventHandler : IEventHandler<string> { }
	public sealed class StringOrgEventHandler : IOrgEventHandler<string> { }
}

namespace DisallowedImplementations {

	using D2L.LP.Distributed.Events.ExternalPublish.UserInteraction;
	using D2L.LP.Distributed.Events.Handlers;

	public sealed class UserInteractionEventHandler :/* EventHandlerDisallowed(D2L.LP.Distributed.Events.Handlers.IEventHandler<D2L.LP.Distributed.Events.ExternalPublish.UserInteraction.UserInteractionEvent>) */ IEventHandler<UserInteractionEvent> /**/{ }
	public sealed class UserInteractionOrgEventHandler :/* EventHandlerDisallowed(D2L.LP.Distributed.Events.Handlers.IOrgEventHandler<D2L.LP.Distributed.Events.ExternalPublish.UserInteraction.UserInteractionEvent>) */ IOrgEventHandler<UserInteractionEvent> /**/{ }

	public sealed class UserInteractionOrgEventHandler : IOrgEventHandler<string>,/* EventHandlerDisallowed(D2L.LP.Distributed.Events.Handlers.IOrgEventHandler<D2L.LP.Distributed.Events.ExternalPublish.UserInteraction.UserInteractionEvent>) */ IOrgEventHandler<UserInteractionEvent> /**/{ }
	public sealed class UserInteractionOrgEventHandler :/* EventHandlerDisallowed(D2L.LP.Distributed.Events.Handlers.IOrgEventHandler<D2L.LP.Distributed.Events.ExternalPublish.UserInteraction.UserInteractionEvent>) */ IOrgEventHandler<UserInteractionEvent> /**/, IOrgEventHandler<string> { }

}


<|start_filename|>src/D2L.CodeStyle.Analyzers/Extensions/Microsoft.CodeAnalysis.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
using System.Linq;
using D2L.CodeStyle.Analyzers.Immutability;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;

namespace D2L.CodeStyle.Analyzers.Extensions {
	internal static partial class RoslynExtensions {

		public static bool IsTypeMarkedSingleton( this ITypeSymbol symbol ) {
			if( Attributes.Singleton.IsDefined( symbol ) ) {
				return true;
			}
			if( symbol.Interfaces.Any( IsTypeMarkedSingleton ) ) {
				return true;
			}
			if( symbol.BaseType != null && IsTypeMarkedSingleton( symbol.BaseType ) ) {
				return true;
			}
			return false;
		}

		private static readonly SymbolDisplayFormat FullTypeDisplayFormat = new SymbolDisplayFormat(
			typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces,
			miscellaneousOptions: SymbolDisplayMiscellaneousOptions.ExpandNullable
		);

		private static readonly SymbolDisplayFormat FullTypeWithGenericsDisplayFormat = new SymbolDisplayFormat(
			typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces,
			genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters,
			miscellaneousOptions: SymbolDisplayMiscellaneousOptions.ExpandNullable
		);

		public static string GetFullTypeName( this ITypeSymbol symbol ) {
			var fullyQualifiedName = symbol.ToDisplayString( FullTypeDisplayFormat );
			return fullyQualifiedName;
		}


		public static string GetFullTypeNameWithGenericArguments( this ITypeSymbol symbol ) {
			var fullyQualifiedName = symbol.ToDisplayString( FullTypeWithGenericsDisplayFormat );
			return fullyQualifiedName;
		}

		public static bool IsNullOrErrorType( this ITypeSymbol symbol ) {
			if( symbol == null ) {
				return true;
			}
			if( symbol.Kind == SymbolKind.ErrorType ) {
				return true;
			}
			if( symbol.TypeKind == TypeKind.Error ) {
				return true;
			}

			return false;
		}

		public static bool IsNullOrErrorType( this ISymbol symbol ) {
			if( symbol == null ) {
				return true;
			}
			if( symbol.Kind == SymbolKind.ErrorType ) {
				return true;
			}

			return false;
		}

		/// <summary>
		/// Find the matching type argument in the base interface
		/// that corresponds to this type parameter.  That is,
		/// if we have Foo<S, T>: IFoo<S>, IBar<T>, this will
		/// match up the Foo S, to the IFoo S, but will get -1
		/// from IBar since it doesn't have S.
		/// </summary>
		public static int IndexOfArgument(
			this INamedTypeSymbol intf,
			string name
		) {

			for( int ordinal = 0; ordinal < intf.TypeArguments.Length; ordinal++ ) {
				if( string.Equals( intf.TypeArguments[ ordinal ].Name, name, StringComparison.Ordinal ) ) {
					return ordinal;
				}
			}

			return -1;
		}

		public static bool TryGetTypeByMetadataName(
				this Compilation compilation,
				string fullyQualifiedMetadataName,
				out INamedTypeSymbol typeSymbol
			) {

			typeSymbol = compilation.GetTypeByMetadataName( fullyQualifiedMetadataName );
			return ( typeSymbol != null );
		}

		// Inspired by https://github.com/dotnet/roslyn/blob/master/src/Compilers/Core/Portable/InternalUtilities/GeneratedCodeUtilities.cs
		public static bool IsFromGeneratedCode( this ISymbol symbol ) {
			var root = symbol.DeclaringSyntaxReferences.Single()
				.SyntaxTree.GetRoot();

			return IsGeneratedCodeFile( root ) || BeginsWithAutoGeneratedComment( root );
		}

		private static bool IsGeneratedCodeFile( SyntaxNode root ) {
			var path = root.SyntaxTree.FilePath.ToLower();

			if ( string.IsNullOrEmpty( path ) ) {
				return false;
			}

			// foo/bar/baz.a.b.c --> baz.a.b
			var end = Path.GetFileNameWithoutExtension( path );

			if ( end.EndsWith( ".designer" ) ) {
				return true;
			}

			if ( end.EndsWith( ".generated" ) ) {
				return true;
			}

			if ( end.EndsWith( ".g" ) ) {
				return true;
			}

			if ( end.EndsWith( ".g.i" ) ) {
				return true;
			}

			return false;
		}

		private static bool BeginsWithAutoGeneratedComment( SyntaxNode root ) {
			if ( !root.HasLeadingTrivia ) {
				return false;
			}

			foreach( var trivia in  root.GetLeadingTrivia() ) {
				bool isComment = trivia.IsKind( SyntaxKind.SingleLineCommentTrivia )
					|| trivia.IsKind( SyntaxKind.MultiLineCommentTrivia );

				if ( !isComment ) {
					continue;
				}

				var text = trivia.ToString();

				if( text.Contains( "<autogenerated" ) ) {
					return true;
				}

				if ( text.Contains( "<auto-generated" ) ) {
					return true;
				}
			}

			return false;
		}

		public static ImmutableArray<IMethodSymbol> GetImplementedMethods(
			this IMethodSymbol method
		) {
			if( method.ExplicitInterfaceImplementations.Length > 0 ) {
				return method.ExplicitInterfaceImplementations;
			}

			ITypeSymbol type = method.ContainingType;

			var result = ImmutableArray.CreateBuilder<IMethodSymbol>();

			result.AddRange(
				type.AllInterfaces
					.SelectMany( i => i.GetMembers( method.Name ).OfType<IMethodSymbol>() )
					.Where( m => method.Equals( type.FindImplementationForInterfaceMember( m ), SymbolEqualityComparer.Default ) )
			); ;

			// Check OverriddenMethod != null instead of IsOverride
			// because "override void Foo() {}" will have IsOverride == true
			// even if there isn't a Foo to override
			if( method.OverriddenMethod != null ) {
				result.Add( method.OverriddenMethod );
			}

			return result.ToImmutable();
		}
	}
}


<|start_filename|>src/D2L.CodeStyle.TestAnalyzers/NUnit/CategoryAnalyzer.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
using System.Linq;
using D2L.CodeStyle.TestAnalyzers.Common;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Text;

namespace D2L.CodeStyle.TestAnalyzers.NUnit {

	[DiagnosticAnalyzer( LanguageNames.CSharp )]
	internal sealed class CategoryAnalyzer : DiagnosticAnalyzer {

		private static readonly ImmutableSortedSet<string> RequiredCategories = ImmutableSortedSet
			.Create(
				StringComparer.OrdinalIgnoreCase,
				"Unit",
				"Integration",
				"System",
				"Load",
				"UI"
			);

		private static readonly ImmutableSortedSet<string> ProhibitedAssemblyCategories = ImmutableSortedSet
			.Create(
				StringComparer.OrdinalIgnoreCase,
				"Isolated"
			);

		public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(
			Diagnostics.NUnitCategory
		);

		public override void Initialize( AnalysisContext context ) {
			context.EnableConcurrentExecution();
			context.ConfigureGeneratedCodeAnalysis( GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics );
			context.RegisterCompilationStartAction( OnCompilationStart );
			context.RegisterCompilationAction( OnCompilation );
		}

		private static void OnCompilationStart( CompilationStartAnalysisContext context ) {
			if( !TryLoadNUnitTypes( context.Compilation, out NUnitTypes types ) ) {
				return;
			}

			ImmutableHashSet<string> bannedCategories = LoadBannedCategoriesList( context.Options );

			context.RegisterSyntaxNodeAction(
				ctx => AnalyzeMethod(
					context: ctx,
					bannedCategories: bannedCategories,
					types: types,
					syntax: ctx.Node as MethodDeclarationSyntax
				),
				SyntaxKind.MethodDeclaration
			);
		}

		private static void OnCompilation( CompilationAnalysisContext context ) {
			if( !TryLoadNUnitTypes( context.Compilation, out NUnitTypes types ) ) {
				return;
			}

			VisitCategories( types, context.Compilation.Assembly, ( category, attribute ) => {
				if( !ProhibitedAssemblyCategories.Contains( category ) ) {
					return;
				}

				context.ReportDiagnostic( Diagnostic.Create(
					Diagnostics.NUnitCategory,
					attribute.ApplicationSyntaxReference.GetSyntax( context.CancellationToken ).GetLocation(),
					$"Assemblies cannot be categorized as any of [{string.Join( ", ", ProhibitedAssemblyCategories )}], but saw '{category}'."
				) );
			} );
		}

		private static void AnalyzeMethod(
			SyntaxNodeAnalysisContext context,
			ImmutableHashSet<string> bannedCategories,
			NUnitTypes types,
			MethodDeclarationSyntax syntax
		) {
			SemanticModel model = context.SemanticModel;

			IMethodSymbol method = model.GetDeclaredSymbol( syntax, context.CancellationToken );
			if( method == null ) {
				return;
			}

			if( !IsTestMethod( types, method ) ) {
				return;
			}

			ImmutableSortedSet<string> categories = GatherTestMethodCategories( types, method );
			if( !categories.Overlaps( RequiredCategories ) ) {
				context.ReportDiagnostic( Diagnostic.Create(
					Diagnostics.NUnitCategory,
					syntax.Identifier.GetLocation(),
					$"Test must be categorized as one of [{string.Join( ", ", RequiredCategories )}], but saw [{string.Join( ", ", categories )}]. See http://docs.dev.d2l/index.php/Test_Categories."
				) );
			}

			if( categories.Overlaps( bannedCategories ) ) {
				context.ReportDiagnostic( Diagnostic.Create(
					Diagnostics.NUnitCategory,
					syntax.Identifier.GetLocation(),
					$"Test is categorized as [{string.Join( ", ", categories )}]; however, the following categories are banned in this assembly: [{string.Join( ", ", bannedCategories )}]."
				) );
			}
		}

		private static bool IsTestMethod(
			NUnitTypes types,
			IMethodSymbol method
		) {
			foreach( AttributeData attribute in method.GetAttributes() ) {
				INamedTypeSymbol attributeType = attribute.AttributeClass;
				if( types.TestAttributes.Contains( attributeType ) ) {
					return true;
				}
			}

			return false;
		}

		private static ImmutableSortedSet<string> GatherTestMethodCategories(
			NUnitTypes types,
			IMethodSymbol method
		) {
			ImmutableSortedSet<string> methodCategories = GetCategories( types, method );
			ImmutableSortedSet<string> fixtureCategories = GetCategories( types, method.ContainingType );
			ImmutableSortedSet<string> assemblyCategories = GetCategories( types, method.ContainingAssembly );

			ImmutableSortedSet<string> categories =
				methodCategories
					.Union( fixtureCategories )
					.Union( assemblyCategories );

			return categories;
		}

		private static ImmutableSortedSet<string> GetCategories(
			NUnitTypes types,
			ISymbol symbol
		) {
			var categories = ImmutableSortedSet.CreateBuilder<string>( StringComparer.OrdinalIgnoreCase );

			VisitCategories( types, symbol, ( category, _ ) => categories.Add( category ) );

			return categories.ToImmutable();
		}

		private static void VisitCategories(
			NUnitTypes types,
			ISymbol symbol,
			Action<string, AttributeData> visitor
		) {
			foreach( AttributeData attribute in symbol.GetAttributes() ) {
				INamedTypeSymbol attributeType = attribute.AttributeClass;
				if( types.CategoryAttribute.Equals( attributeType, SymbolEqualityComparer.Default ) ) {
					VisitCategoryAttribute( attribute, visitor );
					continue;
				}

				if( types.TestFixtureAttribute.Equals( attributeType, SymbolEqualityComparer.Default ) ) {
					VisitTestFixtureAttribute( attribute, visitor );
					continue;
				}
			}

			if( symbol is INamedTypeSymbol typeSymbol ) {
				if( typeSymbol.BaseType != null ) {
					VisitCategories( types, typeSymbol.BaseType, visitor );
				}
			}
		}

		private static void VisitCategoryAttribute(
			AttributeData attribute,
			Action<string, AttributeData> visitor
		) {
			ImmutableArray<TypedConstant> args = attribute.ConstructorArguments;
			if( args.Length != 1 ) {
				return;
			}

			TypedConstant arg = args[0];

			if( arg.Type.SpecialType != SpecialType.System_String ) {
				return;
			}

			string category = arg.Value as string;
			visitor( category, attribute );
		}

		private static void VisitTestFixtureAttribute(
			AttributeData attribute,
			Action<string, AttributeData> visitor
		) {
			foreach( KeyValuePair<string, TypedConstant> namedArg in attribute.NamedArguments ) {
				if( namedArg.Key != "Category" ) {
					continue;
				}

				TypedConstant arg = namedArg.Value;

				if( arg.Type.SpecialType != SpecialType.System_String ) {
					continue;
				}

				string categoryCsv = arg.Value as string;
				foreach( string category in categoryCsv.Split( ',' ) ) {
					visitor( category.Trim(), attribute );
				}
			}
		}

		private static bool TryLoadNUnitTypes(
			Compilation compilation,
			out NUnitTypes types
		) {
			INamedTypeSymbol categoryAttribute = compilation.GetTypeByMetadataName( "NUnit.Framework.CategoryAttribute" );
			if( categoryAttribute == null || categoryAttribute.TypeKind == TypeKind.Error ) {
				types = null;
				return false;
			}

			ImmutableHashSet<INamedTypeSymbol> testAttributes = ImmutableHashSet
				.Create(
					compilation.GetTypeByMetadataName( "NUnit.Framework.TestAttribute" ),
					compilation.GetTypeByMetadataName( "NUnit.Framework.TestCaseAttribute" ),
					compilation.GetTypeByMetadataName( "NUnit.Framework.TestCaseSourceAttribute" ),
					compilation.GetTypeByMetadataName( "NUnit.Framework.TheoryAttribute" )
				);

			INamedTypeSymbol testFixtureAttribute = compilation.GetTypeByMetadataName( "NUnit.Framework.TestFixtureAttribute" );

			types = new NUnitTypes( categoryAttribute, testAttributes, testFixtureAttribute );
			return true;
		}

		private sealed class NUnitTypes {

			internal NUnitTypes(
				INamedTypeSymbol categoryAttribute,
				ImmutableHashSet<INamedTypeSymbol> testAttributes,
				INamedTypeSymbol testFixtureAttribute
			) {
				CategoryAttribute = categoryAttribute;
				TestAttributes = testAttributes;
				TestFixtureAttribute = testFixtureAttribute;
			}

			public INamedTypeSymbol CategoryAttribute { get; }
			public ImmutableHashSet<INamedTypeSymbol> TestAttributes { get; }
			public INamedTypeSymbol TestFixtureAttribute { get; }
		}

		private static ImmutableHashSet<string> LoadBannedCategoriesList(
			AnalyzerOptions options
		) {
			ImmutableHashSet<string>.Builder bannedList = ImmutableHashSet.CreateBuilder(
				StringComparer.Ordinal
			);

			AdditionalText bannedListFile = options.AdditionalFiles.FirstOrDefault(
				file => Path.GetFileName( file.Path ) == "BannedTestCategoriesList.txt"
			);

			if( bannedListFile == null ) {
				return bannedList.ToImmutableHashSet();
			}

			SourceText allowedListText = bannedListFile.GetText();

			foreach( TextLine line in allowedListText.Lines ) {
				bannedList.Add( line.ToString().Trim() );
			}

			return bannedList.ToImmutableHashSet();
		}
	}
}


<|start_filename|>tests/D2L.CodeStyle.Analyzers.Test/ApiUsage/NotNullAnalyzerTests.cs<|end_filename|>
﻿using System.Linq;
using D2L.CodeStyle.Analyzers.Contract;
using D2L.CodeStyle.Analyzers.Test.Verifiers;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using NUnit.Framework;

namespace D2L.CodeStyle.Analyzers.ApiUsage {

	[TestFixture]
	internal sealed class NotNullAnalyzerTests : DiagnosticVerifier {

		private const string Attributes = @"
using System;
using D2L.CodeStyle.Annotations.Contract;

namespace D2L.CodeStyle.Annotations.Contract {
	public class NotNullAttribute : System.Attribute {}
}
";

		private const string NotNullParamMethod = Attributes + @"
namespace Test {
	class TestProvider {
		public void TestMethod(
			[NotNull] string testName
		) {}

		public void TestMethod(
			object allowedToBeNull,
			[NotNull] string testName
		) {}

		public void MultiNotNull(
			[NotNull] string testName,
			[NotNull] string anotherName
		) {}

		public string GetStuff() { return ""Ghostbusters"" };

		public void TestMethodCanTakeNull( string testName ) {}

		public bool ShouldDoStuff => false;
	}
}
";

		private static readonly int NotNullParamMethodLines = NotNullParamMethod.Count( c => c.Equals( '\n' ) ) + 1;

		#region Parameter has [NotNull] directly

		#region Should produce errors

		[Test]
		public void NotNullParam_NullIsPassed_ReportsProblem() {
			const string test = NotNullParamMethod + @"
namespace Test {
	class TestCaller {
		public void TestMethod() {
			var provider = new TestProvider();
			provider.TestMethod( null );
		}
	}
}";
			AssertProducesError(
					test,
					5 + NotNullParamMethodLines,
					25,
					"testName"
				);
		}

		[Test]
		public void NotNullParam_NullIsPassed_MethodInSameClass_ReportsProblem() {
			const string test = NotNullParamMethod + @"
namespace Test {
	class TestCaller {
		public void TestMethod() {
			DoStuff( null );
		}

		public void DoStuff(
			[D2L.CodeStyle.Annotations.Contract.NotNull] string stuff
		) {}
	}
}";
			AssertProducesError(
					test,
					4 + NotNullParamMethodLines,
					13,
					"stuff"
				);
		}

		[Test]
		public void NotNullParam_MultipleParametersWithIssue_ReportsMultipleProblems() {
			const string test = NotNullParamMethod + @"
namespace Test {
	class TestCaller {
		public void TestMethod() {
			var provider = new TestProvider();
			provider.TestMethod( ""Hello!"" );
			provider.MultiNotNull(
					null,
					null
				);
		}
	}
}";
			DiagnosticDescriptor descriptor = Diagnostics.NullPassedToNotNullParameter;
			DiagnosticResult[] expectedResults = new[] {
				new DiagnosticResult() {
					Id = descriptor.Id,
					Message = string.Format( descriptor.MessageFormat.ToString(), "testName" ),
					Severity = DiagnosticSeverity.Error,
					Locations = new[] {
						new DiagnosticResultLocation( "Test0.cs", 7 + NotNullParamMethodLines, 6 )
					}
				},
				new DiagnosticResult() {
					Id = descriptor.Id,
					Message = string.Format( descriptor.MessageFormat.ToString(), "anotherName" ),
					Severity = DiagnosticSeverity.Error,
					Locations = new[] {
						new DiagnosticResultLocation( "Test0.cs", 8 + NotNullParamMethodLines, 6 )
					}
				}
			};

			VerifyCSharpDiagnostic( test, expectedResults );
		}

		[Test]
		public void NotNullParam_NamedArguments_OneIsNull_ReportsProblem() {
			const string test = NotNullParamMethod + @"
namespace Test {
	class TestCaller {
		public void TestMethod() {
			var provider = new TestProvider();
			provider.TestMethod(
				testName: null,
				allowedToBeNull: ""This is an object""
			);
		}
	}
}";
			AssertProducesError(
					test,
					6 + NotNullParamMethodLines,
					5,
					"testName"
				);
		}

		[Test]
		public void NotNullParam_NamedArgumentsWithAtSign_ReportsProblem() {
			const string test = NotNullParamMethod + @"
namespace Test {
	class TestCaller {
		public void TestMethod() {
			DoSomeStuff(
				@char: null,
				myVar: ""A Value""
			);
		}

		private void DoSomeStuff(
			[NotNull] string @char,
			[NotNull] string myVar
		) {}
	}
}";
			AssertProducesError(
					test,
					5 + NotNullParamMethodLines,
					5,
					"char"
				);
		}

		[Test]
		public void NotNullParam_CalledTwice_ReportsProblem() {
			const string test = NotNullParamMethod + @"
namespace Test {
	class TestCaller {
		public void TestMethod() {
			var provider = new TestProvider();
			provider.TestMethod( ""This is a value"" );
			provider.TestMethod( null );
			DoNothing( 3 );
			DoNothing( 4 );
		}

		private void DoNothing( int num ) {}
	}
}";
			AssertProducesError(
					test,
					6 + NotNullParamMethodLines,
					25,
					"testName"
				);
		}

		[Test]
		public void NotNullParam_CalledMethodHasDefaultValues_NullValuePassed_ReportsProblem() {
			const string test = NotNullParamMethod + @"
namespace Test {
	class TestCaller {
		public void TestMethod() {
			var provider = new TestProvider();
			DoSomeStuff( provider, null );
		}

		private void DoSomeStuff(
			TestProvider provider,
			[NotNull] string value = ""a value"",
			[NotNull] string anotherValue = ""another value""
		) {}
	}
}";
			AssertProducesError(
					test,
					5 + NotNullParamMethodLines,
					27,
					"value"
				);
		}

		[Test]
		public void NotNullParam_CalledMethodHasDefaultValues_NamedArgumentsWithNull_ReportsProblem() {
			const string test = NotNullParamMethod + @"
namespace Test {
	class TestCaller {
		public void TestMethod() {
			var provider = new TestProvider();
			DoSomeStuff(
					provider,
					anotherValue: null
				);
		}

		private void DoSomeStuff(
			TestProvider provider,
			[NotNull] string value = ""a value"",
			[NotNull] string anotherValue = ""another value""
		) {}
	}
}";
			AssertProducesError(
					test,
					7 + NotNullParamMethodLines,
					6,
					"anotherValue"
				);
		}

		[Test]
		public void NotNullParam_OverloadedMethodHasDefaultValues_NamedArgumentsWithNull_ReportsProblem() {
			const string test = NotNullParamMethod + @"
namespace Test {
	class TestCaller {
		public void TestMethod() {
			var provider = new TestProvider();
			DoSomeStuff(
					value: null,
					provider: provider
				);
		}

		private void DoSomeStuff(
			string provider = ""a value"",
			[NotNull] string value = ""another value""
		) {}

		private void DoSomeStuff(
			TestProvider provider,
			[NotNull] string value = ""a value""
		) {}
	}
}";
			AssertProducesError(
					test,
					6 + NotNullParamMethodLines,
					6,
					"value"
				);
		}

		[Test]
		public void NotNullParam_MethodHasParamsParameter_FirstIsNotNull_ReportsProblem() {
			const string test = NotNullParamMethod + @"
namespace Test {
	class TestCaller {
		public void TestMethod() {
			var provider = new TestProvider();
			DoSomeStuff( null, 1, 2, 3, 4, 5 );
		}

		private void DoSomeStuff(
			[NotNull] string name = ""a value"",
			params int[] numbers
		) {}
	}
}";
			AssertProducesError(
					test,
					5 + NotNullParamMethodLines,
					17,
					"name"
				);
		}

		[Test]
		public void NotNullParam_ParamsParameterIsNotNull_NullIncludedInList_ReportsProblem() {
			const string test = NotNullParamMethod + @"
namespace Test {
	class TestCaller {
		public void TestMethod() {
			var provider = new TestProvider();
			DoSomeStuff(
					42,
					""a value"",
					""another value"",
					null,
					""value the third""
				);
		}

		private void DoSomeStuff(
			int number,
			[NotNull] params string[] names
		) {}
	}
}";
			AssertProducesError(
					test,
					9 + NotNullParamMethodLines,
					6,
					"names"
				);
		}

		[Test]
		public void NotNullParam_ConstructorCalled_ReportsProblem() {
			const string test = NotNullParamMethod + @"
namespace Test {
	class TestCaller {
		public void TestMethod() {
			var foo = new Foo(
					null,
					null,
					new int[5]
				);
		}
	}

	internal class Foo {
		public Foo(
			string name,
			[NotNull] string other,
			object obj
		) {}
	}
}";
			AssertProducesError(
					test,
					6 + NotNullParamMethodLines,
					6,
					"other"
				);
		}

		[Test]
		public void NotNullParam_ConstructorCalledThenObjectInitializerSyntax_ReportsProblem() {
			const string test = NotNullParamMethod + @"
namespace Test {
	class TestCaller {
		public void TestMethod() {
			var foo = new Foo( null, null ) {
				Id = 42,
				Count = 3.14159
			};
		}
	}

	internal class Foo {
		public Foo(
			string name,
			[NotNull] string other
		) {}

		public int Id {get; set;}
		public double Count {get; set;}
	}
}";
			AssertProducesError(
					test,
					4 + NotNullParamMethodLines,
					29,
					"other"
				);
		}

		#endregion

		#region Should not produce errors

		[Test]
		public void NotNullParam_ValueIsPassed_DoesNotReportProblem() {
			const string test = NotNullParamMethod + @"
namespace Test {
	class TestCaller {
		public void TestMethod() {
			var provider = new TestProvider();
			provider.TestMethod( ""My Name"" );
		}
	}
}";
			AssertDoesNotProduceError( test );
		}

		[Test]
		public void NotNullParam_VariableWithValueAssigned_AtDeclaration_DoesNotReportProblem() {
			const string test = NotNullParamMethod + @"
namespace Test {
	class TestCaller {
		public void TestMethod() {
			var provider = new TestProvider();
			string name = ""Hello!"";
			provider.TestMethod( name );
		}
	}
}";
			AssertDoesNotProduceError( test );
		}

		[Test]
		public void NotNullParam_MethodParameterPassed_DoesNotReportProblem() {
			const string test = NotNullParamMethod + @"
namespace Test {
	class TestCaller {
		public void TestMethod( string intakeName ) {
			var provider = new TestProvider();
			provider.TestMethod( intakeName );
		}
	}
}";
			AssertDoesNotProduceError( test );
		}

		[Test]
		public void NotNullParam_NoAttribute_DoesNotReportProblem() {
			const string test = NotNullParamMethod + @"
namespace Test {
	class TestCaller {
		public void TestMethod() {
			var provider = new TestProvider();
			provider.TestMethodCanTakeNull( null );
		}
	}
}";
			AssertDoesNotProduceError( test );
		}

		[Test]
		public void NotNullParam_OneParamNotNull_NullPassedToNullable_DoesNotReportProblem() {
			const string test = NotNullParamMethod + @"
namespace Test {
	class TestCaller {
		public void TestMethod() {
			var provider = new TestProvider();
			provider.TestMethod( null, ""This is the not-nullable one"" );
		}
	}
}";
			AssertDoesNotProduceError( test );
		}

		[Test]
		public void NotNullParam_MethodCallResultPassedDirectly_DoesNotReportProblem() {
			const string test = NotNullParamMethod + @"
namespace Test {
	class TestCaller {
		public void TestMethod() {
			var provider = new TestProvider();
			provider.TestMethod( provider.GetStuff() );
		}
	}
}";
			AssertDoesNotProduceError( test );
		}

		[Test]
		public void NotNullParam_CalledMethodHasDefaultValues_MarkedAsNotNull_DoesNotReportProblem() {
			const string test = NotNullParamMethod + @"
namespace Test {
	class TestCaller {
		public void TestMethod() {
			var provider = new TestProvider();
			string hello = ""something"";
			DoSomeStuff( provider, hello );
		}

		private void DoSomeStuff(
			TestProvider provider,
			[NotNull] string value = ""a value"",
			[NotNull] string anotherValue = ""another value""
		) {}
	}
}";
			AssertDoesNotProduceError( test );
		}

		[Test]
		public void NotNullParam_CallingMethodWithExtraArguments_MarkedAsNotNull_DoesNotReportProblem() {
			// This should not be marked as an error since the method is being called in a way that
			// shouldn't even compile properly
			const string test = NotNullParamMethod + @"
namespace Test {
	class TestCaller {
		public void TestMethod() {
			var provider = new TestProvider();
			DoSomeStuff( null, ""something"" );
		}

		private void DoSomeStuff(
			[NotNull] string value = ""a value""
		) {}
	}
}";
			AssertDoesNotProduceError( test );
		}

		[Test]
		public void NotNullParam_EmptyConstructorUsingObjectInitializerSyntax_DoesNotReportProblem() {
			const string test = NotNullParamMethod + @"
namespace Test {
	class TestCaller {
		public void TestMethod() {
			var obj = new MyObject {
				Id = 5,
				Name = ""<NAME>""
			};
		}
	}

	class MyObject {
		public int Id {get; set;}
		public string Name {get; set;}
	}
}";
			AssertDoesNotProduceError( test );
		}

		#endregion

		#endregion

		private void AssertDoesNotProduceError(
			string file
		) {
			VerifyCSharpDiagnostic( file );
		}

		private void AssertProducesError(
			string file,
			int line,
			int column,
			string paramName
		) {
			DiagnosticDescriptor descriptor = Diagnostics.NullPassedToNotNullParameter;
			DiagnosticResult result = new DiagnosticResult() {
				Id = descriptor.Id,
				Message = string.Format( descriptor.MessageFormat.ToString(), paramName ),
				Severity = DiagnosticSeverity.Error,
				Locations = new[] {
					new DiagnosticResultLocation( "Test0.cs", line, column ),
				}
			};

			VerifyCSharpDiagnostic( file, result );
		}

		protected override DiagnosticAnalyzer GetCSharpDiagnosticAnalyzer() {
			return new NotNullAnalyzer();
		}

	}
}


<|start_filename|>tests/D2L.CodeStyle.Analyzers.Test/Specs/ReadOnlyParameterAnalyzerSpec.cs<|end_filename|>
﻿// analyzer: D2L.CodeStyle.Analyzers.Immutability.ReadOnlyParameterAnalyzer

using System;

namespace SpecTests {

	using D2L.CodeStyle.Annotations;

	internal sealed class ReadOnlyAttributeUsages {

		void Unused( [ReadOnly] int foo ) {}

		void OnlyRead( [ReadOnly] int foo ) {
			int bar = foo;
		}

		void PassedByValue( [ReadOnly] int foo ) {
			WrittenToInBody( foo );
		}

		void PassedToIn( [ReadOnly] int foo ) {
			InParameter( foo );
		}

		ref readonly int ReadonlyRefReturn( [ReadOnly] int foo ) {
			return foo;
		}

		void WrittenToInBody( /* ReadOnlyParameterIsnt(is assigned to and/or passed by reference) */ [ReadOnly] int foo /**/ ) {
			foo = 1;
		}

		void WrittenToInInlineFunc( /* ReadOnlyParameterIsnt(is assigned to and/or passed by reference) */ [ReadOnly] int foo /**/ ) {
			void Helper() { foo = 1; }
		}

		void WrittenToInLambda( /* ReadOnlyParameterIsnt(is assigned to and/or passed by reference) */ [ReadOnly] int foo /**/ ) {
			() => { foo = 1; };
		}

		void PassedToRef( /* ReadOnlyParameterIsnt(is assigned to and/or passed by reference) */ [ReadOnly] int foo /**/ ) {
			RefParameter( ref foo );
		}

		void RefParameter( /* ReadOnlyParameterIsnt(is an in/ref/out parameter) */ [ReadOnly] ref int foo /**/ ) { }
		void InParameter( /* ReadOnlyParameterIsnt(is an in/ref/out parameter) */ [ReadOnly] in int foo /**/ ) { }

		internal class C {
			C( [ReadOnly] int foo ) { }
		}

		internal interface I {
			void Foo( [ReadOnly] int foo );
		}

	}

	internal sealed class SubclassAttributeUsages {

		[AttributeUsage( AttributeTargets.Parameter, AllowMultiple = false )]
		public sealed class ReadOnlySubclassAttribute : ReadOnlyAttribute { }

		void Unused( [ReadOnlySubclass] int foo ) { }

		void OnlyRead( [ReadOnlySubclass] int foo ) {
			int bar = foo;
		}

		void PassedByValue( [ReadOnlySubclass] int foo ) {
			WrittenToInBody( foo );
		}

		void PassedToIn( [ReadOnlySubclass] int foo ) {
			InParameter( foo );
		}

		ref readonly int ReadonlyRefReturn( [ReadOnlySubclass] int foo ) {
			return foo;
		}

		void WrittenToInBody( /* ReadOnlyParameterIsnt(is assigned to and/or passed by reference) */ [ReadOnlySubclass] int foo /**/ ) {
			foo = 1;
		}

		void WrittenToInInlineFunc( /* ReadOnlyParameterIsnt(is assigned to and/or passed by reference) */ [ReadOnlySubclass] int foo /**/ ) {
			void Helper() { foo = 1; }
		}

		void WrittenToInLambda( /* ReadOnlyParameterIsnt(is assigned to and/or passed by reference) */ [ReadOnlySubclass] int foo /**/ ) {
			() => { foo = 1; };
		}

		void PassedToRef( /* ReadOnlyParameterIsnt(is assigned to and/or passed by reference) */ [ReadOnlySubclass] int foo /**/ ) {
			RefParameter( ref foo );
		}

		void RefParameter( /* ReadOnlyParameterIsnt(is an in/ref/out parameter) */ [ReadOnlySubclass] ref int foo /**/ ) { }
		void InParameter( /* ReadOnlyParameterIsnt(is an in/ref/out parameter) */ [ReadOnlySubclass] in int foo /**/ ) { }

		internal class C {
			C( [ReadOnlySubclass] int foo ) { }
		}

		internal interface I {
			void Foo( [ReadOnlySubclass] int foo );
		}

	}

	internal sealed class NonReadOnlyThings {

		void Unused( int foo ) { }

		void OnlyRead( int foo ) {
			int bar = foo;
		}

		void PassedByValue( int foo ) {
			WrittenToInBody( foo );
		}

		void PassedToIn( int foo ) {
			InParameter( foo );
		}

		ref readonly int ReadonlyRefReturn( int foo ) {
			return foo;
		}

		void WrittenToInBody( int foo ) {
			foo = 1;
		}

		void WrittenToInInlineFunc( int foo ) {
			void Helper() { foo = 1; }
		}

		void WrittenToInLambda( int foo ) {
			() => { foo = 1; };
		}

		void PassedToRef( int foo ) {
			RefParameter( ref foo );
		}

		void RefParameter( ref int foo ) { }
		void InParameter( in int foo ) { }

	}
}


<|start_filename|>src/D2L.CodeStyle.Analyzers/Immutability/ImmutabilityContext.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis;

namespace D2L.CodeStyle.Analyzers.Immutability {
	/// <summary>
	/// Used to answer "do we know if this type immutable?" (in whichever sense).
	/// Generally this means checking for [Immutable] etc., but there are also
	/// some pre-approved types (like int) and other logic that this class
	/// organizes.
	///
	/// This doesn't do anything particularly smart: e.g. for a user defined
	/// class it is mostly just looking for [Immutable]. If this judges a type
	/// as not known to be immutable that doesn't mean it is mutable.
	/// </summary>
	internal sealed partial class ImmutabilityContext {

		private readonly AnnotationsContext m_annotationsContext;
		private readonly ImmutableDictionary<INamedTypeSymbol, ImmutableTypeInfo> m_extraImmutableTypes;
		private readonly ImmutableHashSet<IMethodSymbol> m_knownImmutableReturns;
		private readonly ImmutableHashSet<ITypeParameterSymbol> m_conditionalTypeParameters;

		// Hard code this to avoid looking up the ITypeSymbol to include it in m_extraImmutableTypes
		private static readonly ImmutableHashSet<SpecialType> m_totallyImmutableSpecialTypes = ImmutableHashSet.Create(
			SpecialType.System_Enum,
			SpecialType.System_Boolean,
			SpecialType.System_Char,
			SpecialType.System_SByte,
			SpecialType.System_Byte,
			SpecialType.System_Int16,
			SpecialType.System_UInt16,
			SpecialType.System_Int32,
			SpecialType.System_UInt32,
			SpecialType.System_Int64,
			SpecialType.System_UInt64,
			SpecialType.System_Decimal,
			SpecialType.System_Single,
			SpecialType.System_Double,
			SpecialType.System_String,
			SpecialType.System_IntPtr,
			SpecialType.System_UIntPtr,
			SpecialType.System_ValueType
		);

		private ImmutabilityContext(
			AnnotationsContext annotationsContext,
			ImmutableDictionary<INamedTypeSymbol, ImmutableTypeInfo> extraImmutableTypes,
			ImmutableHashSet<IMethodSymbol> knownImmutableReturns,
			ImmutableHashSet<ITypeParameterSymbol> conditionalTypeParamemters
		) {
			m_annotationsContext = annotationsContext;
			m_extraImmutableTypes = extraImmutableTypes;
			m_knownImmutableReturns = knownImmutableReturns;
			m_conditionalTypeParameters = conditionalTypeParamemters;
		}

		/// <summary>
		/// This function is intended to provide a new immutability context that considers the conditional type parameters
		/// of the ConditionallyImmutable type being checked as immutable. It is important this is only used in those cases,
		/// and not for instance while validating type arguments to an [Immutable] type parameter.
		/// </summary>
		/// <param name="type">The ConditionallyImmutable type definition being checked</param>
		/// <returns></returns>
		public ImmutabilityContext WithConditionalTypeParametersAsImmutable(
			INamedTypeSymbol type
		) {
			if( !m_annotationsContext.Objects.ConditionallyImmutable.IsDefined( type ) ) {
				throw new InvalidOperationException( $"{nameof( WithConditionalTypeParametersAsImmutable )} should only be called on ConditionallyImmutable types" );
			}

			var conditionalTypeParameters = type.TypeParameters.Where( p => m_annotationsContext.Objects.OnlyIf.IsDefined( p ) );

			return new ImmutabilityContext(
				annotationsContext: m_annotationsContext,
				extraImmutableTypes: m_extraImmutableTypes,
				knownImmutableReturns: m_knownImmutableReturns,
				conditionalTypeParamemters: m_conditionalTypeParameters.Union( conditionalTypeParameters )
			);
		}

		/// <summary>
		/// Determines if a type is known to be immutable.
		/// </summary>
		/// <param name="query">The type to check (and what kind of check to do.)</param>
		/// <param name="diag">If this method returns false, an explaination for why its not known to be immutable.</param>
		/// <returns>Is the type immutable?</returns>
		public bool IsImmutable(
			ImmutabilityQuery query,
			Func<Location> getLocation,
			out Diagnostic diagnostic
		) {
			if ( query.Kind == ImmutableTypeKind.None ) {
				throw new ArgumentException(
					"ImmutabilityKind.None is not a valid question to ask this function",
					nameof( query.Kind )
				);
			}

			diagnostic = null;

			// Things like int are totally OK
			if ( m_totallyImmutableSpecialTypes.Contains( query.Type.SpecialType ) ) {
				return true;
			}

			// "new object()" are always immutable (and that's the only
			// constructor for System.Object) but in general things of type
			// System.Object (any reference type) may be mutable.
			//
			// This is hard-coded (rather than in m_extraImmutableTypes) to
			// avoid the ITypeSymbol lookup.
			if ( query.Kind == ImmutableTypeKind.Instance && query.Type.SpecialType == SpecialType.System_Object ) {
				return true;
			}

			if( query.Type is INamedTypeSymbol namedType ) {
				ImmutableTypeInfo info = GetImmutableTypeInfo( namedType );
				if( info.Kind.HasFlag( query.Kind ) ) {
					return info.IsImmutableDefinition(
						context: this,
						definition: namedType,
						getLocation: getLocation,
						out diagnostic
					);
				}
			}

			switch( query.Type.TypeKind ) {
				case TypeKind.Error:
					// Just say this is fine -- there is some other compiler
					// error in this case and we don't need to pile on.
					return true;

				case TypeKind.Enum:
					// Enums are like ints -- always immutable
					return true;

				case TypeKind.Array:
					diagnostic = Diagnostic.Create(
						Diagnostics.ArraysAreMutable,
						getLocation(),
						( query.Type as IArrayTypeSymbol ).ElementType.Name
					);

					return false;

				case TypeKind.Delegate:
					diagnostic = Diagnostic.Create(
						Diagnostics.DelegateTypesPossiblyMutable,
						getLocation()
					);

					return false;

				case TypeKind.Dynamic:
					diagnostic = Diagnostic.Create(
						Diagnostics.DynamicObjectsAreMutable,
						getLocation()
					);

					return false;

				case TypeKind.TypeParameter:
					if( GetImmutabilityFromAttributes( query.Type ).HasFlag( ImmutableTypeKind.Total ) ) {
						return true;
					}

					if( query.Type is ITypeParameterSymbol tp && m_conditionalTypeParameters.Contains( tp ) ) {
						return true;
					}

					diagnostic = Diagnostic.Create(
						Diagnostics.TypeParameterIsNotKnownToBeImmutable,
						getLocation(),
						query.Type.ToDisplayString()
					);

					return false;

				case TypeKind.Class:
					diagnostic = Diagnostic.Create(
						Diagnostics.NonImmutableTypeHeldByImmutable,
						getLocation(),
						query.Type.TypeKind.ToString().ToLower(),
						query.Type.ToDisplayString(),
						query.Kind == ImmutableTypeKind.Instance && !query.Type.IsSealed ? " (or [ImmutableBaseClass])" : ""
					);

					return false;

				case TypeKind.Interface:
				case TypeKind.Struct:
					diagnostic = Diagnostic.Create(
						Diagnostics.NonImmutableTypeHeldByImmutable,
						getLocation(),
						query.Type.TypeKind.ToString().ToLower(),
						query.Type.ToDisplayString(),
						""
					);

					return false;

				case TypeKind.Unknown:
				default:
					diagnostic = Diagnostic.Create(
						Diagnostics.UnexpectedTypeKind,
						location: getLocation(),
						query.Type.Kind
					);

					return false;
			}
		}

		/// <summary>
		/// Determines if the return value of a method is known to be immutable.
		/// </summary>
		/// <param name="methodSymbol">The method to check</param>
		/// <returns>Is the return value immutable?</returns>
		public bool IsReturnValueKnownToBeImmutable( IMethodSymbol methodSymbol ) {
			return m_knownImmutableReturns.Contains( methodSymbol.OriginalDefinition );
		}

		public ImmutableTypeInfo GetImmutableTypeInfo(
			INamedTypeSymbol type
		) {
			// Check for [Immutable] etc.
			ImmutableTypeKind fromAttributes = GetImmutabilityFromAttributes( type );
			if( fromAttributes != ImmutableTypeKind.None ) {
				return ImmutableTypeInfo.Create(
					annotationsContext: m_annotationsContext,
					kind: fromAttributes,
					type: type
				);
			}

			if( type.IsTupleType ) {
				return ImmutableTypeInfo.CreateWithAllConditionalTypeParameters(
					kind: ImmutableTypeKind.Total,
					type: type.OriginalDefinition
				);
			}

			// Check if we were otherwise told that this type is immutable
			if( m_extraImmutableTypes.TryGetValue( type.OriginalDefinition, out ImmutableTypeInfo info ) ) {
				return info;
			}

			return ImmutableTypeInfo.Create(
				annotationsContext: m_annotationsContext,
				kind: ImmutableTypeKind.None,
				type: type
			);
		}

		private ImmutableTypeKind GetImmutabilityFromAttributes(
			ITypeSymbol type
		) {
			if ( m_annotationsContext.Objects.Immutable.IsDefined( type ) ) {
				return ImmutableTypeKind.Total;
			}

			if( m_annotationsContext.Objects.ConditionallyImmutable.IsDefined( type ) ) {
				return ImmutableTypeKind.Total;
			}

			if ( m_annotationsContext.Objects.ImmutableBaseClass.IsDefined( type ) ) {
				return ImmutableTypeKind.Instance;
			}

			return ImmutableTypeKind.None;
		}
	}
}


<|start_filename|>src/D2L.CodeStyle.Analyzers/Immutability/ImmutableAttributeConsistencyChecker.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using D2L.CodeStyle.Analyzers.CommonFixes;
using D2L.CodeStyle.Analyzers.Extensions;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;

namespace D2L.CodeStyle.Analyzers.Immutability {
	internal sealed class ImmutableAttributeConsistencyChecker {

		internal delegate void DiagnosticSink( Diagnostic diagnostic );

		private static readonly ImmutableDictionary<string, string> FixArgs = new Dictionary<string, string> {
			{ AddAttributeCodeFix.USING_STATIC_ARG, "true" },
			{ AddAttributeCodeFix.USING_NAMESPACE_ARG, "D2L.CodeStyle.Annotations.Objects" },
			{ AddAttributeCodeFix.ATTRIBUTE_NAME_ARG, "Immutable" }
		}.ToImmutableDictionary();

		private readonly Compilation m_compilation;
		private readonly DiagnosticSink m_diagnosticSink;
		private readonly ImmutabilityContext m_context;
		private readonly AnnotationsContext m_annotationsContext;

		public ImmutableAttributeConsistencyChecker(
			Compilation compilation,
			DiagnosticSink diagnosticSink,
			ImmutabilityContext context,
			AnnotationsContext annotationsContext
		) {
			m_compilation = compilation;
			m_diagnosticSink = diagnosticSink;
			m_context = context;
			m_annotationsContext = annotationsContext;
		}

		public void CheckTypeDeclaration(
			INamedTypeSymbol typeSymbol
		) {
			ImmutableTypeInfo typeInfo = m_context.GetImmutableTypeInfo( typeSymbol );

			if( typeSymbol.BaseType != null ) {
				CompareConsistencyToBaseType( typeInfo, m_context.GetImmutableTypeInfo( typeSymbol.BaseType ) );
			}

			foreach( INamedTypeSymbol interfaceType in typeSymbol.Interfaces ) {
				CompareConsistencyToBaseType( typeInfo, m_context.GetImmutableTypeInfo( interfaceType ) );
			}
		}

		public void CheckMethodDeclaration(
			IMethodSymbol methodSymbol
		) {
			if( methodSymbol.TypeParameters.Length == 0 ) {
				return;
			}

			ImmutableArray<IMethodSymbol> implementedMethods = methodSymbol.GetImplementedMethods();
			foreach( IMethodSymbol implementedMethod in implementedMethods ) {
				for( int i = 0; i < methodSymbol.TypeParameters.Length; ++i ) {
					ITypeParameterSymbol thisParameter = methodSymbol.TypeParameters[ i ];
					ITypeParameterSymbol implementedParameter = implementedMethod.TypeParameters[ i ];

					bool thisIsImmutable = m_annotationsContext.Objects.Immutable.IsDefined( thisParameter );
					bool implementedIsImmutable = m_annotationsContext.Objects.Immutable.IsDefined( implementedParameter );

					if( thisIsImmutable != implementedIsImmutable ) {
						m_diagnosticSink( Diagnostic.Create(
							Diagnostics.InconsistentMethodAttributeApplication,
							GetLocationOfNthTypeParameter( methodSymbol, i ),
							"Immutable",
							$"{ methodSymbol.ContainingType.Name }.{ methodSymbol.Name }",
							$"{ implementedMethod.ContainingType.Name }.{ implementedMethod.Name }"
						) );
					}
				}
			}
		}

		private void CompareConsistencyToBaseType(
			ImmutableTypeInfo typeInfo,
			ImmutableTypeInfo baseTypeInfo
		) {
			switch( baseTypeInfo.Kind ) {
				case ImmutableTypeKind.None:
					return;

				case ImmutableTypeKind.Instance:
					return;

				case ImmutableTypeKind.Total:
					if( typeInfo.Kind == ImmutableTypeKind.Total ) {
						break;
					}

					// The docs say the only things that can have BaseType == null
					// are interfaces, System.Object itself (won't come up in our
					// analysis because (1) it !hasTheImmutableAttribute (2) you
					// can't explicitly list it as a base class anyway) and
					// pointer types (the base value type probably also doesn't
					// have it.)
					bool isInterface =
						baseTypeInfo.Type.BaseType == null
						&& baseTypeInfo.Type.SpecialType != SpecialType.System_Object
						&& baseTypeInfo.Type.SpecialType != SpecialType.System_ValueType
						&& baseTypeInfo.Type.Kind != SymbolKind.PointerType;

					TypeDeclarationSyntax syntax = FindDeclarationImplementingType(
						typeSymbol: typeInfo.Type,
						baseTypeSymbol: baseTypeInfo.Type
					);

					m_diagnosticSink(
						Diagnostic.Create(
							Diagnostics.MissingTransitiveImmutableAttribute,
							syntax.Identifier.GetLocation(),
							properties: FixArgs,
							typeInfo.Type.GetFullTypeName(),
							baseTypeInfo.IsConditional ? " (or [ConditionallyImmutable])" : "",
							isInterface ? "interface" : "base class",
							baseTypeInfo.Type.GetFullTypeName()
						)
					);

					break;

				default:
					throw new NotImplementedException();
			}
		}

		private TypeDeclarationSyntax FindDeclarationImplementingType(
			INamedTypeSymbol typeSymbol,
			INamedTypeSymbol baseTypeSymbol
		) {
			TypeDeclarationSyntax anySyntax = null;
			foreach( var reference in typeSymbol.DeclaringSyntaxReferences ) {
				var syntax = reference.GetSyntax() as TypeDeclarationSyntax;
				anySyntax = syntax;

				var baseTypes = syntax.BaseList?.Types;
				if( baseTypes == null ) {
					continue;
				}

				SemanticModel model = m_compilation.GetSemanticModel( syntax.SyntaxTree );
				foreach( var baseTypeSyntax in baseTypes ) {
					TypeSyntax typeSyntax = baseTypeSyntax.Type;

					ITypeSymbol thisTypeSymbol = model.GetTypeInfo( typeSyntax ).Type;

					if( baseTypeSymbol.Equals( thisTypeSymbol, SymbolEqualityComparer.Default ) ) {
						return syntax;
					}
				}
			}

			return anySyntax;
		}

		private static Location GetLocationOfNthTypeParameter( IMethodSymbol methodSymbol, int N ) {
			MethodDeclarationSyntax syntax = methodSymbol
				.DeclaringSyntaxReferences[ 0 ]
				.GetSyntax() as MethodDeclarationSyntax;

			Location loc = syntax.TypeParameterList.Parameters[ N ].GetLocation();
			return loc;
		}

	}
}


<|start_filename|>src/D2L.CodeStyle.Annotations/ReadOnlyAttribute.cs<|end_filename|>
﻿using System;

namespace D2L.CodeStyle.Annotations {
	[AttributeUsage( AttributeTargets.Parameter, AllowMultiple = false )]
	public class ReadOnlyAttribute : Attribute {}
}


<|start_filename|>tests/D2L.CodeStyle.Analyzers.Test/Specs/JsonParamBinderAnalyzer.cs<|end_filename|>
﻿// analyzer: D2L.CodeStyle.Analyzers.ApiUsage.JsonParamBinderAttribute.JsonParamBinderAnalyzer

using System;
using D2L.CodeStyle.Analyzers.ApiUsage.JsonParamBinderAttribute;
using D2L.CodeStyle.Annotations;
using D2L.LP.Web.Rest.Attributes;

namespace D2L.LP.Web.Rest.Attributes {

	public sealed class JsonParamBinder : Attribute { }

	public sealed class OtherAttribute : Attribute { }

}

namespace SpecTests {

	public class TestClass {

		public void Test( int x ) { } // No diagnostic because no attribute

		public void Test2( [/* ObsoleteJsonParamBinder */ JsonParamBinder /**/] int x ) { }

		public void Test3( string x, [/* ObsoleteJsonParamBinder */ JsonParamBinder /**/] int y ) { }

		public void Test4( string x, [/* ObsoleteJsonParamBinder */ JsonParamBinder /**/] int y = 0 ) { }

		public void Test5( [OtherAttribute, /* ObsoleteJsonParamBinder */ JsonParamBinder /**/] int x ) { }

		public void Test6( [OtherAttribute][/* ObsoleteJsonParamBinder */ JsonParamBinder /**/] int x ) { }

	}

	public class AllowedClass {

		public void Test( [JsonParamBinder] int x ) { } // No diagnostic because allowed legacy class

		public void Test( [JsonParamBinder, OtherAttribute] int x ) { } // No diagnostic because allowed legacy class

	}

}


<|start_filename|>src/D2L.CodeStyle.TestAnalyzers/NUnit/ConfigTestSetupStringsAnalyzer.cs<|end_filename|>
using System.Collections.Immutable;
using System.Linq;
using D2L.CodeStyle.TestAnalyzers.Common;
using D2L.CodeStyle.TestAnalyzers.Extensions;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;

namespace D2L.CodeStyle.TestAnalyzers.NUnit {

	[DiagnosticAnalyzer( LanguageNames.CSharp )]
	public sealed class ConfigTestSetupStringsAnalyzer : DiagnosticAnalyzer {

		private const string AttributeTypeName =
			"D2L.LP.Configuration.Config.ConfigTestSetupAttribute";

		public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
			=> ImmutableArray.Create( Diagnostics.ConfigTestSetupStrings );

		public override void Initialize( AnalysisContext context ) {

			context.EnableConcurrentExecution();
			context.ConfigureGeneratedCodeAnalysis( GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics );
			context.RegisterCompilationStartAction( Register );
		}

		private static void Register( CompilationStartAnalysisContext context ) {

			INamedTypeSymbol attributeType =
				context.Compilation.GetTypeByMetadataName( AttributeTypeName );

			if( attributeType.IsNullOrErrorType() ) {
				return;
			}

			context.RegisterSyntaxNodeAction(
					ctx => AnalyzeSyntaxNode( ctx, attributeType ),
					SyntaxKind.MethodDeclaration,
					SyntaxKind.ClassDeclaration
				);
		}

		private static void AnalyzeSyntaxNode(
				SyntaxNodeAnalysisContext context,
				INamedTypeSymbol attributeType
			) {

			SyntaxList<AttributeListSyntax> attributeList;

			switch( context.Node ) {

				case MethodDeclarationSyntax method:
					attributeList = method.AttributeLists;
					break;

				case ClassDeclarationSyntax @class:
					attributeList = @class.AttributeLists;
					break;

				default:
					return;
			}

			AttributeSyntax[] attributes = attributeList
				.SelectMany( x => x.Attributes )
				.ToArray();

			// Method has no attributes
			if( attributes.Length <= 0 ) {
				return;
			}

			foreach( AttributeSyntax attribute in attributes ) {

				ISymbol symbol = context
					.SemanticModel
					.GetSymbolInfo( attribute )
					.Symbol;

				if( symbol.IsNullOrErrorType() ) {
					continue;
				}

				// Not a [ConfigTestSetup()]
				if( !attributeType.Equals( symbol.ContainingType, SymbolEqualityComparer.Default ) ) {
					continue;
				}

				SeparatedSyntaxList<AttributeArgumentSyntax> arguments =
					attribute.ArgumentList.Arguments;

				if( arguments.Count != 1 ) {
					continue;
				}

				ExpressionSyntax argExpression = arguments.First().Expression;

				// [ConfigTestSetup( nameof(Foo) )]
				if( IsNameOfSyntax( argExpression ) ) {
					continue;
				}

				// Try to provide the most relevant message that we can.
				string messageArg =
					argExpression is LiteralExpressionSyntax literal
						? literal.ToString().Trim( '"' )
						: "...";

				Diagnostic diagnostic = Diagnostic.Create(
						Diagnostics.ConfigTestSetupStrings,
						argExpression.GetLocation(),
						messageArg
					);
				context.ReportDiagnostic( diagnostic );
			}
		}

		private static bool IsNameOfSyntax( ExpressionSyntax expression ) {

			if( !expression.IsKind( SyntaxKind.InvocationExpression ) ) {
				return false;
			}

			InvocationExpressionSyntax invocation =
				(InvocationExpressionSyntax)expression;

			if( !invocation.Expression.IsKind( SyntaxKind.IdentifierName ) ) {
				return false;
			}

			IdentifierNameSyntax identifier =
				(IdentifierNameSyntax)invocation.Expression;

			if( !identifier.Identifier.IsKind( SyntaxKind.IdentifierToken ) ) {
				return false;
			}

			if( identifier.Identifier.Text != "nameof" ) {
				return false;
			}

			return true;
		}

	}
}


<|start_filename|>src/D2L.CodeStyle.Analyzers/Attributes.cs<|end_filename|>
﻿using System.Collections.Immutable;
using D2L.CodeStyle.Analyzers.Extensions;
using Microsoft.CodeAnalysis;

namespace D2L.CodeStyle.Analyzers {
	internal static class Attributes {

		internal static readonly RoslynAttribute Singleton = new RoslynAttribute( "D2L.LP.Extensibility.Activation.Domain.SingletonAttribute" );
		internal static readonly RoslynAttribute DIFramework = new RoslynAttribute( "D2L.LP.Extensibility.Activation.Domain.DIFrameworkAttribute" );
		internal static readonly RoslynAttribute Dependency = new RoslynAttribute( "D2L.LP.Extensibility.Activation.Domain.DependencyAttribute" );

		internal sealed class RoslynAttribute {

			private readonly string m_fullTypeName;

			public RoslynAttribute( string fullTypeName ) {
				m_fullTypeName = fullTypeName;
			}

			internal ImmutableArray<AttributeData> GetAll( ISymbol s ) {
				var arr = ImmutableArray.CreateBuilder<AttributeData>();

				foreach( var attr in s.GetAttributes() ) {
					var attrFullTypeName = attr.AttributeClass.GetFullTypeName();
					if( attrFullTypeName == m_fullTypeName ) {
						arr.Add( attr );
					}
				}

				return arr.ToImmutable();
			}

			internal bool IsDefined( ISymbol s ) {
				foreach( var attr in s.GetAttributes() ) {
					var attrFullTypeName = attr.AttributeClass.GetFullTypeName();
					if( attrFullTypeName == m_fullTypeName ) {
						return true;
					}
				}
				return false;
			}

		}
	}
}


<|start_filename|>src/D2L.CodeStyle.TestAnalyzers/Common/Diagnostics.cs<|end_filename|>
﻿using Microsoft.CodeAnalysis;

namespace D2L.CodeStyle.TestAnalyzers.Common {
	public static class Diagnostics {
		public static readonly DiagnosticDescriptor TestCaseSourceStrings = new DiagnosticDescriptor(
			id: "D2LTESTS001",
			title: "Use nameof in TestCaseSource attributes.",
			messageFormat: "String arguments in TestCaseSource not allowed. Use nameof( {0} ) instead.",
			category: "Cleanliness",
			defaultSeverity: DiagnosticSeverity.Error,
			isEnabledByDefault: true,
			description: "Using strings in TestCaseSource attributes creates false positives during dead code analysis. nameof should be used instead."
		);

		public static readonly DiagnosticDescriptor ValueSourceStrings = new DiagnosticDescriptor(
			id: "D2LTESTS002",
			title: "Use nameof in ValueSource attributes.",
			messageFormat: "String arguments in ValueSource not allowed. Use nameof( {0} ) instead.",
			category: "Cleanliness",
			defaultSeverity: DiagnosticSeverity.Error,
			isEnabledByDefault: true,
			description: "Using strings in ValueSource attributes creates false positives during dead code analysis. nameof should be used instead."
		);

		public static readonly DiagnosticDescriptor ConfigTestSetupStrings = new DiagnosticDescriptor(
			id: "D2LTESTS003",
			title: "Use nameof in ConfigTestSetup attributes.",
			messageFormat: "String arguments in ConfigTestSetup are not allowed. Use nameof({0}) instead.",
			category: "Cleanliness",
			defaultSeverity: DiagnosticSeverity.Error,
			isEnabledByDefault: true,
			description: "Using strings in ConfigTestSetup attributes creates false positives during dead code analysis. nameof should be used instead."
		);

		public static readonly DiagnosticDescriptor NUnitCategory = new DiagnosticDescriptor(
			id: "D2LTESTS004",
			title: "Test is incorrectly categorized",
			messageFormat: "Test is incorrectly categorized: {0}",
			category: "Correctness",
			defaultSeverity: DiagnosticSeverity.Error,
			isEnabledByDefault: true,
			description: "Tests need to be correctly categorized in order to be run."
		);

		public static readonly DiagnosticDescriptor CustomServiceLocator = new DiagnosticDescriptor(
			id: "D2LTESTS005",
			title: "Use the default test service locator.",
			messageFormat: "Custom service locators should not be used. Use static TestServiceLocator.Get<T>() or TestServiceLocatorFactory.Default instead.",
			category: "Cleanliness",
			defaultSeverity: DiagnosticSeverity.Error,
			isEnabledByDefault: true,
			description: "Custom service locators are expensive to instantiate and slow down tests significantly. Use the default locator instead."
		);

        public static readonly DiagnosticDescriptor TestAttributeMissed = new DiagnosticDescriptor(
            id: "D2LTESTS006",
            title: "Method not labelled as [Test], [Theory], [TestCase], or [TestCaseSource]",
            messageFormat: "Method {0} is public, but does not have [Test], [Theory], [TestCase], or [TestCaseSource] attribute, add attribute or change the method visibility.",
            category: "Correctness",
            defaultSeverity: DiagnosticSeverity.Error,
            isEnabledByDefault: true,
            description: "If a [Test], [Theory], [TestCase], or [TestCaseSource] attribute is missed the test will not be run, leading to false confidence in the code."
        );

        public static readonly DiagnosticDescriptor UnnecessaryAllowedListEntry = new DiagnosticDescriptor(
            id: "D2LTESTS007",
            title: "Unnecessarily listed in an analyzer allowed list",
            messageFormat: "The entry for {0} in {1} is unnecessary.",
            category: "Cleanliness",
            defaultSeverity: DiagnosticSeverity.Error,
            isEnabledByDefault: true,
            description: "Unnecessarily listed in an analyzer allowed list."
        );
    }
}


<|start_filename|>src/D2L.CodeStyle.Annotations/Objects/Immutable.cs<|end_filename|>
﻿using System;

// ReSharper disable once CheckNamespace
namespace D2L.CodeStyle.Annotations {
	public static class Objects {

		public abstract class ImmutableAttributeBase : Attribute {}

		/// <summary>
		/// If a class, struct or interface is marked with this annotation it
		/// means that it's type is immutable. This includes all subtypes of
		/// that type (which is trivial for structs and sealed classes.) It is
		/// always safe to add this annotation because an analyzer will check
		/// that it is valid.
		/// </summary>
		[AttributeUsage(
			validOn: AttributeTargets.Class
			       | AttributeTargets.Interface
			       | AttributeTargets.Struct
			       | AttributeTargets.GenericParameter
		)]
		public sealed class Immutable : ImmutableAttributeBase { }

		/// <summary>
		/// If a class is marked with this annotation it means that it is immutable
		/// but other mutable classes may sub-class it.
		/// It is always safe to add this annotation because an analyzer will check
		/// that it is valid.
		[AttributeUsage( validOn: AttributeTargets.Class )]
		public sealed class ImmutableBaseClassAttribute : ImmutableAttributeBase { }

		/// <summary>
		/// If a class, struct or interface is marked with this annotation it
		/// means that it's type is immutable if all type arguments marked with [OnlyIf]
		/// are themselves immutable.
		/// </summary>
		[AttributeUsage(
			validOn: AttributeTargets.Class
			       | AttributeTargets.Interface
			       | AttributeTargets.Struct
		)]
		public sealed class ConditionallyImmutable : ImmutableAttributeBase {

			[AttributeUsage( validOn: AttributeTargets.GenericParameter )]
			public sealed class OnlyIf : ImmutableAttributeBase { }

		}

	}
}


<|start_filename|>src/D2L.CodeStyle.Analyzers/ApiUsage/ConstantAttributeAnalyzer.cs<|end_filename|>
﻿using System.Collections.Immutable;
using System.Linq;
using D2L.CodeStyle.Analyzers.Extensions;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;

namespace D2L.CodeStyle.Analyzers.ApiUsage {
	[DiagnosticAnalyzer( LanguageNames.CSharp )]
	public sealed class ConstantAttributeAnalyzer : DiagnosticAnalyzer {
		public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics =>
			ImmutableArray.Create(
				Diagnostics.NonConstantPassedToConstantParameter,
				Diagnostics.InvalidConstantType
			);

		public override void Initialize( AnalysisContext context ) {
			context.EnableConcurrentExecution();
			context.ConfigureGeneratedCodeAnalysis( GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics );
			context.RegisterCompilationStartAction( CompilationStart );
		}

		public static void CompilationStart(
			CompilationStartAnalysisContext context
		) {
			var constantAttribute = context.Compilation.GetTypeByMetadataName(
				"D2L.CodeStyle.Annotations.Contract.ConstantAttribute"
			);

			// The D2L.CodeStyle.Annotations reference is optional
			if ( constantAttribute == null || constantAttribute .Kind == SymbolKind.ErrorType ) {
				return;
			}

			context.RegisterSyntaxNodeAction(
				ctx => AnalyzeParameter(
					ctx,
					(ParameterSyntax)ctx.Node,
					constantAttribute
				),
				SyntaxKind.Parameter
			);

			context.RegisterSyntaxNodeAction(
				ctx => AnalyzeArgument(
					ctx,
					(ArgumentSyntax)ctx.Node,
					constantAttribute
				),
				SyntaxKind.Argument
			);
		}

		private static void AnalyzeParameter(
			SyntaxNodeAnalysisContext context,
			ParameterSyntax parameterSyntax,
			ISymbol constantAttribute
		) {
			var parameter = context.SemanticModel.GetDeclaredSymbol( parameterSyntax );

			// Parameter is somehow null, so do nothing
			if( parameter == null ) {
				return;
			}

			// Parameter is not [Constant], so do nothing
			if( !HasAttribute( parameter, constantAttribute ) ) {
				return;
			}

			var type = parameter.Type;

			// Special types (bool, enum, int, string, etc) are the only types
			// which might be constant, aside from type parameters filled with
			// special types
			if( TypeCanBeConstant( type.SpecialType ) ) {
				return;
			}

			// If the parameter's type is a type parameter, then whether
			// it can be constant depends on the type that gets filled;
			// We can't determine that here
			if( type.Kind == SymbolKind.TypeParameter ) {
				return;
			}

			// The current parameter type cannot be marked as [Constant]
			context.ReportDiagnostic(
				Diagnostic.Create(
					descriptor: Diagnostics.InvalidConstantType,
					location: parameterSyntax.GetLocation(),
					messageArgs: type.TypeKind
				)
			);
		}

		private static void AnalyzeArgument(
			SyntaxNodeAnalysisContext context,
			ArgumentSyntax argument,
			ISymbol constantAttribute
		) {
			// Get the associated parameter
			var parameter = argument.DetermineParameter(
				context.SemanticModel,
				allowParams: false
			);

			// Parameter is somehow null, so do nothing
			if( parameter == null ) {
				return;
			}

			// Parameter is not [Constant], so do nothing
			if( !HasAttribute( parameter, constantAttribute ) ) {
				return;
			}

			// Argument is a constant value, so do nothing
			if( context.SemanticModel.GetConstantValue( argument.Expression ).HasValue ) {
				return;
			}

			// Argument was defined as [Constant] already, so trust it
			var argumentSymbol = context.SemanticModel.GetSymbolInfo( argument.Expression ).Symbol;
			if( argumentSymbol != null && HasAttribute( argumentSymbol, constantAttribute ) ) {
				return;
			}

			// Argument is not constant, so report it
			context.ReportDiagnostic(
				Diagnostic.Create(
					descriptor: Diagnostics.NonConstantPassedToConstantParameter,
					location: argument.GetLocation(),
					messageArgs: parameter.Name
				)
			);
		}


		/// <summary>
		/// Check if the symbol has a specific attribute attached to it.
		/// </summary>
		/// <param name="symbol">The symbol to check for an attribute on</param>
		/// <param name="attributeSymbol">The symbol of the attribute</param>
		/// <returns>True if the attribute exists on the symbol, false otherwise</returns>
		private static bool HasAttribute(
			ISymbol symbol,
			ISymbol attributeSymbol
		) {
			return symbol.GetAttributes()
				.Any( attr => SymbolEqualityComparer.Default.Equals(
						attributeSymbol,
						attr.AttributeClass
					)
				);
		}

		private static bool TypeCanBeConstant( SpecialType specialType ) {
			switch( specialType ) {
				case SpecialType.System_Enum:
				case SpecialType.System_Boolean:
				case SpecialType.System_Char:
				case SpecialType.System_SByte:
				case SpecialType.System_Byte:
				case SpecialType.System_Int16:
				case SpecialType.System_UInt16:
				case SpecialType.System_Int32:
				case SpecialType.System_UInt32:
				case SpecialType.System_Int64:
				case SpecialType.System_UInt64:
				case SpecialType.System_Decimal:
				case SpecialType.System_Single:
				case SpecialType.System_Double:
				case SpecialType.System_String:
					return true;
				default:
					return false;
			}
		}
	}
}


<|start_filename|>src/D2L.CodeStyle.Analyzers/ApiUsage/DependencyInjection/Domain/RegisterDynamicObjectFactoryExpression.cs<|end_filename|>
﻿using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;

namespace D2L.CodeStyle.Analyzers.ApiUsage.DependencyInjection.Domain {
	// void RegisterDynamicObjectFactory<TOutput, TConcrete, TArg>(
	//		this IDependencyRegistry registry,
	//		ObjectScope scope
	// ) where TConcrete : class, TOutput
	// void RegisterDynamicObjectFactory<TOutput, TConcrete, TArg0, TArg1>(
	//		this IDependencyRegistry registry,
	//		ObjectScope scope
	// ) where TConcrete : class, TOutput
	internal sealed class RegisterDynamicObjectFactoryExpression : DependencyRegistrationExpression {

		internal override bool CanHandleMethod( IMethodSymbol method ) {
			return method.Name == "RegisterDynamicObjectFactory"
				&& method.IsExtensionMethod
				&& ( method.TypeArguments.Length == 3 || method.TypeArguments.Length == 4 )
				&& method.Parameters.Length == 1;
		}

		internal override DependencyRegistration GetRegistration( IMethodSymbol method, SeparatedSyntaxList<ArgumentSyntax> arguments, SemanticModel semanticModel ) {
			if( arguments.Count != 1 ) {
				return null;
			}

			ObjectScope scope;
			if( !TryGetObjectScope( arguments[0], semanticModel, out scope ) ) {
				return null;
			}
			return DependencyRegistration.DynamicObjectFactory(
				scope: scope,
				dependencyType: WrapWithIFactoryType( method.TypeArguments[0], semanticModel.Compilation ),
				dynamicObjectType: method.TypeArguments[1]
			);
		}

		private ITypeSymbol WrapWithIFactoryType( ITypeSymbol toWrap, Compilation compilation ) {
			var factoryType = compilation.GetTypeByMetadataName( IFactoryTypeMetadataName );
			var wrapped = factoryType.Construct( toWrap );
			return wrapped;
		}
	}
}


<|start_filename|>src/D2L.CodeStyle.TestAnalyzers/NUnit/TestAttributeAnalyzer.cs<|end_filename|>
﻿using D2L.CodeStyle.TestAnalyzers.Common;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Text;
using System.Collections.Immutable;
using System.IO;
using System.Linq;

namespace D2L.CodeStyle.TestAnalyzers.NUnit {
    [DiagnosticAnalyzer( LanguageNames.CSharp )]
    internal sealed class TestAttributeAnalyzer : DiagnosticAnalyzer {
        public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(
            Diagnostics.TestAttributeMissed
        );

        private const string AllowedListFileName = "TestAttributeAnalyzerDisallowedList.txt";

        public override void Initialize( AnalysisContext context ) {
            context.EnableConcurrentExecution();
			context.ConfigureGeneratedCodeAnalysis( GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics );
			context.RegisterCompilationStartAction( OnCompilationStart );
        }

        private static void OnCompilationStart(
            CompilationStartAnalysisContext context
        ) {
            if ( !TryLoadNUnitTypes( context.Compilation, out NUnitTypes types ) ) {
                return;
            }

            ImmutableHashSet<string> allowedClasses = GetAllowedList(
                context.Options.AdditionalFiles
            );

            context.RegisterSyntaxNodeAction(
                ctx => AnalyzeMethod(
                    context: ctx,
                    types: types,
                    syntax: ctx.Node as MethodDeclarationSyntax,
                    disallowedList: allowedClasses
                ),
                SyntaxKind.MethodDeclaration
            );
        }

        private static void AnalyzeMethod(
            SyntaxNodeAnalysisContext context,
            NUnitTypes types,
            MethodDeclarationSyntax syntax,
            ImmutableHashSet<string> disallowedList
        ) {
            SemanticModel model = context.SemanticModel;

            IMethodSymbol method = model.GetDeclaredSymbol( syntax, context.CancellationToken );
            if ( method == null ) {
                return;
            }

            // Any private/helper methods should be private/internal and can be ignored
            if ( method.DeclaredAccessibility != Accessibility.Public ) {
                return;
            }

            // We need the declaring class to be a [TestFixture] to continue
            INamedTypeSymbol declaringClass = method.ContainingType;
            if ( !declaringClass.GetAttributes().Any(
				attr => attr.AttributeClass.Equals( types.TestFixtureAttribute, SymbolEqualityComparer.Default )
			) ) {
                return;
            }

            // Ignore any classes which are disallowed
            if( IsClassAllowed( disallowedList, method.ContainingType ) ) {
                return;
            }

            bool isTest = IsTestMethod( types, method );
            if ( !isTest ) {
                context.ReportDiagnostic( Diagnostic.Create(
                        Diagnostics.TestAttributeMissed, syntax.Identifier.GetLocation(), method.Name )
                    );
                return;
            }
        }

        private static bool IsTestMethod(
            NUnitTypes types,
            IMethodSymbol method
        ) {
            foreach ( AttributeData attribute in method.GetAttributes() ) {
                INamedTypeSymbol attributeType = attribute.AttributeClass;
                if ( types.TestAttributes.Contains( attributeType ) || types.SetupTeardownAttributes.Contains( attributeType ) ) {
                    return true;
                }
            }

            return false;
        }

        private static bool TryLoadNUnitTypes(
            Compilation compilation,
            out NUnitTypes types
        ) {
            ImmutableHashSet<INamedTypeSymbol> testAttributes = ImmutableHashSet
                .Create(
                    compilation.GetTypeByMetadataName( "NUnit.Framework.TestAttribute" ),
                    compilation.GetTypeByMetadataName( "NUnit.Framework.TestCaseAttribute" ),
                    compilation.GetTypeByMetadataName( "NUnit.Framework.TestCaseSourceAttribute" ),
                    compilation.GetTypeByMetadataName( "NUnit.Framework.TheoryAttribute" )
                );
            ImmutableHashSet<INamedTypeSymbol> setupTeardownAttributes = ImmutableHashSet
                .Create(
                    compilation.GetTypeByMetadataName( "NUnit.Framework.SetUpAttribute" ),
                    compilation.GetTypeByMetadataName( "NUnit.Framework.OneTimeSetUpAttribute" ),
                    compilation.GetTypeByMetadataName( "NUnit.Framework.TearDownAttribute" ),
                    compilation.GetTypeByMetadataName( "NUnit.Framework.OneTimeTearDownAttribute" )
                );

            INamedTypeSymbol testFixtureAttribute = compilation.GetTypeByMetadataName( "NUnit.Framework.TestFixtureAttribute" );

            types = new NUnitTypes( testAttributes, setupTeardownAttributes, testFixtureAttribute );
            return true;
        }

        private static bool IsClassAllowed(
            ImmutableHashSet<string> allowedClasses,
            ISymbol classSymbol
        ) {
            bool isAllowed = allowedClasses.Contains( GetAllowedListName( classSymbol ) );

            return isAllowed;
        }

        private static string GetAllowedListName( ISymbol classSymbol ) =>
            classSymbol.ToString()
            + ", "
            + classSymbol.ContainingAssembly.ToDisplayString( SymbolDisplayFormat.MinimallyQualifiedFormat )
        ;

        private static ImmutableHashSet<string> GetAllowedList(
            ImmutableArray<AdditionalText> additionalFiles
        ) {
            ImmutableHashSet<string>.Builder allowedClasses = ImmutableHashSet.CreateBuilder<string>();

            AdditionalText allowedListFile = additionalFiles.FirstOrDefault(
                file => Path.GetFileName( file.Path ) == AllowedListFileName
            );

            if( allowedListFile == null ) {
                return allowedClasses.ToImmutableHashSet();
            }

            SourceText allowedListText = allowedListFile.GetText();

            foreach( TextLine line in allowedListText.Lines ) {
				allowedClasses.Add( line.ToString().Trim() );
            }

            return allowedClasses.ToImmutableHashSet();
        }

        private sealed class NUnitTypes {

            internal NUnitTypes(
                ImmutableHashSet<INamedTypeSymbol> testAttributes,
                ImmutableHashSet<INamedTypeSymbol> setupTeardownAttributes,
                INamedTypeSymbol testFixtureAttribute
            ) {
                TestAttributes = testAttributes;
                SetupTeardownAttributes = setupTeardownAttributes;
                TestFixtureAttribute = testFixtureAttribute;
            }

            public ImmutableHashSet<INamedTypeSymbol> TestAttributes { get; }
            public INamedTypeSymbol TestFixtureAttribute { get; }
            public ImmutableHashSet<INamedTypeSymbol> SetupTeardownAttributes { get; }
        }
    }
}


<|start_filename|>src/D2L.CodeStyle.Analyzers/Properties/AssemblyInfo.cs<|end_filename|>
﻿using System.Runtime.CompilerServices;

[assembly: InternalsVisibleTo( "D2L.CodeStyle.Analyzers.Tests" )]


<|start_filename|>tests/D2L.CodeStyle.Analyzers.Test/Specs/OldAndBrokenServiceLocatorAnalyzer.cs<|end_filename|>
﻿// analyzer: D2L.CodeStyle.Analyzers.ApiUsage.ServiceLocator.OldAndBrokenServiceLocatorAnalyzer

using System;
using D2L.LP.Extensibility.Activation.Domain;

namespace D2L.LP.Extensibility.Activation.Domain {
	public interface IServiceLocator { }

	public class OldAndBrokenServiceLocator {
		public static IServiceLocator Instance {
			get { return null; }
		}
	}

	public class OldAndBrokenServiceLocatorFactory {
		public IServiceLocator Create() { return null; }
	}

	public sealed class DIFrameworkAttribute : Attribute { }
}

namespace D2L.CodeStyle.Analyzers.OldAndBrokenLocator.Examples {
	public sealed class FooDependency { }
	public sealed class BarDependency { }

	public sealed class BadClass {

		public BadClass() { }

		public void UsesBrokenLocator() {
			IServiceLocator locator = /* OldAndBrokenLocatorIsObsolete */ OldAndBrokenServiceLocator /**/.Instance;
		}

		public void UsesBrokenLocatorFactory() {
			IServiceLocator locator = /* OldAndBrokenLocatorIsObsolete */ OldAndBrokenServiceLocatorFactory /**/.Create();
		}
	}

	public sealed class SketchyButNotYetOutlawedInjection {
		IServiceLocator m_locator;

		public SketchyButNotYetOutlawedInjection(
			IServiceLocator injectedLocator
		) {
			m_locator = injectedLocator;
		}
	}

	public sealed class GoodInjection {
		FooDependency m_foo;
		BarDependency m_bar;

		public GoodInjection(
			FooDependency injectedFoo,
			BarDependency injectedBar
		) {
			m_foo = injectedFoo;
			m_bar = injectedBar;
		}
	}

	[DIFramework]
	public sealed class DIFrameworkClass {

		public int InDIFrameworkClass() {
			IServiceLocator ok = OldAndBrokenServiceLocator.Instance;
			IServiceLocator alsoOk = OldAndBrokenServiceLocatorFactory.Create();
			return 0;
		}
	}
}


<|start_filename|>tests/D2L.CodeStyle.Analyzers.Test/Specs/ImmutableCollectionsAnalyzer.cs<|end_filename|>
﻿// analyzer: D2L.CodeStyle.Analyzers.ApiUsage.SystemCollectionsImmutable.ImmutableCollectionsAnalyzer

using System;
using System.Collections.Generic;
using System.Collections.Immutable;

namespace D2L.CodeStyle.Analyzers.Specs {
	class ImmutableCollectionsAnalyzer {
		public object m_good = new object();
		public int[] m_good2 = new int[] { 1, 2, 3 };
		public IEnumerable<int> m_good3 = new List<int> { };
		public IEnumerable<int> m_good4 = ImmutableArray.Create( 1, 2, 3 );
		public ImmutableArray<int> m_good5 = ImmutableArray<int>.Empty;
		public int[] m_good6 = new int[0](); // FYI: not an ObjectionCreationSyntax - there is a distinct ArrayCreationSyntax

		public ImmutableArray<int> m_neutral = new ImmutableArray<int>( 1 ); // we don't need to report for this, there is no such constructor.
		public ImmutableArray<int> m_neutral2 = new ImmutableArray<int>( new int[] { 1, 2, 3 } ); // this constructor is internal

		public IEnumerable<int> m_bad = /* DontUseImmutableArrayConstructor */ new ImmutableArray<int> { 1, 2, 3 } /**/;
		public ImmutableArray<int> m_bad2 = /* DontUseImmutableArrayConstructor */ new ImmutableArray<int> { 1, 2, 3 } /**/;
		public ImmutableArray<int> m_bad2 = /* DontUseImmutableArrayConstructor */ new ImmutableArray<int> {} /**/;
		public ImmutableArray<int> m_bad2 = /* DontUseImmutableArrayConstructor */ new ImmutableArray<int>() /**/;

		// not handled, but not likely?
		public ImmutableArray<int> m_notCurrentlyHandled = default( ImmutableArray<int> );

		public static void SomeMethod() {
			var bad = /* DontUseImmutableArrayConstructor */ new ImmutableArray<int>() /**/;
			var good = ImmutableArray.Create( 1, 2, 3 );
			var good2 = new object();
		}

		public static T GenericMethod<T>() where T : new() {
			return new T();
		}

		public void SomeOtherMethod() {
			// We don't catch this. In general doing so would be too
			// complicated for an analyzer: we'd need to look up the defn of
			// GenericMethod, do data-flow analysis etc.
			var whoops = GenericMethod<ImmutableArray<int>>();
		}
	}
}


<|start_filename|>tests/D2L.CodeStyle.Analyzers.Test/Specs/StructShouldBeReadonlyAnalyzer.cs<|end_filename|>
﻿// analyzer: D2L.CodeStyle.Analyzers.Language.StructShouldBeReadonlyAnalyzer

namespace D2L.CodeStyle.Analyzers.Specs.Good {

	public readonly struct EmptyStruct { }

	public readonly struct StructWithFieldsAndMembers {

		public const int ConstField = 0;
		public readonly int ReadonlyField = 0;
		public int ReadonlyProperty { get; } = 0;

	}

	public readonly partial struct PartialStructWithFieldsAndMembers {

		public const int ConstField = 0;
		public readonly int ReadonlyField = 0;
		public int ReadonlyProperty { get; } = 0;

	}

	partial struct PartialStructWithFieldsAndMembers { }

}

namespace D2L.CodeStyle.Analyzers.Specs.Sad {

	public struct StructWithFieldsAndMembers {

		public int Field = 0;
		public int Property { get; set; } = 0;

	}

}

namespace D2L.CodeStyle.Analyzers.Specs.Bad {

	public struct /* StructShouldBeReadonly(EmptyStruct) */ EmptyStruct /**/ { }

	public struct /* StructShouldBeReadonly(StructWithFieldsAndMembers) */ StructWithFieldsAndMembers /**/ {

		public const int ConstField = 0;
		public readonly int ReadonlyField = 0;
		public int ReadonlyProperty { get; } = 0;

	}

	public partial struct /* StructShouldBeReadonly(PartialStructWithFieldsAndMembers) */ PartialStructWithFieldsAndMembers /**/ {

		public const int ConstField = 0;
		public readonly int ReadonlyField = 0;
		public int ReadonlyProperty { get; } = 0;

	}

	partial struct PartialStructWithFieldsAndMembers {}

}


<|start_filename|>tests/D2L.CodeStyle.Analyzers.Test/Extensions/TypeSymbolExtensionsTests.cs<|end_filename|>
﻿using NUnit.Framework;
using static D2L.CodeStyle.Analyzers.RoslynSymbolFactory;

namespace D2L.CodeStyle.Analyzers.Extensions {

	[TestFixture]
	internal sealed class TypeSymbolExtensionsTests {

		[Test]
		[TestCase( "string", "System.String", Description = "special non-generic type" )]
		[TestCase( "String", "System.String", Description = "non-special non-generic type" )]
		[TestCase( "Lazy<string>", "System.Lazy", Description = "special generic type" )]
		[TestCase( "Lazy<String>", "System.Lazy", Description = "non-special generic type" )]
		public void GetFullTypeName_ReturnsCorrectValue( string typeName, string expected ) {
			var type = Field( typeName + " name;" ).Symbol.Type;

			var actual = type.GetFullTypeName();

			Assert.AreEqual( expected, actual );
		}

		[Test]
		[TestCase( "string", "System.String", Description = "special non-generic type" )]
		[TestCase( "String", "System.String", Description = "non-special non-generic type" )]
		[TestCase( "Lazy<string>", "System.Lazy<System.String>", Description = "special generic type" )]
		[TestCase( "Lazy<String>", "System.Lazy<System.String>", Description = "non-special generic type" )]
		public void GetFullTypeNameWithGenericArguments_ReturnsCorrectValue( string typeName, string expected ) {
			var type = Field( typeName + " name;" ).Symbol.Type;

			var actual = type.GetFullTypeNameWithGenericArguments();

			Assert.AreEqual( expected, actual );
		}
	}
}


<|start_filename|>src/D2L.CodeStyle.Analyzers/ApiUsage/Events/EventHandlersDisallowedListAnalyzer.cs<|end_filename|>
﻿using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using D2L.CodeStyle.Analyzers.Extensions;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;

namespace D2L.CodeStyle.Analyzers.ApiUsage.Events {

	[DiagnosticAnalyzer( LanguageNames.CSharp )]
	internal sealed class EventHandlersDisallowedListAnalyzer : DiagnosticAnalyzer {

		public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(
				Diagnostics.EventHandlerDisallowed
			);

		public override void Initialize( AnalysisContext context ) {

			context.EnableConcurrentExecution();
			context.ConfigureGeneratedCodeAnalysis( GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics );
			context.RegisterCompilationStartAction( RegisterAnalysis );
		}

		private void RegisterAnalysis( CompilationStartAnalysisContext context ) {

			Compilation compilation = context.Compilation;

			IImmutableSet<INamedTypeSymbol> disallowedTypes = GetDisallowedTypes( compilation );
			if( disallowedTypes.Count == 0 ) {
				return;
			}

			context.RegisterSyntaxNodeAction(
					c => AnalyzeSimpleBaseType( c, disallowedTypes ),
					SyntaxKind.SimpleBaseType
				);
		}

		private void AnalyzeSimpleBaseType(
				SyntaxNodeAnalysisContext context,
				IImmutableSet<INamedTypeSymbol> disallowedTypes
			) {

			SimpleBaseTypeSyntax baseTypeSyntax = (SimpleBaseTypeSyntax)context.Node;
			SymbolInfo baseTypeSymbol = context.SemanticModel.GetSymbolInfo( baseTypeSyntax.Type );

			INamedTypeSymbol baseSymbol = ( baseTypeSymbol.Symbol as INamedTypeSymbol );
			if( baseSymbol.IsNullOrErrorType() ) {
				return;
			}

			if( !disallowedTypes.Contains( baseSymbol ) ) {
				return;
			}

			Diagnostic diagnostic = Diagnostic.Create(
					Diagnostics.EventHandlerDisallowed,
					baseTypeSyntax.GetLocation(),
					baseSymbol.ToDisplayString()
				);

			context.ReportDiagnostic( diagnostic );
		}

		private static IImmutableSet<INamedTypeSymbol> GetDisallowedTypes( Compilation compilation ) {

			IImmutableSet<INamedTypeSymbol> types = EventHandlersDisallowedList.DisallowedTypes
				.SelectMany( genericType => GetGenericTypes( compilation, genericType.Key, genericType.Value ) )
				.ToImmutableHashSet();

			return types;
		}

		private static IEnumerable<INamedTypeSymbol> GetGenericTypes(
				Compilation compilation,
				string genericTypeName,
				ImmutableArray<ImmutableArray<string>> genericTypeArgumentSets
			) {

			INamedTypeSymbol genericTypeDefinition = compilation.GetTypeByMetadataName( genericTypeName );
			if( genericTypeDefinition.IsNullOrErrorType() ) {
				yield break;
			}

			foreach( ImmutableArray<string> genericTypeArguments in genericTypeArgumentSets ) {
				if( TryGetGenericType( compilation, genericTypeDefinition, genericTypeArguments, out INamedTypeSymbol genericType ) ) {
					yield return genericType;
				}
			}
		}

		private static bool TryGetGenericType(
				Compilation compilation,
				INamedTypeSymbol genericTypeDefinition,
				ImmutableArray<string> genericTypeArguments,
				out INamedTypeSymbol genericType
			) {

			INamedTypeSymbol[] genericTypeArgumentSymbols = new INamedTypeSymbol[ genericTypeArguments.Length ];

			for( int i = 0; i < genericTypeArguments.Length; i++ ) {
				string genericTypeArgumentName = genericTypeArguments[ i ];

				INamedTypeSymbol genericTypeArgumentSymbol = compilation.GetTypeByMetadataName( genericTypeArgumentName );
				if( genericTypeArgumentSymbol.IsNullOrErrorType() ) {

					genericType = null;
					return false;
				}

				genericTypeArgumentSymbols[ i ] = genericTypeArgumentSymbol;
			}

			genericType = genericTypeDefinition.Construct( genericTypeArgumentSymbols );
			if( genericType.IsNullOrErrorType() ) {

				genericType = null;
				return false;
			}

			return true;
		}
	}
}


<|start_filename|>src/D2L.CodeStyle.Analyzers/ApiUsage/ContentPhysicalPaths/ILegacyLpContentDirectoryFullNameAnalyzer.cs<|end_filename|>
﻿using System.Collections.Immutable;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;

namespace D2L.CodeStyle.Analyzers.ApiUsage.ContentPhysicalPaths {

	[DiagnosticAnalyzer( LanguageNames.CSharp )]
	internal sealed class ILegacyLpContentDirectoryFullNameAnalyzer : DiagnosticAnalyzer {

		private const string TypeName = "D2L.Files.ILegacyLpContentDirectory";
		private const string PropertyName = "FullName";

		public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(
				PhysicalPathPropertyAnalysis.DiagnosticDescriptor
			);

		public override void Initialize( AnalysisContext context ) {
			context.EnableConcurrentExecution();
			context.ConfigureGeneratedCodeAnalysis( GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics );

			PhysicalPathPropertyAnalysis analysis = new PhysicalPathPropertyAnalysis(
					TypeName,
					PropertyName,
					AllowedTypes
				);

			analysis.Initialize( context );
		}

		/// <summary>
		/// A list of types that already contain ILegacyLpContentDirectory.FullName references
		/// </summary>
		private static readonly IImmutableSet<string> AllowedTypes = ImmutableHashSet.Create<string>(
				"D2L.Files.Content.ContentDirectory",
				"D2L.Files.Compression.Archive",
				"D2L.Files.Content.ContentFile",
				"D2L.Files.FileSystemObjectNameUtility",
				"D2L.Files.FileSystemObjectWrapper",
				"D2L.PlatformTools.ManageFiles.BusinessLayer.Domain.DirectoryEntityInternal",
				"D2L.PlatformTools.ManageFiles.BusinessLayer.Domain.FileSystemManager",
				"D2L.PlatformTools.ManageFiles.Webpages.Isf.MyComputer",
				"D2L.Lms.CourseExport.CoursePackage",
				"D2L.Integration.LOR_LE.Webpages.CourseBuilder.Rpcs"
			);
	}
}


<|start_filename|>src/D2L.CodeStyle.Analyzers/ApiUsage/DependencyInjection/Domain/NonGenericRegisterExpression.cs<|end_filename|>
﻿using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;

namespace D2L.CodeStyle.Analyzers.ApiUsage.DependencyInjection.Domain {
	// void Register( Type dependencyType, Type concreteType, ObjectScope scope );
	internal sealed class NonGenericRegisterExpression : DependencyRegistrationExpression {
		internal override bool CanHandleMethod( IMethodSymbol method ) {
			return method.Name == "Register"
				&& !method.IsGenericMethod
				&& method.Parameters.Length == 3;
		}

		internal override DependencyRegistration GetRegistration( IMethodSymbol method, SeparatedSyntaxList<ArgumentSyntax> arguments, SemanticModel semanticModel ) {
			if( arguments.Count != 3 ) {
				return null;
			}

			// This only handles the case where `typeof` is used inline.
			// This sucks, but there are very few usages of this method (it's
			// only used in open-generic types), so we don't care.
			//		r.Register( typeof(IFoo), typeof(Foo), ObjectScope.Singleton );
			var dependencyTypeExpression = arguments[0].Expression as TypeOfExpressionSyntax;
			if( dependencyTypeExpression == null ) {
				return null;
			}
			var dependencyType = semanticModel.GetSymbolInfo( dependencyTypeExpression.Type ).Symbol as ITypeSymbol;

			var concreteTypeExpression = arguments[1].Expression as TypeOfExpressionSyntax;
			if( concreteTypeExpression == null ) {
				return null;
			}
			var concreteType = semanticModel.GetSymbolInfo( concreteTypeExpression.Type ).Symbol as ITypeSymbol;

			ObjectScope scope;
			if( !TryGetObjectScope( arguments[2], semanticModel, out scope ) ) {
				return null;
			}

			return DependencyRegistration.NonFactory( scope, dependencyType, concreteType );
		}
	}

}


<|start_filename|>src/D2L.CodeStyle.Analyzers/ApiUsage/DangerousMemberUsages/DangerousProperties.cs<|end_filename|>
﻿using System.Collections.Generic;
using System.Collections.Immutable;

namespace D2L.CodeStyle.Analyzers.ApiUsage.DangerousMemberUsages {

	internal static class DangerousProperties {

		internal static readonly IReadOnlyDictionary<string, ImmutableArray<string>> Definitions =
			ImmutableDictionary.Create<string, ImmutableArray<string>>()
			.Add(
				"System.Net.ServicePointManager",
				ImmutableArray.Create(
					"CertificatePolicy",
					"CheckCertificateRevocationList",
					"DefaultConnectionLimit",
					"DnsRefreshTimeout",
					"EnableDnsRoundRobin",
					"EncryptionPolicy",
					"Expect100Continue",
					"MaxServicePointIdleTime",
					"MaxServicePoints",
					"ReusePort",
					"SecurityProtocol",
					"ServerCertificateValidationCallback",
					"UseNagleAlgorithm"
				)
			)
			//Bazel caching requires knowledge of inputs, and verification that they haven't changed. If inputs have changed, it does not use the cache.
			//The usage of the assembly version number property prevents Bazel from caching because on each CI build, the version number is different
			//resulting in Bazel never using the cache. 
			.Add(
				"System.Reflection.AssemblyName",
				ImmutableArray.Create(
					"Version"
				)
			)
			.Add(
				"System.Web.HttpContext",
				ImmutableArray.Create(
					"Current",
					"CurrentHandler",
					"Handler"
				)
			);

		private static ImmutableDictionary<string, ImmutableArray<string>> Add<T>(
				this ImmutableDictionary<string, ImmutableArray<string>> types,
				params string[] propertyNames
			) {

			return types.Add(
				typeof( T ).FullName,
				ImmutableArray.Create( propertyNames )
			);
		}
	}
}