Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,43 @@ public static MauiApp CreateMauiApp()
await VerifyCameraToolkitAnalyzer(source, Diagnostic().WithSpan(12, 4, 12, 61).WithSeverity(DiagnosticSeverity.Error));
}

[Fact]
public async Task VerifyNoErrorsWhenUseMauiCommunityToolkitCameraWrapInPreprocessorDirectives()
{
const string source =
/* language=C#-test */
//lang=csharp
"""
namespace CommunityToolkit.Maui.Analyzers.UnitTests
{
using Microsoft.Maui.Controls.Hosting;
using Microsoft.Maui.Hosting;
using CommunityToolkit.Maui;

public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder.UseMauiApp<Microsoft.Maui.Controls.Application>()
#if ANDROID || IOS
.UseMauiCommunityToolkitCamera()
#endif
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
});

return builder.Build();
}
}
}
""";

await VerifyCameraToolkitAnalyzer(source);
}

static Task VerifyCameraToolkitAnalyzer(string source, params IReadOnlyList<DiagnosticResult> diagnosticResults)
{
return VerifyAnalyzerAsync(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,79 @@ public static MauiApp CreateMauiApp()
await VerifyMauiToolkitAnalyzer(source, Diagnostic().WithSpan(12, 4, 12, 61).WithSeverity(DiagnosticSeverity.Error));
}

[Fact]
public async Task VerifyNoErrorsWhenUseMauiCommunityToolkitWrapInPreprocessorDirectives()
{
const string source =
/* language=C#-test */
//lang=csharp
"""
namespace CommunityToolkit.Maui.Analyzers.UnitTests
{
using Microsoft.Maui.Controls.Hosting;
using Microsoft.Maui.Hosting;
using CommunityToolkit.Maui;

public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder.UseMauiApp<Microsoft.Maui.Controls.Application>()
#if ANDROID || IOS
.UseMauiCommunityToolkit()
#endif
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
});

return builder.Build();
}
}
}
""";


await VerifyMauiToolkitAnalyzer(source);
}

[Fact]
public async Task VerifyErrorsWhenUseMauiCommunityToolkitIsCommentedOut()
{
const string source =
/* language=C#-test */
//lang=csharp
"""
namespace CommunityToolkit.Maui.Analyzers.UnitTests
{
using Microsoft.Maui.Controls.Hosting;
using Microsoft.Maui.Hosting;
using CommunityToolkit.Maui;

public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder.UseMauiApp<Microsoft.Maui.Controls.Application>()
//.UseMauiCommunityToolkit()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
});

return builder.Build();
}
}
}
""";

await VerifyMauiToolkitAnalyzer(source, Diagnostic().WithSpan(12, 4, 12, 61).WithSeverity(DiagnosticSeverity.Error));
}

static Task VerifyMauiToolkitAnalyzer(string source, params IReadOnlyList<DiagnosticResult> expected)
{
return VerifyAnalyzerAsync(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using CommunityToolkit.Maui.MediaElement.Analyzers;
using CommunityToolkit.Maui.Core;
using CommunityToolkit.Maui.MediaElement.Analyzers;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Testing;
using Xunit;
Expand Down Expand Up @@ -118,12 +119,49 @@ public static MauiApp CreateMauiApp()
await VerifyMediaElementToolkitAnalyzer(source, Diagnostic().WithSpan(12, 4, 12, 61).WithSeverity(DiagnosticSeverity.Error));
}

[Fact]
public async Task VerifyNoErrorsWhenUseMauiCommunityToolkitMediaElementWrapInPreprocessorDirectives()
{
const string source =
/* language=C#-test */
//lang=csharp
"""
namespace CommunityToolkit.Maui.Analyzers.UnitTests
{
using Microsoft.Maui.Controls.Hosting;
using Microsoft.Maui.Hosting;
using CommunityToolkit.Maui;

public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder.UseMauiApp<Microsoft.Maui.Controls.Application>()
#if ANDROID || IOS
.UseMauiCommunityToolkitMediaElement()
#endif
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
});

return builder.Build();
}
}
}
""";

await VerifyMediaElementToolkitAnalyzer(source);
}

static Task VerifyMediaElementToolkitAnalyzer(string source, params IReadOnlyList<DiagnosticResult> diagnosticResults)
{
return VerifyAnalyzerAsync(
source,
[
typeof(Views.MediaElement) // CommunityToolkit.Maui.MediaElement
typeof(MediaElementOptions) // CommunityToolkit.Maui.MediaElement
],
diagnosticResults);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,13 @@ namespace CommunityToolkit.Maui.Analyzers;
public class UseCommunityToolkitInitializationAnalyzer : DiagnosticAnalyzer
{
public const string DiagnosticId = "MCT001";

const string category = "Initialization";
const string useMauiAppMethodName = "UseMauiApp";
const string useMauiCommunityToolkitMethodName = "UseMauiCommunityToolkit";

static readonly LocalizableString title = new LocalizableResourceString(nameof(Resources.InitializationErrorTitle), Resources.ResourceManager, typeof(Resources));
static readonly LocalizableString messageFormat = new LocalizableResourceString(nameof(Resources.InitalizationMessageFormat), Resources.ResourceManager, typeof(Resources));
static readonly LocalizableString description = new LocalizableResourceString(nameof(Resources.InitializationErrorMessage), Resources.ResourceManager, typeof(Resources));

static readonly DiagnosticDescriptor rule = new(DiagnosticId, title, messageFormat, category, DiagnosticSeverity.Error, isEnabledByDefault: true, description: description);

public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = [rule];
Expand All @@ -32,21 +30,42 @@ public override void Initialize(AnalysisContext context)

static void AnalyzeNode(SyntaxNodeAnalysisContext context)
{
if (context.Node is InvocationExpressionSyntax { Expression: MemberAccessExpressionSyntax { Name.Identifier.ValueText: useMauiAppMethodName } } invocationExpression)
if (context.Node is InvocationExpressionSyntax invocationExpression
&& invocationExpression.Expression is MemberAccessExpressionSyntax memberAccessExpression
&& memberAccessExpression.Name.Identifier.ValueText == useMauiAppMethodName)
{
var root = invocationExpression.SyntaxTree.GetRoot();
var methodDeclaration = root.FindNode(invocationExpression.FullSpan)
var methodDeclaration = invocationExpression
.Ancestors()
.OfType<MethodDeclarationSyntax>()
.FirstOrDefault();

if (methodDeclaration is not null
&& !methodDeclaration.DescendantNodes().OfType<InvocationExpressionSyntax>().Any(static n =>
n.Expression is MemberAccessExpressionSyntax { Name.Identifier.ValueText: useMauiCommunityToolkitMethodName }))
if (methodDeclaration is not null && !HasUseMauiCommunityToolkitCall(methodDeclaration))
{
var diagnostic = Diagnostic.Create(rule, invocationExpression.GetLocation());
context.ReportDiagnostic(diagnostic);
}
}
}

static bool HasUseMauiCommunityToolkitCall(MethodDeclarationSyntax methodDeclaration)
{
// Check syntax nodes first (handles active code)
var hasInSyntaxTree = methodDeclaration
.DescendantNodes()
.OfType<InvocationExpressionSyntax>()
.Any(static invocation => invocation.Expression is MemberAccessExpressionSyntax memberAccess
&& memberAccess.Name.Identifier.ValueText == useMauiCommunityToolkitMethodName);

if (hasInSyntaxTree)
{
return true;
}

// Check trivia (comments, preprocessor directives, disabled code)
return methodDeclaration
.DescendantTrivia()
.Any(static trivia =>
trivia.IsKind(SyntaxKind.DisabledTextTrivia) &&
trivia.ToString().Contains(useMauiCommunityToolkitMethodName, StringComparison.Ordinal));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,20 +36,37 @@ static void AnalyzeNode(SyntaxNodeAnalysisContext context)
&& invocationExpression.Expression is MemberAccessExpressionSyntax memberAccessExpression
&& memberAccessExpression.Name.Identifier.ValueText == useMauiAppMethodName)
{
var root = invocationExpression.SyntaxTree.GetRoot();
var methodDeclaration = root.FindNode(invocationExpression.FullSpan)
var methodDeclaration = invocationExpression
.Ancestors()
.OfType<MethodDeclarationSyntax>()
.FirstOrDefault();

if (methodDeclaration is not null
&& !methodDeclaration.DescendantNodes().OfType<InvocationExpressionSyntax>().Any(static n =>
n.Expression is MemberAccessExpressionSyntax m &&
m.Name.Identifier.ValueText == useMauiCommunityToolkitCameraMethodName))
if (methodDeclaration is not null && !HasUseMauiCommunityToolkitCameraCall(methodDeclaration))
{
var diagnostic = Diagnostic.Create(rule, invocationExpression.GetLocation());
context.ReportDiagnostic(diagnostic);
}
}
}

static bool HasUseMauiCommunityToolkitCameraCall(MethodDeclarationSyntax methodDeclaration)
{
// Check syntax nodes first (handles active code)
var hasInSyntaxTree = methodDeclaration
.DescendantNodes()
.OfType<InvocationExpressionSyntax>()
.Any(static invocation => invocation.Expression is MemberAccessExpressionSyntax memberAccess
&& memberAccess.Name.Identifier.ValueText == useMauiCommunityToolkitCameraMethodName);

if (hasInSyntaxTree)
{
return true;
}

// Check trivia (comments, preprocessor directives, disabled code)
return methodDeclaration
.DescendantTrivia()
.Any(static trivia => trivia.IsKind(SyntaxKind.DisabledTextTrivia)
&& trivia.ToString().Contains(useMauiCommunityToolkitCameraMethodName, StringComparison.Ordinal));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,20 +36,37 @@ static void AnalyzeNode(SyntaxNodeAnalysisContext context)
&& invocationExpression.Expression is MemberAccessExpressionSyntax memberAccessExpression
&& memberAccessExpression.Name.Identifier.ValueText == useMauiAppMethodName)
{
var root = invocationExpression.SyntaxTree.GetRoot();
var methodDeclaration = root.FindNode(invocationExpression.FullSpan)
var methodDeclaration = invocationExpression
.Ancestors()
.OfType<MethodDeclarationSyntax>()
.FirstOrDefault();

if (methodDeclaration is not null
&& !methodDeclaration.DescendantNodes().OfType<InvocationExpressionSyntax>().Any(static n =>
n.Expression is MemberAccessExpressionSyntax m &&
m.Name.Identifier.ValueText == useMauiCommunityToolkitMediaElementMethodName))
if (methodDeclaration is not null && !HasUseMauiCommunityToolkitMediaElementCall(methodDeclaration))
{
var diagnostic = Diagnostic.Create(rule, invocationExpression.GetLocation());
context.ReportDiagnostic(diagnostic);
}
}
}

static bool HasUseMauiCommunityToolkitMediaElementCall(MethodDeclarationSyntax methodDeclaration)
{
// Check syntax nodes first (handles active code)
var hasInSyntaxTree = methodDeclaration
.DescendantNodes()
.OfType<InvocationExpressionSyntax>()
.Any(static invocation => invocation.Expression is MemberAccessExpressionSyntax memberAccess
&& memberAccess.Name.Identifier.ValueText == useMauiCommunityToolkitMediaElementMethodName);

if (hasInSyntaxTree)
{
return true;
}

// Check trivia (comments, preprocessor directives, disabled code)
return methodDeclaration
.DescendantTrivia()
.Any(static trivia => trivia.IsKind(SyntaxKind.DisabledTextTrivia)
&& trivia.ToString().Contains(useMauiCommunityToolkitMediaElementMethodName, StringComparison.Ordinal));
}
}
Loading