diff --git a/AppLibDotnet.sln b/AppLibDotnet.sln
index 826edaf71..e5d00f268 100644
--- a/AppLibDotnet.sln
+++ b/AppLibDotnet.sln
@@ -18,6 +18,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{7AD5FADE-607
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Altinn.App.Core", "src\Altinn.App.Core\Altinn.App.Core.csproj", "{1745B251-BD5C-43B7-BA7D-9C4BFAB37535}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Altinn.App.Analyzers", "src\Altinn.App.Analyzers\Altinn.App.Analyzers.csproj", "{9F956488-F123-48A2-B21A-2C2918E8B416}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Altinn.App.Analyzers.Tests", "test\Altinn.App.Analyzers.Tests\Altinn.App.Analyzers.Tests.csproj", "{34E40EB8-DFEA-432C-9F53-371932E21D8A}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -40,6 +44,14 @@ Global
{1745B251-BD5C-43B7-BA7D-9C4BFAB37535}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1745B251-BD5C-43B7-BA7D-9C4BFAB37535}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1745B251-BD5C-43B7-BA7D-9C4BFAB37535}.Release|Any CPU.Build.0 = Release|Any CPU
+ {9F956488-F123-48A2-B21A-2C2918E8B416}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {9F956488-F123-48A2-B21A-2C2918E8B416}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {9F956488-F123-48A2-B21A-2C2918E8B416}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {9F956488-F123-48A2-B21A-2C2918E8B416}.Release|Any CPU.Build.0 = Release|Any CPU
+ {34E40EB8-DFEA-432C-9F53-371932E21D8A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {34E40EB8-DFEA-432C-9F53-371932E21D8A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {34E40EB8-DFEA-432C-9F53-371932E21D8A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {34E40EB8-DFEA-432C-9F53-371932E21D8A}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -49,6 +61,8 @@ Global
{17D7DCE9-7797-4BC1-B448-D0529FD6FB3D} = {6C8EB054-1747-4BAC-A637-754F304BCAFA}
{2FD56505-1DB2-4AE1-8911-E076E535EAC6} = {6C8EB054-1747-4BAC-A637-754F304BCAFA}
{1745B251-BD5C-43B7-BA7D-9C4BFAB37535} = {7AD5FADE-607F-4D5F-8511-6647D0C1AA1C}
+ {9F956488-F123-48A2-B21A-2C2918E8B416} = {7AD5FADE-607F-4D5F-8511-6647D0C1AA1C}
+ {34E40EB8-DFEA-432C-9F53-371932E21D8A} = {6C8EB054-1747-4BAC-A637-754F304BCAFA}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {4584C6E1-D5B4-40B1-A8C4-CF4620EB0896}
diff --git a/src/Altinn.App.Analyzers/Altinn.App.Analyzers.csproj b/src/Altinn.App.Analyzers/Altinn.App.Analyzers.csproj
new file mode 100644
index 000000000..b607cda2a
--- /dev/null
+++ b/src/Altinn.App.Analyzers/Altinn.App.Analyzers.csproj
@@ -0,0 +1,28 @@
+
+
+
+ netstandard2.0
+ enable
+ enable
+ true
+
+ false
+ true
+ true
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Altinn.App.Analyzers/AnalyzerReleases.Shipped.md b/src/Altinn.App.Analyzers/AnalyzerReleases.Shipped.md
new file mode 100644
index 000000000..60c1edfa5
--- /dev/null
+++ b/src/Altinn.App.Analyzers/AnalyzerReleases.Shipped.md
@@ -0,0 +1,3 @@
+; Shipped analyzer releases
+; https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md
+
diff --git a/src/Altinn.App.Analyzers/AnalyzerReleases.Unshipped.md b/src/Altinn.App.Analyzers/AnalyzerReleases.Unshipped.md
new file mode 100644
index 000000000..6ee9f5d64
--- /dev/null
+++ b/src/Altinn.App.Analyzers/AnalyzerReleases.Unshipped.md
@@ -0,0 +1,8 @@
+; Unshipped analyzer release
+; https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md
+
+### New Rules
+
+Rule ID | Category | Severity | Notes
+--------|----------|----------|-------
+ALTINN999 | General | Warning | TestAnalyzer
diff --git a/src/Altinn.App.Analyzers/TestAnalyzer.cs b/src/Altinn.App.Analyzers/TestAnalyzer.cs
new file mode 100644
index 000000000..db0dc041f
--- /dev/null
+++ b/src/Altinn.App.Analyzers/TestAnalyzer.cs
@@ -0,0 +1,84 @@
+namespace Altinn.App.Analyzers;
+
+internal static class Diagnostics
+{
+ internal static readonly DiagnosticDescriptor UnknownError = Warning(
+ "ALTINN999",
+ "Unknown analyzer error",
+ "Unknown error occurred during analysis: '{0}' {1}"
+ );
+
+ private const string DocsRoot = "https://docs.altinn.studio/app/development/analysis/";
+ private const string RulesRoot = DocsRoot + "rules/";
+
+ private static DiagnosticDescriptor Warning(string id, string title, string messageFormat) =>
+ Create(id, title, messageFormat, Category.General, DiagnosticSeverity.Warning);
+
+ private static DiagnosticDescriptor Create(
+ string id,
+ string title,
+ string messageFormat,
+ string category,
+ DiagnosticSeverity severity
+ ) => new(id, title, messageFormat, category, severity, true, helpLinkUri: RulesRoot + id);
+
+ private static class Category
+ {
+ public const string General = nameof(General);
+ }
+}
+
+///
+/// Analyzer
+///
+[DiagnosticAnalyzer(LanguageNames.CSharp)]
+public class TestAnalyzer : DiagnosticAnalyzer
+{
+ ///
+ /// Supported diagnostics
+ ///
+ public override ImmutableArray SupportedDiagnostics =>
+ ImmutableArray.Create(Diagnostics.UnknownError);
+
+ ///
+ /// Initializer
+ ///
+ ///
+ public override void Initialize(AnalysisContext context)
+ {
+ // var configFlags = GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics;
+ var configFlags = GeneratedCodeAnalysisFlags.None;
+ context.ConfigureGeneratedCodeAnalysis(configFlags);
+ context.EnableConcurrentExecution();
+
+ // context.RegisterCompilationStartAction(OnCompilationStart);
+ context.RegisterCompilationAction(OnCompilation);
+ context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.SimpleMemberAccessExpression);
+ }
+
+ // private void OnCompilationStart(CompilationStartAnalysisContext context)
+ // {
+ // }
+
+ private void OnCompilation(CompilationAnalysisContext context) { }
+
+ private void AnalyzeNode(SyntaxNodeAnalysisContext context)
+ {
+ try
+ {
+ if (context.Node is not MemberAccessExpressionSyntax memberAccess)
+ return;
+
+ if (memberAccess.Name.Identifier.Text != "OpenTelemetry")
+ return;
+
+ var symbol = context.SemanticModel.GetSymbolInfo(memberAccess.Expression, context.CancellationToken).Symbol;
+ }
+ catch (Exception ex)
+ {
+ context.ReportDiagnostic(
+ Diagnostic.Create(Diagnostics.UnknownError, context.Node.GetLocation(), ex.Message, ex.StackTrace)
+ );
+ }
+ }
+}
diff --git a/src/Altinn.App.Core/Altinn.App.Core.csproj b/src/Altinn.App.Core/Altinn.App.Core.csproj
index 6d1b313b3..c9765b2e3 100644
--- a/src/Altinn.App.Core/Altinn.App.Core.csproj
+++ b/src/Altinn.App.Core/Altinn.App.Core.csproj
@@ -24,6 +24,11 @@
+
+
+
+
+
diff --git a/test/Altinn.App.Analyzers.Tests/Altinn.App.Analyzers.Tests.csproj b/test/Altinn.App.Analyzers.Tests/Altinn.App.Analyzers.Tests.csproj
new file mode 100644
index 000000000..540ac461e
--- /dev/null
+++ b/test/Altinn.App.Analyzers.Tests/Altinn.App.Analyzers.Tests.csproj
@@ -0,0 +1,28 @@
+
+
+
+ net8.0
+ enable
+ enable
+
+ false
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/Altinn.App.Analyzers.Tests/UnitTest1.cs b/test/Altinn.App.Analyzers.Tests/UnitTest1.cs
new file mode 100644
index 000000000..c9d7f3937
--- /dev/null
+++ b/test/Altinn.App.Analyzers.Tests/UnitTest1.cs
@@ -0,0 +1,96 @@
+using System.Collections.Immutable;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Testing;
+using Microsoft.CodeAnalysis.Diagnostics;
+using Microsoft.CodeAnalysis.Testing;
+using Microsoft.CodeAnalysis.Testing.Verifiers;
+using Verifier = Altinn.App.Analyzers.Tests.CSharpAnalyzerVerifier;
+
+namespace Altinn.App.Analyzers.Tests;
+
+public static partial class CSharpAnalyzerVerifier
+ where TAnalyzer : DiagnosticAnalyzer, new()
+{
+ ///
+ public static DiagnosticResult Diagnostic() => CSharpAnalyzerVerifier.Diagnostic();
+
+ ///
+ public static DiagnosticResult Diagnostic(string diagnosticId) =>
+ CSharpAnalyzerVerifier.Diagnostic(diagnosticId);
+
+ ///
+ public static DiagnosticResult Diagnostic(DiagnosticDescriptor descriptor) =>
+ CSharpAnalyzerVerifier.Diagnostic(descriptor);
+
+ ///
+ public static async Task VerifyAnalyzerAsync(string source, params DiagnosticResult[] expected)
+ {
+ var test = new Test { TestCode = source, };
+
+ test.ExpectedDiagnostics.AddRange(expected);
+ await test.RunAsync(CancellationToken.None);
+ }
+
+ public class Test : CSharpAnalyzerTest
+ {
+ public Test()
+ {
+ SolutionTransforms.Add(
+ (solution, projectId) =>
+ {
+ var compilationOptions = solution.GetProject(projectId).CompilationOptions;
+ compilationOptions = compilationOptions.WithSpecificDiagnosticOptions(
+ compilationOptions.SpecificDiagnosticOptions.SetItems(NullableWarnings)
+ );
+ solution = solution.WithProjectCompilationOptions(projectId, compilationOptions);
+
+ return solution;
+ }
+ );
+ }
+ }
+
+ internal static ImmutableDictionary NullableWarnings { get; } =
+ GetNullableWarningsFromCompiler();
+
+ private static ImmutableDictionary GetNullableWarningsFromCompiler()
+ {
+ string[] args = { "/warnaserror:nullable" };
+ var commandLineArguments = CSharpCommandLineParser.Default.Parse(
+ args,
+ baseDirectory: Environment.CurrentDirectory,
+ sdkDirectory: Environment.CurrentDirectory
+ );
+ var nullableWarnings = commandLineArguments.CompilationOptions.SpecificDiagnosticOptions;
+
+ // Workaround for https://github.com/dotnet/roslyn/issues/41610
+ nullableWarnings = nullableWarnings
+ .SetItem("CS8632", ReportDiagnostic.Error)
+ .SetItem("CS8669", ReportDiagnostic.Error);
+
+ return nullableWarnings;
+ }
+}
+
+public class UnitTest1
+{
+ [Fact]
+ public async Task Test1()
+ {
+ var code = """
+ using System;
+
+ class Program
+ {
+ static void Main()
+ {
+ int i = 0;
+ Console.WriteLine(i++);
+ }
+ }
+ """;
+
+ await Verifier.VerifyAnalyzerAsync(code);
+ }
+}