diff --git a/src/Analyzers/Activities/MatchingInputOutputTypeActivityAnalyzer.cs b/src/Analyzers/Activities/MatchingInputOutputTypeActivityAnalyzer.cs
new file mode 100644
index 00000000..d749df24
--- /dev/null
+++ b/src/Analyzers/Activities/MatchingInputOutputTypeActivityAnalyzer.cs
@@ -0,0 +1,352 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using System.Collections.Concurrent;
+using System.Collections.Immutable;
+using System.Diagnostics;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.Diagnostics;
+using Microsoft.CodeAnalysis.Operations;
+
+namespace Microsoft.DurableTask.Analyzers.Activities;
+
+///
+/// Analyzer that checks for mismatches between the input and output types of Activities invocations and their definitions.
+///
+[DiagnosticAnalyzer(LanguageNames.CSharp)]
+public class MatchingInputOutputTypeActivityAnalyzer : DiagnosticAnalyzer
+{
+ ///
+ /// The diagnostic ID for the diagnostic that reports when the input argument type of an Activity invocation does not match the input parameter type of the Activity definition.
+ ///
+ public const string InputArgumentTypeMismatchDiagnosticId = "DURABLE2001";
+
+ ///
+ /// The diagnostic ID for the diagnostic that reports when the output argument type of an Activity invocation does not match the return type of the Activity definition.
+ ///
+ public const string OutputArgumentTypeMismatchDiagnosticId = "DURABLE2002";
+
+ static readonly LocalizableString InputArgumentTypeMismatchTitle = new LocalizableResourceString(nameof(Resources.InputArgumentTypeMismatchAnalyzerTitle), Resources.ResourceManager, typeof(Resources));
+ static readonly LocalizableString InputArgumentTypeMismatchMessageFormat = new LocalizableResourceString(nameof(Resources.InputArgumentTypeMismatchAnalyzerMessageFormat), Resources.ResourceManager, typeof(Resources));
+
+ static readonly LocalizableString OutputArgumentTypeMismatchTitle = new LocalizableResourceString(nameof(Resources.OutputArgumentTypeMismatchAnalyzerTitle), Resources.ResourceManager, typeof(Resources));
+ static readonly LocalizableString OutputArgumentTypeMismatchMessageFormat = new LocalizableResourceString(nameof(Resources.OutputArgumentTypeMismatchAnalyzerMessageFormat), Resources.ResourceManager, typeof(Resources));
+
+ static readonly DiagnosticDescriptor InputArgumentTypeMismatchRule = new(
+ InputArgumentTypeMismatchDiagnosticId,
+ InputArgumentTypeMismatchTitle,
+ InputArgumentTypeMismatchMessageFormat,
+ AnalyzersCategories.Activity,
+ DiagnosticSeverity.Warning,
+ customTags: [WellKnownDiagnosticTags.CompilationEnd],
+ isEnabledByDefault: true);
+
+ static readonly DiagnosticDescriptor OutputArgumentTypeMismatchRule = new(
+ OutputArgumentTypeMismatchDiagnosticId,
+ OutputArgumentTypeMismatchTitle,
+ OutputArgumentTypeMismatchMessageFormat,
+ AnalyzersCategories.Activity,
+ DiagnosticSeverity.Warning,
+ customTags: [WellKnownDiagnosticTags.CompilationEnd],
+ isEnabledByDefault: true);
+
+ ///
+ public override ImmutableArray SupportedDiagnostics => [InputArgumentTypeMismatchRule, OutputArgumentTypeMismatchRule];
+
+ ///
+ public override void Initialize(AnalysisContext context)
+ {
+ context.EnableConcurrentExecution();
+ context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
+
+ context.RegisterCompilationStartAction(context =>
+ {
+ KnownTypeSymbols knownSymbols = new(context.Compilation);
+
+ if (knownSymbols.ActivityTriggerAttribute == null || knownSymbols.FunctionNameAttribute == null ||
+ knownSymbols.DurableTaskRegistry == null || knownSymbols.TaskActivityBase == null ||
+ knownSymbols.Task == null || knownSymbols.TaskT == null)
+ {
+ // symbols not available in this compilation, skip analysis
+ return;
+ }
+
+ IMethodSymbol taskActivityRunAsync = knownSymbols.TaskActivityBase.GetMembers("RunAsync").OfType().Single();
+ INamedTypeSymbol voidSymbol = context.Compilation.GetSpecialType(SpecialType.System_Void);
+
+ // Search for Activity invocations
+ ConcurrentBag invocations = [];
+ context.RegisterOperationAction(
+ ctx =>
+ {
+ ctx.CancellationToken.ThrowIfCancellationRequested();
+
+ if (ctx.Operation is not IInvocationOperation invocationOperation)
+ {
+ return;
+ }
+
+ IMethodSymbol targetMethod = invocationOperation.TargetMethod;
+ if (!targetMethod.IsEqualTo(knownSymbols.TaskOrchestrationContext, "CallActivityAsync"))
+ {
+ return;
+ }
+
+ Debug.Assert(invocationOperation.Arguments.Length is 2 or 3, "CallActivityAsync has 2 or 3 parameters");
+ Debug.Assert(invocationOperation.Arguments[0].Parameter?.Name == "name", "First parameter of CallActivityAsync is name");
+ IArgumentOperation activityNameArgumentOperation = invocationOperation.Arguments[0];
+
+ // extracts the constant value from the argument (e.g.: it can be a nameof, string literal or const field)
+ Optional