Skip to content

Commit d9e140f

Browse files
committed
Added extension keyword ban analyzer
* There are just too many things outside the compiler itself, including first party analyzers, that don't work correctly with that feature to support it yet. - The analyzer tests are written but can't be used yet due to a bug in the previoew releases of the NuGet packages. * Renamed analysers to reflect repository analysis level * Updated for latest testing fromework - Assembly level explicit parallelization is required - For now, all are set to the default to silence the error/warning - Changed DistinctProcessMethod to support async execution - Resolved latest round of test analyzer errors - Use `Assert.Contains` instead of `Assert.IsTrue(x.contains(...))` - Use `Assert.HasCount()` instead of `Assert.AreEqual(n, x.Count)` * Applied nullability attributes to implicit converters for `LazyEncodedString` - Allows implicit conversions of non-null strings to avoid nullability warnings/errors. * Added explicit template arg for `Assert.AreEqual<string>` for cases validating a LazyEncodedString as implicit casting isn't consider anymore apparently... * Updated CLassCleanup attribute for tests as it seems there's yet another breaking change for those...
1 parent 70bb2ee commit d9e140f

49 files changed

Lines changed: 577 additions & 817 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Directory.Packages.props

Lines changed: 45 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,54 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3-
<!--
4-
Global references are included in ALL projects in this repository
5-
-->
6-
<ItemGroup>
7-
<GlobalPackageReference Include="Ubiquity.NET.Versioning.Build.Tasks" Version="5.0.7-alpha.0.1" />
8-
<GlobalPackageReference Include="IDisposableAnalyzers" Version="4.0.8" Condition="'$(NoCommonAnalyzers)' !=' true'" />
9-
<GlobalPackageReference Include="MustUseRetVal" Version="0.0.2" Condition="'$(NoCommonAnalyzers)' !=' true'" />
10-
113
<!--
12-
NOTE: This analyzer is sadly, perpetually in "pre-release mode". There have been many issues/discussion on the point
13-
and it has all fallen on deaf ears. So policies regarding "NO-Prerelease" components need to be overruled on this one
4+
Global references are included in ALL projects in this repository
145
-->
15-
<GlobalPackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.556" Condition="'$(UseStyleCop)' != 'false'" />
16-
</ItemGroup>
17-
<!--
6+
<ItemGroup>
7+
<GlobalPackageReference Include="Ubiquity.NET.Versioning.Build.Tasks" Version="5.0.7-alpha.0.1" />
8+
<GlobalPackageReference Include="IDisposableAnalyzers" Version="4.0.8" Condition="'$(NoCommonAnalyzers)' !=' true'" />
9+
<GlobalPackageReference Include="MustUseRetVal" Version="0.0.2" Condition="'$(NoCommonAnalyzers)' !=' true'" />
10+
<!--
11+
NOTE: This analyzer is sadly, perpetually in "pre-release mode". There have been many issues/discussion on the point
12+
and it has all fallen on deaf ears. So policies regarding "NO-Prerelease" components need to be overruled on this one
13+
-->
14+
<GlobalPackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.556" Condition="'$(UseStyleCop)' != 'false'" />
15+
</ItemGroup>
16+
<!--
1817
Package versions made consistent across all packages referenced in this repository
19-
-->
20-
<ItemGroup>
21-
<!-- Roslyn Analyzers ***MUST*** target older framework -->
22-
<PackageVersion Include="AnsiCodes" Version="0.2.1" />
23-
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="4.14.0" />
24-
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.14.0" />
25-
<PackageVersion Include="Microsoft.CodeAnalysis" Version="4.14.0" />
26-
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Features" Version="4.14.0" />
27-
<PackageVersion Include="Microsoft.CodeAnalysis.VisualBasic.Features" Version="4.14.0" />
28-
<PackageVersion Include="Basic.Reference.Assemblies.Net80" Version="1.8.3" />
29-
<PackageVersion Include="System.Buffers" Version="4.6.1" />
30-
<PackageVersion Include="System.Collections.Immutable" Version="9.0.8" />
31-
<PackageVersion Include="System.CommandLine" Version="2.0.0-beta7.25380.108" />
18+
-->
19+
<ItemGroup>
20+
<!-- Roslyn Analyzers ***MUST*** target older framework -->
21+
<PackageVersion Include="AnsiCodes" Version="0.2.1" />
22+
<PackageVersion Include="Microsoft.CodeAnalysis" Version="4.14.0" />
23+
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="4.14.0" />
24+
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.14.0" />
25+
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Features" Version="4.14.0" />
26+
<PackageVersion Include="Microsoft.CodeAnalysis.VisualBasic.Features" Version="4.14.0" />
27+
<PackageVersion Include="Basic.Reference.Assemblies.Net80" Version="1.8.3" />
28+
<PackageVersion Include="System.Buffers" Version="4.6.1" />
29+
<PackageVersion Include="System.Collections.Immutable" Version="9.0.8" />
30+
<PackageVersion Include="System.CommandLine" Version="2.0.0-beta7.25380.108" />
3231

33-
<!-- Workaround(2): https://github.com/dotnet/roslyn-sdk/issues/1191 -->
34-
<PackageVersion Include="System.Formats.Asn1" Version="9.0.8" />
32+
<!-- Workaround(2): https://github.com/dotnet/roslyn-sdk/issues/1191 -->
33+
<PackageVersion Include="System.Formats.Asn1" Version="9.0.10" />
3534

36-
<!-- Common packages for solution -->
37-
<PackageVersion Include="System.Linq.Async" Version="6.0.3" />
38-
<PackageVersion Include="Ubiquity.NET.LibLLVM" Version="20.1.8" />
39-
<PackageVersion Include="Ubiquity.NET.Versioning" Version="6.0.2-beta" />
40-
<PackageVersion Include="Antlr4BuildTasks" Version="12.10.0" />
41-
<PackageVersion Include="Antlr4.Runtime.Standard" Version="4.13.1" />
42-
<PackageVersion Include="OpenSoftware.DgmlBuilder" Version="2.1.0" />
43-
<PackageVersion Include="CppSharp" Version="1.1.5.3168" />
44-
<PackageVersion Include="System.CodeDom" Version="9.0.7" />
35+
<!-- Common packages for solution -->
36+
<PackageVersion Include="System.Linq.Async" Version="6.0.3" />
37+
<PackageVersion Include="Ubiquity.NET.LibLLVM" Version="20.1.8" />
38+
<PackageVersion Include="Ubiquity.NET.Versioning" Version="6.0.2-beta" />
39+
<PackageVersion Include="Antlr4BuildTasks" Version="12.10.0" />
40+
<PackageVersion Include="Antlr4.Runtime.Standard" Version="4.13.1" />
41+
<PackageVersion Include="OpenSoftware.DgmlBuilder" Version="2.1.0" />
42+
<PackageVersion Include="CppSharp" Version="1.1.5.3168" />
43+
<PackageVersion Include="System.CodeDom" Version="9.0.7" />
4544

46-
<!-- Tests all use the same framework versions -->
47-
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
48-
<PackageVersion Include="MSTest.TestAdapter" Version="3.10.4" />
49-
<PackageVersion Include="MSTest.TestFramework" Version="3.10.4" />
50-
<PackageVersion Include="Tmds.ExecFunction" Version="0.8.0" />
51-
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Analyzer.Testing" Version="1.1.2" />
52-
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.CodeFix.Testing" Version="1.1.2" />
53-
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.CodeRefactoring.Testing" Version="1.1.2" />
54-
</ItemGroup>
45+
<!-- Tests all use the same framework versions -->
46+
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.0.0" />
47+
<PackageVersion Include="MSTest.TestAdapter" Version="4.0.1" />
48+
<PackageVersion Include="MSTest.TestFramework" Version="4.0.1" />
49+
<PackageVersion Include="Tmds.ExecFunction" Version="0.8.0" />
50+
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Analyzer.Testing" Version="1.1.2" />
51+
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.CodeFix.Testing" Version="1.1.2" />
52+
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.CodeRefactoring.Testing" Version="1.1.2" />
53+
</ItemGroup>
5554
</Project>
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Copyright (c) Ubiquity.NET Contributors. All rights reserved.
2+
// Licensed under the Apache-2.0 WITH LLVM-exception license. See the LICENSE.md file in the project root for full license information.
3+
4+
using System.Runtime.InteropServices;
5+
6+
using Microsoft.VisualStudio.TestTools.UnitTesting;
7+
8+
// In SDK-style projects such as this one, several assembly attributes that were historically
9+
// defined in this file are now automatically added during build and populated with
10+
// values defined in project properties. For details of which attributes are included
11+
// and how to customise this process see: https://aka.ms/assembly-info-properties
12+
13+
// Setting ComVisible to false makes the types in this assembly not visible to COM
14+
// components. If you need to access a type in this assembly from COM, set the ComVisible
15+
// attribute to true on that type.
16+
17+
[assembly: ComVisible(false)]
18+
19+
// The following GUID is for the ID of the typelib if this project is exposed to COM.
20+
21+
[assembly: Guid("8ebe7a8e-6ee2-4762-8d6d-333a4497dc96")]
22+
23+
[assembly: Parallelize( Scope = ExecutionScope.ClassLevel )]
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
// Copyright (c) Ubiquity.NET Contributors. All rights reserved.
2+
// Licensed under the Apache-2.0 WITH LLVM-exception license. See the LICENSE.md file in the project root for full license information.
3+
4+
// due to bug in Nuget publication, this can't be tested yet...
5+
// see: https://developercommunity.visualstudio.com/t/VS2026--NET-10-Cant-build-analyzier/10989212
6+
#if NET10_0_OR_GREATER
7+
using System.Threading.Tasks;
8+
9+
using Microsoft.CodeAnalysis;
10+
using Microsoft.CodeAnalysis.CSharp;
11+
using Microsoft.CodeAnalysis.CSharp.Testing;
12+
using Microsoft.CodeAnalysis.Testing;
13+
using Microsoft.VisualStudio.TestTools.UnitTesting;
14+
15+
using static Microsoft.CodeAnalysis.Testing.ReferenceAssemblies;
16+
17+
namespace RepositoryVerifier.UT
18+
{
19+
[TestClass]
20+
public class ExtensionsKeywordAnalyzerTests
21+
{
22+
public TestContext TestContext { get; set; }
23+
24+
[TestMethod]
25+
public async Task EmptySourceAnalyzesClean( )
26+
{
27+
var analyzerTest = CreateTestRunner(string.Empty);
28+
await analyzerTest.RunAsync( TestContext.CancellationTokenSource.Token );
29+
}
30+
31+
[TestMethod]
32+
public async Task UsingKeywordReportsDiagnostic( )
33+
{
34+
var analyzerTest = CreateTestRunner(ExtensionKeywordUsed);
35+
analyzerTest.ExpectedDiagnostics.AddRange(
36+
[
37+
new DiagnosticResult("UNL002", DiagnosticSeverity.Error).WithLocation(5, 5)
38+
]
39+
);
40+
41+
await analyzerTest.RunAsync( TestContext.CancellationTokenSource.Token );
42+
}
43+
44+
[TestMethod]
45+
public async Task StructEquatableIsNotReferenceEquality( )
46+
{
47+
var analyzerTest = CreateTestRunner(NoExtensionKeywordUsed);
48+
49+
// no diagnostics expected
50+
await analyzerTest.RunAsync( TestContext.CancellationTokenSource.Token );
51+
}
52+
53+
private static AnalyzerTest<DefaultVerifier> CreateTestRunner( string source )
54+
{
55+
return new ExtensionKeywordAnalyzerTest()
56+
{
57+
TestState =
58+
{
59+
Sources = { source },
60+
ReferenceAssemblies = Net.Net80
61+
}
62+
};
63+
}
64+
65+
private class ExtensionKeywordAnalyzerTest
66+
: CSharpAnalyzerTest<ExtensionKeywordAnalyzer, DefaultVerifier>
67+
{
68+
protected override ParseOptions CreateParseOptions( )
69+
{
70+
// Until C# 14 and .NET SDK 10 is formally released, it is considered "Preview"
71+
return new CSharpParseOptions(LanguageVersion.Preview, DocumentationMode.Diagnose);
72+
}
73+
}
74+
75+
private const string ExtensionKeywordUsed = """
76+
using System;
77+
78+
public static class TestExtension
79+
{
80+
extension(string self)
81+
{
82+
public string MyExtension()
83+
{
84+
return self;
85+
}
86+
}
87+
}
88+
""";
89+
90+
private const string NoExtensionKeywordUsed = """
91+
using System;
92+
93+
public static class TestExtension
94+
{
95+
public static string MyExtension(this string self)
96+
{
97+
return self;
98+
}
99+
}
100+
""";
101+
}
102+
}
103+
#endif

src/ReferenceEqualityVerifier/ReferenceEqualityVerifier.Test/GlobalSuppressions.cs renamed to src/Analyzers/RepositoryVerifier.UT/GlobalSuppressions.cs

File renamed without changes.

src/ReferenceEqualityVerifier/ReferenceEqualityVerifier.Test/ReferenceEqualityVerifierUnitTests.cs renamed to src/Analyzers/RepositoryVerifier.UT/ReferenceEqualityVerifierUnitTests.cs

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,18 @@
1010

1111
using static Microsoft.CodeAnalysis.Testing.ReferenceAssemblies;
1212

13-
namespace ReferenceEqualityVerifier.Test
13+
namespace RepositoryVerifier.UT
1414
{
1515
[TestClass]
1616
public class ReferenceEqualityVerifierUnitTests
1717
{
18+
public TestContext TestContext { get; set; }
19+
1820
[TestMethod]
1921
public async Task EmptySourceAnalyzesClean( )
2022
{
2123
var analyzerTest = CreateTestRunner(string.Empty);
22-
await analyzerTest.RunAsync();
24+
await analyzerTest.RunAsync( TestContext.CancellationToken );
2325
}
2426

2527
[TestMethod]
@@ -33,7 +35,7 @@ public async Task CommonDirectCasesReportDiagnostics( )
3335
]
3436
);
3537

36-
await analyzerTest.RunAsync();
38+
await analyzerTest.RunAsync( TestContext.CancellationToken );
3739
}
3840

3941
[TestMethod]
@@ -46,7 +48,7 @@ public async Task AdvancedInterfacesReportDiagnostics( )
4648
]
4749
);
4850

49-
await analyzerTest.RunAsync();
51+
await analyzerTest.RunAsync( TestContext.CancellationToken );
5052
}
5153

5254
[TestMethod]
@@ -60,7 +62,7 @@ public async Task EquatableToInterfaceReportsDiagnostic( )
6062
]
6163
);
6264

63-
await analyzerTest.RunAsync();
65+
await analyzerTest.RunAsync( TestContext.CancellationToken );
6466
}
6567

6668
[TestMethod]
@@ -73,7 +75,7 @@ public async Task CommonBaseEquatableReportsDiagnostic( )
7375
]
7476
);
7577

76-
await analyzerTest.RunAsync();
78+
await analyzerTest.RunAsync( TestContext.CancellationToken );
7779
}
7880

7981
[TestMethod]
@@ -82,7 +84,7 @@ public async Task NullCheckDoesNotReportDiagnostics( )
8284
var analyzerTest = CreateTestRunner(CompareWithNull);
8385

8486
// no diagnostics expected (ref equality against null is always a ref equality check!)
85-
await analyzerTest.RunAsync();
87+
await analyzerTest.RunAsync( TestContext.CancellationToken );
8688
}
8789

8890
[TestMethod]
@@ -93,7 +95,7 @@ public async Task NoNamespaceDoesNotReportDiagnostics( )
9395
var analyzerTest = CreateTestRunner(NoNamespaceEquatable);
9496

9597
// no diagnostics expected
96-
await analyzerTest.RunAsync();
98+
await analyzerTest.RunAsync( TestContext.CancellationToken );
9799
}
98100

99101
[TestMethod]
@@ -102,7 +104,7 @@ public async Task StructEquatableIsNotReferenceEquality( )
102104
var analyzerTest = CreateTestRunner(StructEquatable);
103105

104106
// no diagnostics expected
105-
await analyzerTest.RunAsync();
107+
await analyzerTest.RunAsync( TestContext.CancellationToken );
106108
}
107109

108110
[TestMethod]
@@ -114,7 +116,7 @@ public async Task IncompleteSyntaxDoesNotReportDiagnostics( )
114116
new DiagnosticResult("CS1525", DiagnosticSeverity.Error).WithLocation(16,25),
115117
]
116118
);
117-
await analyzerTest.RunAsync();
119+
await analyzerTest.RunAsync( TestContext.CancellationToken );
118120
}
119121

120122
private static AnalyzerTest<DefaultVerifier> CreateTestRunner( string source )

src/ReferenceEqualityVerifier/ReferenceEqualityVerifier.Test/ReferenceEqualityVerifier.Test.csproj renamed to src/Analyzers/RepositoryVerifier.UT/RepositoryVerifier.UT.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
</ItemGroup>
2929

3030
<ItemGroup>
31-
<ProjectReference Include="..\ReferenceEqualityVerifier\ReferenceEqualityVerifier.csproj" />
31+
<ProjectReference Include="..\RepositoryVerifier\RepositoryVerifier.csproj" />
3232
</ItemGroup>
3333

3434
</Project>

src/ReferenceEqualityVerifier/ReferenceEqualityVerifier/AnalyzerReleases.Shipped.md renamed to src/Analyzers/RepositoryVerifier/AnalyzerReleases.Shipped.md

File renamed without changes.

src/ReferenceEqualityVerifier/ReferenceEqualityVerifier/AnalyzerReleases.Unshipped.md renamed to src/Analyzers/RepositoryVerifier/AnalyzerReleases.Unshipped.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ Rule ID | Category | Severity | Notes
77
--------|----------|----------|-------
88
UNL000 | Internal | Error | Diagnostics, [Documentation](AnalyzerException)
99
UNL001 | Usage | Error | Diagnostics
10+
UNL002 | Usage | Error | Diagnostics, [Documentation](Compiler)

src/ReferenceEqualityVerifier/ReferenceEqualityVerifier/Diagnostics.cs renamed to src/Analyzers/RepositoryVerifier/Diagnostics.cs

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@
55

66
using Microsoft.CodeAnalysis;
77

8-
namespace ReferenceEqualityVerifier
8+
namespace RepositoryVerifier
99
{
1010
// The prefix "UNL" used here reflects the constrained usage of this analyzer. It is
11-
// limited to testing types in the "Ubiquity.NET.Llvm" namespace only. It is intentionally
11+
// limited to anylizing source in the "Ubiquity.NET.Llvm" namespace only. It is intentionally
1212
// NOT general purpose.
1313
internal static class Diagnostics
1414
{
@@ -17,10 +17,13 @@ private static LocalizableResourceString Localized( string resName )
1717
return new LocalizableResourceString( resName, Resources.ResourceManager, typeof( Resources ) );
1818
}
1919

20-
internal static ImmutableArray<DiagnosticDescriptor> AllDiagnostics
21-
=> ImmutableArray.Create( RefEqualityWhenEquatable );
20+
internal static ImmutableArray<DiagnosticDescriptor> ReferenceEqualityAnalyzer
21+
=> [InternalError, RefEqualityWhenEquatable ];
2222

23-
internal static readonly DiagnosticDescriptor RefEqualityInternalError = new DiagnosticDescriptor(
23+
internal static ImmutableArray<DiagnosticDescriptor> ExtensionKeywordAnalyzer
24+
=> [InternalError, ExtensionKeywordUsed];
25+
26+
internal static readonly DiagnosticDescriptor InternalError = new(
2427
id: "UNL000",
2528
title: Localized(nameof(Resources.UNL000_Title)),
2629
messageFormat: Localized(nameof(Resources.UNL000_MessageFormat)),
@@ -31,7 +34,7 @@ internal static ImmutableArray<DiagnosticDescriptor> AllDiagnostics
3134
WellKnownDiagnosticTags.AnalyzerException
3235
);
3336

34-
internal static readonly DiagnosticDescriptor RefEqualityWhenEquatable = new DiagnosticDescriptor(
37+
internal static readonly DiagnosticDescriptor RefEqualityWhenEquatable = new(
3538
id: "UNL001",
3639
title: Localized(nameof(Resources.UNL001_Title)),
3740
messageFormat: Localized(nameof(Resources.UNL001_MessageFormat)),
@@ -41,5 +44,16 @@ internal static ImmutableArray<DiagnosticDescriptor> AllDiagnostics
4144
description: Localized(nameof(Resources.UNL001_Description)),
4245
WellKnownDiagnosticTags.Compiler
4346
);
47+
48+
internal static readonly DiagnosticDescriptor ExtensionKeywordUsed = new(
49+
id: "UNL002",
50+
title: Localized(nameof(Resources.UNL002_Title)),
51+
messageFormat: Localized(nameof(Resources.UNL002_MessageFormat)),
52+
category: "Usage",
53+
defaultSeverity: DiagnosticSeverity.Error,
54+
isEnabledByDefault: true,
55+
description: Localized(nameof(Resources.UNL002_Description)),
56+
WellKnownDiagnosticTags.Compiler
57+
);
4458
}
4559
}

0 commit comments

Comments
 (0)