Skip to content

Commit

Permalink
Stride Diagnostics Analyzer (#1864)
Browse files Browse the repository at this point in the history
* Set up analyzer project to be included in every project

* Create basic structure

* Add Tests project

* Add STRDIAG003

* add STRDIAG004

* add STRDIAG005

* add STRDIAG006

* cleanup with wellknownreferences

* cleanup code

* add strdiag007

* format

* empty line removed

* expand references

* add attributehelper

* addSTRDIAG008

* add strdiag000

* repair assign mode

* repair STRDIAG000

* fix strdiag000

* fix strdiag008

* improve strdiag004

* finally fixed with originaldefinition

* Performance update

* fix delegate check

* fix delegate comparison on delegate

* add comment for Updatable attribute

* make HasAttribute an extension method and remove attributehelper

* change strdiag003 error message

* update rules for STRDIAG004

* rename strdiag005

* change strdiag005 error message

* rename to Serialization category

* refactor AnalyzeField

* remove useless null checks

* rename class to match file name

* fix error messages

* remove try catch

* update error message

* update delegate error message

* add missing accessibility check

* add missing accesibility check

* add missing accesibility check

* make accessibility check extension method

* fix readonly member, negate isvisible

* fix title

* cleanup ifs

* project cleanup

* reorganize if statements

* extract if

* fix spacing

* remove unused parameter

* fix style

* remove unused paramter

* add some tests

* rename files

* more tests

* add tests, fix typos

* add todo

* rename tests

* cleanup

* addd invalid content mode

* cleanup analyzers

* cleanup tests

* fix tests and bugs

* update test

* add edge cases, update tests, cleanup

* add rest of the tests

* add updatable test

* update comments

* make repot diagnostics an extension method

* add immutable comment

* compiler utils cleanup

* format cleanup

* upgrade to latest roslyn

* remove folder

* remove visibility , add using directive for debugger

* add reason

* renamed template

* remove String

* rename tests

* named parameter, bump version of test project

* add comments

* fix and strdiag001

* add strdiag009

* add strdiag001 tests

* make class proper accessor for serialization rules

* alter message

* add pragma disable

* update message

* add pragma

* fix test

* add strdiag009 tests

* add enums to supported values

* convert title to fit to datacontract title

* add links

* add link

* add missing help link

---------

Co-authored-by: Marian Dziubiak <marian.dziubiak@gmail.com>
Co-authored-by: IXLLEGACYIXL <ixllegacy123@outlook.com>
  • Loading branch information
3 people committed Oct 17, 2023
1 parent cee7491 commit a2dcf78
Show file tree
Hide file tree
Showing 36 changed files with 1,499 additions and 17 deletions.
@@ -0,0 +1,51 @@
using Stride.Core.CompilerServices.Analyzers;
using Xunit;

namespace Stride.Core.CompilerServices.Tests.AnalyzerTests;

public class STRDIAG000_Test
{
[Fact]
public void Error_On_Attribute_Contradiction_On_Property()
{
string sourceCode = string.Format(ClassTemplates.BasicClassTemplate, "[DataMemberIgnore][DataMember]public int Value { get; set; }");
TestHelper.ExpectDiagnosticsError(sourceCode, STRDIAG000AttributeContradiction.DiagnosticId);
}

[Fact]
public void Error_On_Attribute_Contradiction_On_Field()
{
string sourceCode = string.Format(ClassTemplates.BasicClassTemplate, "[DataMemberIgnore][DataMember]public int Value;");
TestHelper.ExpectDiagnosticsError(sourceCode, STRDIAG000AttributeContradiction.DiagnosticId);
}

[Fact]
public void NoErrorOn_Attribute_Contradiction_With_Updatable()
{
string sourceCode = @"
using System;
using Stride.Core;
using Stride.Updater;
namespace Stride.Updater
{
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false, Inherited = true)]
public class DataMemberUpdatableAttribute : Attribute
{
}
}
namespace Test
{
[DataContract]
public class TripleAnnotation
{
[DataMemberIgnore]
[DataMemberUpdatable]
[DataMember]
public int Value;
}
}
";
TestHelper.ExpectNoDiagnosticsErrors(sourceCode);
}
}
@@ -0,0 +1,37 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Stride.Core.CompilerServices.Analyzers;
using Xunit;

namespace Stride.Core.CompilerServices.Tests.AnalyzerTests;
public class STRDIAG001_Test
{
[Fact]
public void Error_On_Private_Inner_Class_with_DataContract()
{
string sourceCode = string.Format(ClassTemplates.BasicClassTemplate, "[DataContract] private class InnerClass { }");
TestHelper.ExpectDiagnosticsError(sourceCode, STRDIAG001InvalidDataContract.DiagnosticId);
}
[Fact]
public void No_Error_On_Private_Inner_Class_without_DataContract()
{
string sourceCode = string.Format(ClassTemplates.BasicClassTemplate, "private class InnerClass { }");
TestHelper.ExpectNoDiagnosticsErrors(sourceCode);
}
// TODO: Enable with .NET8 merge as we need a higher C# version
[Fact(Skip = "file scoped classes won't compile")]
public void Error_On_file_scope_Class_with_DataContract()
{
string sourceCode = "using Stride.Core; [DataContract] file class FileScopeClass { }";
TestHelper.ExpectDiagnosticsError(sourceCode, STRDIAG001InvalidDataContract.DiagnosticId);
}
[Fact(Skip = "file scoped classes won't compile")]
public void No_Error_On_file_scope_Class_without_DataContract()
{
string sourceCode = "using Stride.Core; file class FileScopeClass { }";
TestHelper.ExpectNoDiagnosticsErrors(sourceCode);
}
}
@@ -0,0 +1,14 @@
using Stride.Core.CompilerServices.Analyzers;
using Xunit;

namespace Stride.Core.CompilerServices.Tests.AnalyzerTests;

public class STRDIAG002_Test
{
[Fact]
public void Error_On_Attribute_Contradiction_On_Property()
{
string sourceCode = string.Format(ClassTemplates.BasicClassTemplate, "[DataMember(DataMemberMode.Content)]public int Value { get; set; }");
TestHelper.ExpectDiagnosticsError(sourceCode, STRDIAG002InvalidContentMode.DiagnosticId);
}
}
@@ -0,0 +1,49 @@
using Stride.Core.CompilerServices.Analyzers;
using Xunit;

namespace Stride.Core.CompilerServices.Tests.AnalyzerTests;

public class STRDIAG003_Test
{
[Fact]
public void Error_On_Datamember_With_private_InaccessibleMember_On_Property()
{
string sourceCode = string.Format(ClassTemplates.BasicClassTemplate, "[DataMember] private int Value { get; set; }");
TestHelper.ExpectDiagnosticsError(sourceCode, STRDIAG003InaccessibleMember.DiagnosticId);
}

[Fact]
public void Error_On_Datamember_With_private_InaccessibleMember_On_Field()
{
string sourceCode = string.Format(ClassTemplates.BasicClassTemplate, "[DataMember] private int Value = 0;");
TestHelper.ExpectDiagnosticsError(sourceCode, STRDIAG003InaccessibleMember.DiagnosticId);
}

[Fact]
public void Error_On_Datamember_With_protected_InaccessibleMember_On_Property()
{
string sourceCode = string.Format(ClassTemplates.BasicClassTemplate, "[DataMember] protected int Value { get; set; }");
TestHelper.ExpectDiagnosticsError(sourceCode, STRDIAG003InaccessibleMember.DiagnosticId);
}

[Fact]
public void Error_On_Datamember_With_protected_InaccessibleMember_On_Field()
{
string sourceCode = string.Format(ClassTemplates.BasicClassTemplate, "[DataMember] protected int Value = 0;");
TestHelper.ExpectDiagnosticsError(sourceCode, STRDIAG003InaccessibleMember.DiagnosticId);
}

[Fact]
public void Error_On_Datamember_With_private_protected_InaccessibleMember_On_Property()
{
string sourceCode = string.Format(ClassTemplates.BasicClassTemplate, "[DataMember] private protected int Value { get; set; }");
TestHelper.ExpectDiagnosticsError(sourceCode, STRDIAG003InaccessibleMember.DiagnosticId);
}

[Fact]
public void Error_On_Datamember_With_private_protected_InaccessibleMember_On_Field()
{
string sourceCode = string.Format(ClassTemplates.BasicClassTemplate, "[DataMember] private protected int Value = 0;");
TestHelper.ExpectDiagnosticsError(sourceCode, STRDIAG003InaccessibleMember.DiagnosticId);
}
}
@@ -0,0 +1,35 @@
using Stride.Core.CompilerServices.Analyzers;
using Xunit;

namespace Stride.Core.CompilerServices.Tests.AnalyzerTests;

public class STRDIAG004_Test
{
[Fact]
public void Error_On_No_Get_Property()
{
string sourceCode = string.Format(ClassTemplates.BasicClassTemplate, "[DataMember] public int Value { set; }");
TestHelper.ExpectDiagnosticsError(sourceCode, STRDIAG004PropertyWithNoGetter.DiagnosticId);
}

[Fact]
public void Error_On_private_Get_Property()
{
string sourceCode = string.Format(ClassTemplates.BasicClassTemplate, "[DataMember] public int Value { private get; set; }");
TestHelper.ExpectDiagnosticsError(sourceCode, STRDIAG004PropertyWithNoGetter.DiagnosticId);
}

[Fact]
public void Error_On_protected_Get_Property()
{
string sourceCode = string.Format(ClassTemplates.BasicClassTemplate, "[DataMember] public int Value { protected get; set; }");
TestHelper.ExpectDiagnosticsError(sourceCode, STRDIAG004PropertyWithNoGetter.DiagnosticId);
}

[Fact]
public void Error_On_private_protected_Get_Property()
{
string sourceCode = string.Format(ClassTemplates.BasicClassTemplate, "[DataMember] public int Value { private protected get; set; }");
TestHelper.ExpectDiagnosticsError(sourceCode, STRDIAG004PropertyWithNoGetter.DiagnosticId);
}
}
@@ -0,0 +1,35 @@
using Stride.Core.CompilerServices.Analyzers;
using Xunit;

namespace Stride.Core.CompilerServices.Tests.AnalyzerTests;

public class STRDIAG005_Test
{
[Fact]
public void Error_On_string_readonly_Property()
{
string sourceCode = string.Format(ClassTemplates.BasicClassTemplate, "[DataMember] public string Value { get; }");
TestHelper.ExpectDiagnosticsError(sourceCode, STRDIAG005ReadonlyMemberTypeIsNotSupported.DiagnosticId);
}

[Fact]
public void Error_On_string_readonly_Field()
{
string sourceCode = string.Format(ClassTemplates.BasicClassTemplate, "[DataMember] public readonly string Value;");
TestHelper.ExpectDiagnosticsError(sourceCode, STRDIAG005ReadonlyMemberTypeIsNotSupported.DiagnosticId);
}

[Fact]
public void Error_On_int_readonly_Property()
{
string sourceCode = string.Format(ClassTemplates.BasicClassTemplate, "[DataMember] public int Value { get; }");
TestHelper.ExpectDiagnosticsError(sourceCode, STRDIAG005ReadonlyMemberTypeIsNotSupported.DiagnosticId);
}

[Fact]
public void Error_On_int_readonly_Field()
{
string sourceCode = string.Format(ClassTemplates.BasicClassTemplate, "[DataMember] public readonly int Value;");
TestHelper.ExpectDiagnosticsError(sourceCode, STRDIAG005ReadonlyMemberTypeIsNotSupported.DiagnosticId);
}
}
@@ -0,0 +1,35 @@
using Stride.Core.CompilerServices.Analyzers;
using Xunit;

namespace Stride.Core.CompilerServices.Tests.AnalyzerTests;
public class STRDIAG006_Test
{
[Fact]
public void Error_On_No_Set_AssignMode()
{
string sourceCode = string.Format(ClassTemplates.BasicClassTemplate, "[DataMember(DataMemberMode.Assign)] public int Value { get; }");
TestHelper.ExpectDiagnosticsError(sourceCode, STRDIAG006InvalidAssignMode.DiagnosticId);
}

[Fact]
public void Error_On_private_Set_AssignMode()
{
string sourceCode = string.Format(ClassTemplates.BasicClassTemplate, "[DataMember(DataMemberMode.Assign)] public int Value { get; private set; }");
TestHelper.ExpectDiagnosticsError(sourceCode, STRDIAG006InvalidAssignMode.DiagnosticId);
}

[Fact]
public void Error_On_protected_Set_AssignMode()
{
string sourceCode = string.Format(ClassTemplates.BasicClassTemplate, "[DataMember(DataMemberMode.Assign)] public int Value { get; protected set; }");
TestHelper.ExpectDiagnosticsError(sourceCode, STRDIAG006InvalidAssignMode.DiagnosticId);

}

[Fact]
public void Error_On_private_protected_Set_AssignMode()
{
string sourceCode = string.Format(ClassTemplates.BasicClassTemplate, "[DataMember(DataMemberMode.Assign)] public int Value { get; private protected set; }");
TestHelper.ExpectDiagnosticsError(sourceCode, STRDIAG006InvalidAssignMode.DiagnosticId);
}
}
@@ -0,0 +1,21 @@
using Stride.Core.CompilerServices.Analyzers;
using Xunit;

namespace Stride.Core.CompilerServices.Tests.AnalyzerTests;

public class STRDIAG007_Test
{
[Fact]
public void Error_On_DataMembered_Delegate_Field()
{
string sourceCode = string.Format(ClassTemplates.BasicClassTemplate, "[DataMember] public Action Value;");
TestHelper.ExpectDiagnosticsError(sourceCode, STRDIAG007DataMemberOnDelegate.DiagnosticId);
}

[Fact]
public void Error_On_DataMembered_Delegate_Property()
{
string sourceCode = string.Format(ClassTemplates.BasicClassTemplate, "[DataMember] public Action Value { get; set; }");
TestHelper.ExpectDiagnosticsError(sourceCode, STRDIAG007DataMemberOnDelegate.DiagnosticId);
}
}
@@ -0,0 +1,23 @@
using Stride.Core.CompilerServices.Analyzers;
using Xunit;

namespace Stride.Core.CompilerServices.Tests.AnalyzerTests;

public class STRDIAG008_Test
{
[Fact]
public void Error_On_DataMembered_Delegate_Property()
{
string sourceCode = @"
using Stride.Core;
using System;
[DataContract]
public unsafe struct B
{
[DataMember]
public fixed byte T[12];
}
";
TestHelper.ExpectDiagnosticsError(sourceCode, STRDIAG008FixedFieldInStructs.DiagnosticId);
}
}
@@ -0,0 +1,47 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;
using Stride.Core.CompilerServices.Analyzers;
using Xunit;

namespace Stride.Core.CompilerServices.Tests.AnalyzerTests;
public class STRDIAG009_Test
{
private static string[] Types = new string[]
{
"string",
"int",
"float",
"double",
};
[Fact]
public void No_Error_On_Immutable_Types()
{
foreach(var type in Types)
{
string sourceCode = string.Format(ClassTemplates.BasicClassTemplate, $"[DataMember] public System.Collections.Generic.Dictionary<{type},object> Value {{ get; }}");
TestHelper.ExpectNoDiagnosticsErrors(sourceCode);
}
}
[Fact]
public void No_Error_On_Enum_As_Key()
{
string sourceCode = string.Format(ClassTemplates.BasicClassTemplate, $"public enum TestEnumKey {{ Yes,No }}[DataMember] public System.Collections.Generic.Dictionary<TestEnumKey,object> Value {{ get; }}");
TestHelper.ExpectNoDiagnosticsErrors(sourceCode);
}
[Fact]
public void Error_On_Reference_Type()
{
string sourceCode = string.Format(ClassTemplates.BasicClassTemplate, "[DataMember] public System.Collections.Generic.Dictionary<object,object> Value { get; }");
TestHelper.ExpectDiagnosticsError(sourceCode, STRDIAG009InvalidDictionaryKey.DiagnosticId);
}
[Fact]
public void Error_On_Interface_Type()
{
string sourceCode = string.Format(ClassTemplates.BasicClassTemplate, "[DataMember] public System.Collections.Generic.Dictionary<ICloneable,object> Value { get; }");
TestHelper.ExpectDiagnosticsError(sourceCode, STRDIAG009InvalidDictionaryKey.DiagnosticId);
}
}

0 comments on commit a2dcf78

Please sign in to comment.