Skip to content

Commit

Permalink
Merge branch 'master' into fix-long-waveform-performance
Browse files Browse the repository at this point in the history
  • Loading branch information
bdach committed Jun 17, 2023
2 parents d12f05e + 7015842 commit 1fe574a
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 8 deletions.
6 changes: 3 additions & 3 deletions osu.Framework.SourceGeneration/Analysers/DiagnosticRules.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ public class DiagnosticRules

public static readonly DiagnosticDescriptor MAKE_DI_CLASS_PARTIAL = new DiagnosticDescriptor(
"OFSG001",
"This class is a candidate for dependency injection and should be partial",
"This class is a candidate for dependency injection and should be partial",
"This type, or a nested type, is a candidate for dependency injection and should be partial",
"This type, or a nested type, is a candidate for dependency injection and should be partial",
"Performance",
DiagnosticSeverity.Warning,
true,
"Classes that are candidates for dependency injection should be made partial to benefit from compile-time optimisations.");
"Types that are candidates for dependency injection should be made partial to benefit from compile-time optimisations.");

#pragma warning restore RS2008
}
Expand Down
29 changes: 24 additions & 5 deletions osu.Framework.SourceGeneration/Analysers/DrawableAnalyser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,38 @@ public override void Initialize(AnalysisContext context)
}

/// <summary>
/// Analyses class definitions for implementations of IDrawable, ISourceGeneratedDependencyActivator, and Transformable.
/// Analyses class definitions for implementations of IDependencyInjectionCandidateInterface.
/// </summary>
private void analyseClass(SyntaxNodeAnalysisContext context)
{
var classSyntax = (ClassDeclarationSyntax)context.Node;

if (classSyntax.Modifiers.Any(m => m.IsKind(SyntaxKind.PartialKeyword)))
if (classSyntax.Ancestors().OfType<ClassDeclarationSyntax>().Any())
return;

INamedTypeSymbol? type = context.SemanticModel.GetDeclaredSymbol(classSyntax);
analyseRecursively(context, classSyntax);

if (type?.AllInterfaces.Any(SyntaxHelpers.IsIDependencyInjectionCandidateInterface) == true)
context.ReportDiagnostic(Diagnostic.Create(DiagnosticRules.MAKE_DI_CLASS_PARTIAL, context.Node.GetLocation(), context.Node));
static bool analyseRecursively(SyntaxNodeAnalysisContext context, ClassDeclarationSyntax node)
{
bool requiresPartial = false;

// Child nodes always have to be analysed to provide diagnostics.
foreach (var nested in node.DescendantNodes().OfType<ClassDeclarationSyntax>())
requiresPartial |= analyseRecursively(context, nested);

// - If at least one child requires partial, then this node also needs to be partial regardless of its own type (optimisation).
// - If no child requires partial, we need to check if this node is a DI candidate (e.g. If the node has no nested types).
if (!requiresPartial)
requiresPartial = context.SemanticModel.GetDeclaredSymbol(node)?.AllInterfaces.Any(SyntaxHelpers.IsIDependencyInjectionCandidateInterface) == true;

// Whether the node is already partial.
bool isPartial = node.Modifiers.Any(m => m.IsKind(SyntaxKind.PartialKeyword));

if (requiresPartial && !isPartial)
context.ReportDiagnostic(Diagnostic.Create(DiagnosticRules.MAKE_DI_CLASS_PARTIAL, node.GetLocation(), node));

return requiresPartial;
}
}
}
}

0 comments on commit 1fe574a

Please sign in to comment.