Created
March 9, 2025 13:11
-
-
Save adammyhre/8bbaec4ed012579f7e31a758681b9ae8 to your computer and use it in GitHub Desktop.
Roslyn Fundamentals
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<Project Sdk="Microsoft.NET.Sdk"> | |
<PropertyGroup> | |
<TargetFramework>netstandard2.0</TargetFramework> | |
<LangVersion>latest</LangVersion> | |
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules> | |
</PropertyGroup> | |
<ItemGroup> | |
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4"> | |
<PrivateAssets>all</PrivateAssets> | |
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | |
</PackageReference> | |
<PackageReference ExcludeAssets="runtime" Include="Microsoft.CodeAnalysis.CSharp" Version="4.3.0"/> | |
<PackageReference ExcludeAssets="runtime" Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.3.0"/> | |
</ItemGroup> | |
<Target Name="CustomAfterBuild" AfterTargets="Build"> | |
<ItemGroup> | |
<_FilesToCopy Include="$(OutputPath)**\$(AssemblyName).dll"/> | |
<_FilesToCopy Include="$(OutputPath)**\$(AssemblyName).pdb"/> | |
</ItemGroup> | |
<Copy SourceFiles="@(_FilesToCopy)" DestinationFolder="$(OutputPath)..\..\..\..\..\.."/> | |
</Target> | |
</Project> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Microsoft.CodeAnalysis; | |
using Microsoft.CodeAnalysis.Text; | |
using System.Text; | |
using System.CodeDom.Compiler; | |
using System.IO; | |
namespace ExampleGenerators; | |
[Generator] | |
public class ExampleSourceGenerator : IIncrementalGenerator | |
{ | |
public void Initialize(IncrementalGeneratorInitializationContext context) | |
{ | |
var assemblyName = context.CompilationProvider.Select(static (compilation, _) => compilation.AssemblyName); | |
context.RegisterSourceOutput(assemblyName, (spc, currentAssemblyName) => | |
{ | |
if (currentAssemblyName == "Assembly-CSharp") | |
{ | |
spc.AddSource("ExampleSourceGenerator.g.cs", GenerateSource()); | |
} | |
}); | |
} | |
SourceText GenerateSource() | |
{ | |
using var sourceStream = new StringWriter(); | |
using var codeWriter = new IndentedTextWriter(sourceStream); | |
codeWriter.WriteLine("using System;"); | |
codeWriter.WriteLine("namespace ExampleSourceGenerated {"); | |
codeWriter.Indent++; | |
codeWriter.WriteLine("public static class ExampleSourceGenerated {"); | |
codeWriter.Indent++; | |
codeWriter.WriteLine("public static string GetTestText()"); | |
codeWriter.WriteLine("{"); | |
codeWriter.Indent++; | |
codeWriter.WriteLine("return \"This is from incremental generator - Generated at build time\";"); | |
codeWriter.Indent--; | |
codeWriter.WriteLine("}"); | |
codeWriter.Indent--; | |
codeWriter.WriteLine("}"); | |
codeWriter.Indent--; | |
codeWriter.WriteLine("}"); | |
return SourceText.From(sourceStream.ToString(), Encoding.UTF8); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Microsoft.CodeAnalysis; | |
using Microsoft.CodeAnalysis.CSharp.Syntax; | |
using System.Linq; | |
namespace ExampleGenerators; | |
[Generator] | |
public class UnityPerformanceAnalyzer : IIncrementalGenerator | |
{ | |
public void Initialize(IncrementalGeneratorInitializationContext context) | |
{ | |
var syntaxProvider = context.SyntaxProvider | |
.CreateSyntaxProvider( | |
predicate: static (node, _) => IsPotentialMonoBehaviour(node), | |
transform: static (context, _) => (ClassDeclarationSyntax)context.Node | |
) | |
.Where(static node => node != null); | |
context.RegisterSourceOutput(syntaxProvider, (ctx, classNode) => | |
{ | |
if (HasEmptyUpdateMethod(classNode)) | |
{ | |
var diagnostic = Diagnostic.Create( | |
EmptyUpdateRule, | |
classNode.Identifier.GetLocation(), | |
classNode.Identifier.Text | |
); | |
ctx.ReportDiagnostic(diagnostic); | |
} | |
if (HasExcessiveDebugLogs(classNode)) { | |
var diagnostic = Diagnostic.Create( | |
ExcessiveLogsRule, | |
classNode.Identifier.GetLocation(), | |
classNode.Identifier.Text | |
); | |
ctx.ReportDiagnostic(diagnostic); | |
} | |
}); | |
} | |
static bool IsPotentialMonoBehaviour(SyntaxNode node) => | |
node is ClassDeclarationSyntax classDecl && | |
classDecl.BaseList?.Types.Any(t => t.Type.ToString() == "MonoBehaviour") == true; | |
static bool HasEmptyUpdateMethod(ClassDeclarationSyntax classDecl) => | |
classDecl.Members.OfType<MethodDeclarationSyntax>() | |
.Any(m => m.Identifier.Text == "Update" && m.Body?.Statements.Count == 0); | |
static bool HasExcessiveDebugLogs(ClassDeclarationSyntax classDecl) => | |
classDecl.Members.OfType<MethodDeclarationSyntax>() | |
.SelectMany(m => m.Body?.Statements.OfType<ExpressionStatementSyntax>() ?? []) | |
.Count(expr => | |
expr.Expression is InvocationExpressionSyntax invocation && | |
invocation.Expression.ToString().Contains("Debug.Log")) >= 3; | |
static readonly DiagnosticDescriptor EmptyUpdateRule = new( | |
id: "UPA001", | |
title: "Empty Update() Detected", | |
messageFormat: "Class '{0}' contains an empty Update() method, which may harm performance", | |
category: "Performance", | |
DiagnosticSeverity.Warning, | |
isEnabledByDefault: true | |
); | |
static readonly DiagnosticDescriptor ExcessiveLogsRule = new( | |
id: "UPA002", | |
title: "Excessive Debug.Log() Calls", | |
messageFormat: "Class '{0}' has excessive Debug.Log() calls, which may impact performance", | |
category: "Performance", | |
DiagnosticSeverity.Warning, | |
isEnabledByDefault: true | |
); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment