diff --git a/.editorconfig b/.editorconfig
index 62a533e468..d5b2badfd5 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -208,6 +208,12 @@ dotnet_diagnostic.AVA2001.severity = error
# Xaml files
[*.{xaml,axaml}]
indent_size = 2
+# DuplicateSetterError
+avalonia_xaml_diagnostic.AVLN2203.severity = error
+# StyleInMergedDictionaries
+avalonia_xaml_diagnostic.AVLN2204.severity = error
+# Obsolete
+avalonia_xaml_diagnostic.AVLN5001.severity = error
# Xml project files
[*.{csproj,vcxproj,vcxproj.filters,proj,nativeproj,locproj}]
diff --git a/build/BuildTargets.targets b/build/BuildTargets.targets
index 481dbf06b2..13b002d523 100644
--- a/build/BuildTargets.targets
+++ b/build/BuildTargets.targets
@@ -4,6 +4,7 @@
true
true
true
+ true
diff --git a/packages/Avalonia/AvaloniaBuildTasks.targets b/packages/Avalonia/AvaloniaBuildTasks.targets
index ee9d8c6dfe..2232ea35af 100644
--- a/packages/Avalonia/AvaloniaBuildTasks.targets
+++ b/packages/Avalonia/AvaloniaBuildTasks.targets
@@ -130,6 +130,7 @@
$(IntermediateOutputPath)/Avalonia/original.dll
false
false
+ false
+ DefaultCompileBindings="$(AvaloniaUseCompiledBindingsByDefault)"
+ VerboseExceptions="$(AvaloniaXamlVerboseExceptions)"
+ AnalyzerConfigFiles="@(EditorConfigFiles)">
-
-
-
+ DisplayName="Debug XAML Compiler"
+ Description="Allow debug XAML compilation"
+ Category="Debug" />
+
+
+
+
diff --git a/src/Avalonia.Build.Tasks/BuildEngineErrorCode.cs b/src/Avalonia.Build.Tasks/BuildEngineErrorCode.cs
deleted file mode 100644
index a31c9a7516..0000000000
--- a/src/Avalonia.Build.Tasks/BuildEngineErrorCode.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-namespace Avalonia.Build.Tasks
-{
- public enum BuildEngineErrorCode
- {
- InvalidXAML = 1,
- DuplicateXClass = 2,
- LegacyResmScheme = 3,
- TransformError = 4,
- EmitError = 4,
- Loader = 5,
-
- Unknown = 9999
- }
-}
diff --git a/src/Avalonia.Build.Tasks/CompileAvaloniaXamlTask.cs b/src/Avalonia.Build.Tasks/CompileAvaloniaXamlTask.cs
index 3bd1ce5ce7..5287827652 100644
--- a/src/Avalonia.Build.Tasks/CompileAvaloniaXamlTask.cs
+++ b/src/Avalonia.Build.Tasks/CompileAvaloniaXamlTask.cs
@@ -56,7 +56,9 @@ namespace Avalonia.Build.Tasks
refInput, RefOutputPath,
File.ReadAllLines(ReferencesFilePath).Where(l => !string.IsNullOrWhiteSpace(l)).ToArray(),
ProjectDirectory, VerifyIl, DefaultCompileBindings, outputImportance,
- (SignAssembly && !DelaySign) ? AssemblyOriginatorKeyFile : null, SkipXamlCompilation, DebuggerLaunch);
+ new XamlCompilerDiagnosticsFilter(AnalyzerConfigFiles),
+ (SignAssembly && !DelaySign) ? AssemblyOriginatorKeyFile : null,
+ SkipXamlCompilation, DebuggerLaunch, VerboseExceptions);
if (!res.Success)
{
WrittenFilePaths = writtenFilePaths.ToArray();
@@ -121,6 +123,10 @@ namespace Avalonia.Build.Tasks
public bool DebuggerLaunch { get; set; }
+ public bool VerboseExceptions { get; set; }
+
+ public ITaskItem[] AnalyzerConfigFiles { get; set; }
+
[Output]
public string[] WrittenFilePaths { get; private set; } = Array.Empty();
}
diff --git a/src/Avalonia.Build.Tasks/Extensions.cs b/src/Avalonia.Build.Tasks/Extensions.cs
index b0b7e0cc3d..557e1e1fbe 100644
--- a/src/Avalonia.Build.Tasks/Extensions.cs
+++ b/src/Avalonia.Build.Tasks/Extensions.cs
@@ -1,41 +1,110 @@
using System;
+using System.Text;
+using System.Xml;
using Microsoft.Build.Framework;
+using XamlX;
-namespace Avalonia.Build.Tasks
+namespace Avalonia.Build.Tasks;
+
+internal static class Extensions
{
- static class Extensions
+ public static void LogError(this IBuildEngine engine, string code, string file, Exception ex,
+ int? lineNumber = null, int? linePosition = null)
{
- static string FormatErrorCode(BuildEngineErrorCode code) => FormattableString.Invariant($"AVLN:{(int)code:0000}");
-
- public static void LogError(this IBuildEngine engine, BuildEngineErrorCode code, string file, Exception ex,
- int lineNumber = 0, int linePosition = 0)
+ if (lineNumber is null && linePosition is null
+ && ex is XmlException xe)
{
+ lineNumber = xe.LineNumber;
+ linePosition = xe.LinePosition;
+ }
+
#if DEBUG
- LogError(engine, code, file, ex.ToString(), lineNumber, linePosition);
+ LogError(engine, code, file, ex.ToString(), lineNumber, linePosition);
#else
- LogError(engine, code, file, ex.Message, lineNumber, linePosition);
+ LogError(engine, code, file, ex.Message, lineNumber, linePosition);
#endif
+ }
+
+ public static void LogDiagnostic(this IBuildEngine engine, XamlDiagnostic diagnostic)
+ {
+ var message = diagnostic.Title;
+
+ if (diagnostic.Severity == XamlDiagnosticSeverity.None)
+ {
+ // Skip.
}
-
- public static void LogError(this IBuildEngine engine, BuildEngineErrorCode code, string file, string message,
- int lineNumber = 0, int linePosition = 0)
+ else if (diagnostic.Severity == XamlDiagnosticSeverity.Warning)
{
- engine.LogErrorEvent(new BuildErrorEventArgs("Avalonia", FormatErrorCode(code), file ?? "",
- lineNumber, linePosition, lineNumber, linePosition, message,
+ engine.LogWarningEvent(new BuildWarningEventArgs("Avalonia", diagnostic.Code, diagnostic.Document ?? "",
+ diagnostic.LineNumber ?? 0, diagnostic.LinePosition ?? 0,
+ diagnostic.LineNumber ?? 0, diagnostic.LinePosition ?? 0,
+ message,
"", "Avalonia"));
}
-
- public static void LogWarning(this IBuildEngine engine, BuildEngineErrorCode code, string file, string message,
- int lineNumber = 0, int linePosition = 0)
+ else
{
- engine.LogWarningEvent(new BuildWarningEventArgs("Avalonia", FormatErrorCode(code), file ?? "",
- lineNumber, linePosition, lineNumber, linePosition, message,
+ engine.LogErrorEvent(new BuildErrorEventArgs("Avalonia", diagnostic.Code, diagnostic.Document ?? "",
+ diagnostic.LineNumber ?? 0, diagnostic.LinePosition ?? 0,
+ diagnostic.LineNumber ?? 0, diagnostic.LinePosition ?? 0,
+ message,
"", "Avalonia"));
}
+ }
+
+ public static void LogError(this IBuildEngine engine, string code, string file, string message,
+ int? lineNumber = null, int? linePosition = null)
+ {
+ engine.LogErrorEvent(new BuildErrorEventArgs("Avalonia", code, file ?? "",
+ lineNumber ?? 0, linePosition ?? 0, lineNumber ?? 0, linePosition ?? 0,
+ message, "", "Avalonia"));
+ }
- public static void LogMessage(this IBuildEngine engine, string message, MessageImportance imp)
+ public static void LogWarning(this IBuildEngine engine, string code, string file, string message,
+ int lineNumber = 0, int linePosition = 0)
+ {
+ engine.LogWarningEvent(new BuildWarningEventArgs("Avalonia", code, file ?? "",
+ lineNumber, linePosition, lineNumber, linePosition, message,
+ "", "Avalonia"));
+ }
+
+ public static void LogMessage(this IBuildEngine engine, string message, MessageImportance imp)
+ {
+ engine.LogMessageEvent(new BuildMessageEventArgs(message, "", "Avalonia", imp));
+ }
+
+ public static string FormatException(this Exception exception, bool verbose)
+ {
+ if (!verbose)
{
- engine.LogMessageEvent(new BuildMessageEventArgs(message, "", "Avalonia", imp));
+ return exception.Message;
+ }
+
+ var builder = new StringBuilder();
+ Process(exception);
+ return builder.ToString();
+
+ // Inspired by https://github.com/dotnet/msbuild/blob/e6409007d3a09255431eb28af01835ce1cd316b5/src/Shared/TaskLoggingHelper.cs#L909
+ void Process(Exception exception)
+ {
+ if (exception is AggregateException aggregateException)
+ {
+ foreach (Exception innerException in aggregateException.Flatten().InnerExceptions)
+ {
+ Process(innerException);
+ }
+
+ return;
+ }
+
+ do
+ {
+ builder.Append(exception.GetType().Name);
+ builder.Append(": ");
+ builder.AppendLine(exception.Message);
+ builder.AppendLine(exception.StackTrace);
+
+ exception = exception.InnerException;
+ } while (exception != null);
}
}
}
diff --git a/src/Avalonia.Build.Tasks/GenerateAvaloniaResourcesTask.cs b/src/Avalonia.Build.Tasks/GenerateAvaloniaResourcesTask.cs
index 86c04132a0..6aa815a28b 100644
--- a/src/Avalonia.Build.Tasks/GenerateAvaloniaResourcesTask.cs
+++ b/src/Avalonia.Build.Tasks/GenerateAvaloniaResourcesTask.cs
@@ -5,6 +5,7 @@ using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using Avalonia.Markup.Xaml.PortableXaml;
+using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions;
using Avalonia.Utilities;
using Microsoft.Build.Framework;
using SPath = System.IO.Path;
@@ -100,7 +101,7 @@ namespace Avalonia.Build.Tasks
}
catch (Exception e)
{
- BuildEngine.LogError(BuildEngineErrorCode.InvalidXAML, s.SystemPath, "File doesn't contain valid XAML: " + e);
+ BuildEngine.LogError(AvaloniaXamlDiagnosticCodes.InvalidXAML, s.SystemPath, "File doesn't contain valid XAML: " + e);
return false;
}
@@ -109,7 +110,7 @@ namespace Avalonia.Build.Tasks
if (typeToXamlIndex.ContainsKey(info.XClass))
{
- BuildEngine.LogError(BuildEngineErrorCode.DuplicateXClass, s.SystemPath,
+ BuildEngine.LogError(AvaloniaXamlDiagnosticCodes.DuplicateXClass, s.SystemPath,
$"Duplicate x:Class directive, {info.XClass} is already used in {typeToXamlIndex[info.XClass]}");
return false;
}
diff --git a/src/Avalonia.Build.Tasks/XamlCompilerDiagnosticsFilter.cs b/src/Avalonia.Build.Tasks/XamlCompilerDiagnosticsFilter.cs
new file mode 100644
index 0000000000..4314c7b724
--- /dev/null
+++ b/src/Avalonia.Build.Tasks/XamlCompilerDiagnosticsFilter.cs
@@ -0,0 +1,70 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text.RegularExpressions;
+using Microsoft.Build.Framework;
+using XamlX;
+
+namespace Avalonia.Build.Tasks;
+
+// With MSBuild, we don't need to read for TreatWarningsAsErrors/WarningsAsErrors/WarningsNotAsErrors/NoWarn properties.
+// Just by reporting them with LogWarning MSBuild will do the rest for us.
+// But we still need to read EditorConfig manually.
+public class XamlCompilerDiagnosticsFilter
+{
+ private static readonly Regex s_editorConfigRegex =
+ new("""avalonia_xaml_diagnostic\.([\w\d]+)\.severity\s*=\s*(\w*)""");
+
+ private readonly Lazy> _lazyEditorConfig;
+
+ public XamlCompilerDiagnosticsFilter(
+ ITaskItem[]? analyzerConfigFiles)
+ {
+ _lazyEditorConfig = new Lazy>(() => ParseEditorConfigFiles(analyzerConfigFiles));
+ }
+
+ internal XamlDiagnosticSeverity Handle(XamlDiagnostic diagnostic)
+ {
+ return Handle(diagnostic.Severity, diagnostic.Code);
+ }
+
+ internal XamlDiagnosticSeverity Handle(XamlDiagnosticSeverity currentSeverity, string diagnosticCode)
+ {
+ if (_lazyEditorConfig.Value.TryGetValue(diagnosticCode, out var severity))
+ {
+ return severity.ToLowerInvariant() switch
+ {
+ "default" => currentSeverity,
+ "error" => XamlDiagnosticSeverity.Error,
+ "warning" => XamlDiagnosticSeverity.Warning,
+ _ => XamlDiagnosticSeverity.None // "suggestion", "silent", "none"
+ };
+ }
+
+ return currentSeverity;
+ }
+
+ private Dictionary ParseEditorConfigFiles(ITaskItem[]? analyzerConfigFiles)
+ {
+ // Very naive EditorConfig parser, supporting minimal properties set via regex:
+ var severities = new Dictionary();
+ if (analyzerConfigFiles is not null)
+ {
+ foreach (var fileItem in analyzerConfigFiles)
+ {
+ if (File.Exists(fileItem.ItemSpec))
+ {
+ var fileContent = File.ReadAllText(fileItem.ItemSpec);
+ var matches = s_editorConfigRegex.Matches(fileContent);
+ foreach (Match match in matches)
+ {
+ severities[match.Groups[1].Value] = match.Groups[2].Value;
+ }
+ }
+ }
+ }
+
+ return severities;
+ }
+}
diff --git a/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs b/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs
index 3f68092b56..4a5b8b7ab2 100644
--- a/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs
+++ b/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@@ -44,21 +45,13 @@ namespace Avalonia.Build.Tasks
}
}
- public static CompileResult Compile(IBuildEngine engine,
- string input, string output,
- string refInput, string refOutput,
- string[] references, string projectDirectory,
- bool verifyIl, bool defaultCompileBindings, MessageImportance logImportance, string strongNameKey,
- bool skipXamlCompilation)
- {
- return Compile(engine, input, output, refInput, refOutput, references, projectDirectory, verifyIl, defaultCompileBindings, logImportance, strongNameKey, skipXamlCompilation, debuggerLaunch:false);
- }
-
internal static CompileResult Compile(IBuildEngine engine,
string input, string output,
string refInput, string refOutput,
string[] references, string projectDirectory,
- bool verifyIl, bool defaultCompileBindings, MessageImportance logImportance, string strongNameKey, bool skipXamlCompilation, bool debuggerLaunch)
+ bool verifyIl, bool defaultCompileBindings, MessageImportance logImportance,
+ XamlCompilerDiagnosticsFilter diagnosticsFilter, string strongNameKey,
+ bool skipXamlCompilation, bool debuggerLaunch, bool verboseExceptions)
{
try
{
@@ -70,7 +63,10 @@ namespace Avalonia.Build.Tasks
var refAsm = refTypeSystem?.TargetAssemblyDefinition;
if (!skipXamlCompilation)
{
- var compileRes = CompileCore(engine, typeSystem, projectDirectory, verifyIl, defaultCompileBindings, logImportance, debuggerLaunch);
+ var compileRes = CompileCore(
+ engine, typeSystem, projectDirectory, verifyIl,
+ defaultCompileBindings, logImportance, diagnosticsFilter,
+ debuggerLaunch, verboseExceptions);
if (compileRes == null)
return new CompileResult(true);
if (compileRes == false)
@@ -99,7 +95,7 @@ namespace Avalonia.Build.Tasks
}
catch (Exception ex)
{
- engine.LogError(BuildEngineErrorCode.Unknown, "", ex);
+ engine.LogError(AvaloniaXamlDiagnosticCodes.Unknown, "", ex);
return new CompileResult(false);
}
}
@@ -107,8 +103,10 @@ namespace Avalonia.Build.Tasks
static bool? CompileCore(IBuildEngine engine, CecilTypeSystem typeSystem,
string projectDirectory, bool verifyIl,
bool defaultCompileBindings,
- MessageImportance logImportance
- , bool debuggerLaunch = false)
+ MessageImportance logImportance,
+ XamlCompilerDiagnosticsFilter diagnosticsFilter,
+ bool debuggerLaunch,
+ bool verboseExceptions)
{
if (debuggerLaunch)
{
@@ -170,6 +168,20 @@ namespace Avalonia.Build.Tasks
asm.MainModule.Types.Add(trampolineBuilder);
var (xamlLanguage , emitConfig) = AvaloniaXamlIlLanguage.Configure(typeSystem);
+ var diagnostics = new List();
+ var diagnosticsHandler = new XamlDiagnosticsHandler()
+ {
+ HandleDiagnostic = diagnostic =>
+ {
+ var newSeverity = diagnosticsFilter.Handle(diagnostic);
+ diagnostic = diagnostic with { Severity = newSeverity };
+ diagnostics.Add(diagnostic);
+ return newSeverity;
+ },
+ CodeMappings = AvaloniaXamlDiagnosticCodes.XamlXDiagnosticCodeToAvalonia,
+ ExceptionFormatter = ex => ex.FormatException(verboseExceptions)
+ };
+
var compilerConfig = new AvaloniaXamlIlCompilerConfiguration(typeSystem,
typeSystem.TargetAssembly,
xamlLanguage,
@@ -178,7 +190,8 @@ namespace Avalonia.Build.Tasks
new XamlIlClrPropertyInfoEmitter(typeSystem.CreateTypeBuilder(clrPropertiesDef)),
new XamlIlPropertyInfoAccessorFactoryEmitter(typeSystem.CreateTypeBuilder(indexerAccessorClosure)),
new XamlIlTrampolineBuilder(typeSystem.CreateTypeBuilder(trampolineBuilder)),
- new DeterministicIdGenerator());
+ new DeterministicIdGenerator(),
+ diagnosticsHandler);
var contextDef = new TypeDefinition(CompiledAvaloniaXamlNamespace, "XamlIlContext",
@@ -259,6 +272,7 @@ namespace Avalonia.Build.Tasks
{
var typeDef = new TypeDefinition(CompiledAvaloniaXamlNamespace, "!"+ group.Name,
TypeAttributes.Class | TypeAttributes.Public, asm.MainModule.TypeSystem.Object);
+ var transformFailed = false;
typeDef.CustomAttributes.Add(new CustomAttribute(editorBrowsableCtor)
{
@@ -277,6 +291,7 @@ namespace Avalonia.Build.Tasks
// StreamReader is needed here to handle BOM
var xaml = new StreamReader(new MemoryStream(res.FileContents)).ReadToEnd();
var parsed = XDocumentXamlParser.Parse(xaml);
+ parsed.Document = res.FilePath;
var initialRoot = (XamlAstObjectNode)parsed.Root;
@@ -338,8 +353,7 @@ namespace Avalonia.Build.Tasks
new XamlAstClrTypeReference(classDirective, classType, false));
initialRoot.Children.Remove(classDirective);
}
-
-
+
compiler.Transform(parsed);
var populateName = classType == null ? "Populate:" + res.Name : "!XamlIlPopulate";
@@ -376,29 +390,28 @@ namespace Avalonia.Build.Tasks
}
catch (Exception e)
{
- int lineNumber = 0, linePosition = 0;
- if (e is XamlParseException xe)
- {
- lineNumber = xe.LineNumber;
- linePosition = xe.LinePosition;
- }
-
- engine.LogError(BuildEngineErrorCode.TransformError, res.FilePath, e, lineNumber, linePosition);
- return false;
+ transformFailed = true;
+ engine.LogError(AvaloniaXamlDiagnosticCodes.TransformError, res.FilePath, e);
}
}
try
{
- compiler.TransformGroup(parsedXamlDocuments);
+ if (!transformFailed)
+ {
+ compiler.TransformGroup(parsedXamlDocuments);
+ }
}
- catch (XamlDocumentParseException e)
+ catch (Exception e)
{
- engine.LogError(BuildEngineErrorCode.TransformError, e.FilePath, e, e.LineNumber, e.LinePosition);
+ transformFailed = true;
+ engine.LogError(AvaloniaXamlDiagnosticCodes.TransformError, "", e);
}
- catch (XamlParseException e)
+
+ var hasAnyError = ReportDiagnostics(engine, diagnostics) || transformFailed;
+ if (hasAnyError)
{
- engine.LogError(BuildEngineErrorCode.TransformError, "", e, e.LineNumber, e.LinePosition);
+ return false;
}
foreach (var document in parsedXamlDocuments)
@@ -605,21 +618,23 @@ namespace Avalonia.Build.Tasks
}
else
{
- engine.LogWarning(BuildEngineErrorCode.Loader, "",
- $"XAML resource \"{res.Uri}\" won't be reachable via runtime loader, as no public constructor was found");
+ var diagnostic = new XamlDiagnostic(
+ AvaloniaXamlDiagnosticCodes.XamlLoaderUnreachable,
+ diagnosticsFilter.Handle(
+ XamlDiagnosticSeverity.Warning,
+ AvaloniaXamlDiagnosticCodes.XamlLoaderUnreachable),
+ $"XAML resource \"{res.Uri}\" won't be reachable via runtime loader, as no public constructor was found")
+ {
+ Document = document.FileSource?.FilePath
+ };
+ engine.LogDiagnostic(diagnostic);
}
}
}
catch (Exception e)
{
- int lineNumber = 0, linePosition = 0;
- if (e is XamlParseException xe)
- {
- lineNumber = xe.LineNumber;
- linePosition = xe.LinePosition;
- }
- engine.LogError(BuildEngineErrorCode.EmitError, res.FilePath, e, lineNumber, linePosition);
+ engine.LogError(AvaloniaXamlDiagnosticCodes.EmitError, res.FilePath, e);
return false;
}
res.Remove();
@@ -636,7 +651,6 @@ namespace Avalonia.Build.Tasks
}
}
-
return true;
}
@@ -652,6 +666,21 @@ namespace Avalonia.Build.Tasks
return true;
}
+ static bool ReportDiagnostics(IBuildEngine engine, IReadOnlyCollection diagnostics)
+ {
+ var hasAnyError = diagnostics.Any(d => d.Severity >= XamlDiagnosticSeverity.Error);
+
+ const int maxErrorsPerDocument = 100;
+ foreach (var diagnostic in diagnostics
+ .GroupBy(d => d.Document)
+ .SelectMany(d => d.Take(maxErrorsPerDocument)))
+ {
+ engine.LogDiagnostic(diagnostic);
+ }
+
+ return hasAnyError;
+ }
+
static bool? CompileCoreForRefAssembly(
IBuildEngine engine, CecilTypeSystem sourceTypeSystem, CecilTypeSystem refTypeSystem)
{
@@ -693,9 +722,7 @@ namespace Avalonia.Build.Tasks
}
catch (Exception e)
{
- engine.LogErrorEvent(new BuildErrorEventArgs("Avalonia", "XAMLIL", "",
- 0, 0, 0, 0,
- e.Message, "", "Avalonia"));
+ engine.LogError(AvaloniaXamlDiagnosticCodes.Unknown, e.Message, e);
return false;
}
diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/Avalonia.Markup.Xaml.Loader.csproj b/src/Markup/Avalonia.Markup.Xaml.Loader/Avalonia.Markup.Xaml.Loader.csproj
index 939efc60d1..bb18dd5ee3 100644
--- a/src/Markup/Avalonia.Markup.Xaml.Loader/Avalonia.Markup.Xaml.Loader.csproj
+++ b/src/Markup/Avalonia.Markup.Xaml.Loader/Avalonia.Markup.Xaml.Loader.csproj
@@ -13,6 +13,7 @@
+
diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaXamlIlRuntimeCompiler.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaXamlIlRuntimeCompiler.cs
index f3a5a9415c..480c74223f 100644
--- a/src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaXamlIlRuntimeCompiler.cs
+++ b/src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaXamlIlRuntimeCompiler.cs
@@ -209,11 +209,34 @@ namespace Avalonia.Markup.Xaml.XamlIl
var indexerClosureType = _sreBuilder.DefineType("IndexerClosure_" + Guid.NewGuid().ToString("N"));
var trampolineBuilder = _sreBuilder.DefineType("Trampolines_" + Guid.NewGuid().ToString("N"));
+ var diagnostics = new List();
+ var diagnosticsHandler = new XamlDiagnosticsHandler()
+ {
+ HandleDiagnostic = (diagnostic) =>
+ {
+ var runtimeDiagnostic = new RuntimeXamlDiagnostic(diagnostic.Code.ToString(),
+ (RuntimeXamlDiagnosticSeverity)diagnostic.Severity,
+ diagnostic.Title, diagnostic.LineNumber, diagnostic.LinePosition)
+ {
+ Document = diagnostic.Document
+ };
+ var newSeverity =
+ (XamlDiagnosticSeverity?)configuration.DiagnosticHandler?.Invoke(runtimeDiagnostic) ??
+ diagnostic.Severity;
+ diagnostic = diagnostic with { Severity = newSeverity };
+ diagnostics.Add(diagnostic);
+ return newSeverity;
+ },
+ CodeMappings = AvaloniaXamlDiagnosticCodes.XamlXDiagnosticCodeToAvalonia
+ };
+
var compiler = new AvaloniaXamlIlCompiler(new AvaloniaXamlIlCompilerConfiguration(_sreTypeSystem, asm,
_sreMappings, _sreXmlns, AvaloniaXamlIlLanguage.CustomValueConverter,
new XamlIlClrPropertyInfoEmitter(_sreTypeSystem.CreateTypeBuilder(clrPropertyBuilder)),
new XamlIlPropertyInfoAccessorFactoryEmitter(_sreTypeSystem.CreateTypeBuilder(indexerClosureType)),
- new XamlIlTrampolineBuilder(_sreTypeSystem.CreateTypeBuilder(trampolineBuilder))),
+ new XamlIlTrampolineBuilder(_sreTypeSystem.CreateTypeBuilder(trampolineBuilder)),
+ null,
+ diagnosticsHandler),
_sreEmitMappings,
_sreContextType)
{
@@ -236,8 +259,9 @@ namespace Avalonia.Markup.Xaml.XamlIl
{
overrideType = _sreTypeSystem.GetType(document.RootInstance.GetType());
}
-
+
var parsed = compiler.Parse(xaml, overrideType);
+ parsed.Document = "runtimexaml:" + parsedDocuments.Count;
compiler.Transform(parsed);
var xamlName = GetSafeUriIdentifier(document.BaseUri)
@@ -263,6 +287,8 @@ namespace Avalonia.Markup.Xaml.XamlIl
compiler.TransformGroup(parsedDocuments);
+ diagnostics.ThrowExceptionIfAnyError();
+
var createdTypes = parsedDocuments.Select(document =>
{
compiler.Compile(document.XamlDocument, document.TypeBuilderProvider, document.Uri, document.FileSource);
diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AstNodes/AvaloniaXamlIlArrayConstantAstNode.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AstNodes/AvaloniaXamlIlArrayConstantAstNode.cs
index 339b720d10..80e1807ac3 100644
--- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AstNodes/AvaloniaXamlIlArrayConstantAstNode.cs
+++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AstNodes/AvaloniaXamlIlArrayConstantAstNode.cs
@@ -22,14 +22,6 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.AstNodes
_values = values;
Type = new XamlAstClrTypeReference(lineInfo, arrayType, false);
-
- foreach (var element in values)
- {
- if (!elementType.IsAssignableFrom(element.Type.GetClrType()))
- {
- throw new XamlParseException("x:Array element is not assignable to the array element type!", lineInfo);
- }
- }
}
public IXamlAstTypeReference Type { get; }
diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlDiagnosticCodes.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlDiagnosticCodes.cs
new file mode 100644
index 0000000000..ddc5e1c2de
--- /dev/null
+++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlDiagnosticCodes.cs
@@ -0,0 +1,63 @@
+using System;
+using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers;
+using XamlX;
+using XamlX.Ast;
+
+namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions;
+
+internal static class AvaloniaXamlDiagnosticCodes
+{
+ public const string Unknown = "AVLN9999";
+
+ // XML/XAML parsing errors 1000-1999.
+ public const string ParseError = "AVLN1000";
+ public const string InvalidXAML = "AVLN1001";
+
+ // XAML transform errors 2000-2999.
+ public const string TransformError = "AVLN2000";
+ public const string DuplicateXClass = "AVLN2002";
+ public const string TypeSystemError = "AVLN2003";
+ public const string AvaloniaIntrinsicsError = "AVLN2005";
+ public const string BindingsError = "AVLN2100";
+ public const string DataContextResolvingError = "AVLN2101";
+ public const string StyleTransformError = "AVLN2200";
+ public const string SelectorsTransformError = "AVLN2201";
+ public const string PropertyPathError = "AVLN2202";
+ public const string DuplicateSetterError = "AVLN2203";
+ public const string StyleInMergedDictionaries = "AVLN2204";
+
+ // XAML emit errors 3000-3999.
+ public const string EmitError = "AVLN3000";
+ public const string XamlLoaderUnreachable = "AVLN3001";
+
+ // Generator specific errors 4000-4999.
+ public const string NameGeneratorError = "AVLN4001";
+
+ // Reserved 5000-9998
+ public const string Obsolete = "AVLN5001";
+
+ internal static string XamlXDiagnosticCodeToAvalonia(object xamlException)
+ {
+ return xamlException switch
+ {
+ XamlXWellKnownDiagnosticCodes wellKnownDiagnosticCodes => wellKnownDiagnosticCodes switch
+ {
+ XamlXWellKnownDiagnosticCodes.Obsolete => Obsolete,
+ _ => throw new ArgumentOutOfRangeException()
+ },
+
+ XamlDataContextException => DataContextResolvingError,
+ XamlBindingsTransformException => BindingsError,
+ XamlPropertyPathException => PropertyPathError,
+ XamlStyleTransformException => StyleTransformError,
+ XamlSelectorsTransformException => SelectorsTransformError,
+
+ XamlTransformException => TransformError,
+ XamlTypeSystemException => TypeSystemError,
+ XamlLoadException => EmitError,
+ XamlParseException => ParseError,
+
+ _ => Unknown
+ };
+ }
+}
diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs
index 88a0039dba..723675f975 100644
--- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs
+++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs
@@ -57,6 +57,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
new AvaloniaXamlIlPropertyPathTransformer(),
new AvaloniaXamlIlSetterTargetTypeMetadataTransformer(),
new AvaloniaXamlIlSetterTransformer(),
+ new AvaloniaXamlIlStyleValidatorTransformer(),
new AvaloniaXamlIlConstructorServiceProviderTransformer(),
new AvaloniaXamlIlTransitionsTypeMetadataTransformer(),
new AvaloniaXamlIlResolveByNameMarkupExtensionReplacer(),
@@ -126,9 +127,9 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
public List GroupTransformers { get; }
- public void TransformGroup(IReadOnlyCollection documents, bool strict = true)
+ public void TransformGroup(IReadOnlyCollection documents)
{
- var ctx = new AstGroupTransformationContext(documents, _configuration, strict);
+ var ctx = new AstGroupTransformationContext(documents, _configuration);
foreach (var transformer in GroupTransformers)
{
foreach (var doc in documents)
@@ -166,8 +167,8 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
new XamlAstClrTypeReference(classDirective,
_configuration.TypeSystem.GetType(((XamlAstTextNode)classDirective.Values[0]).Text),
false) :
- TypeReferenceResolver.ResolveType(CreateTransformationContext(parsed, true),
- (XamlAstXmlTypeReference)rootObject.Type, true);
+ TypeReferenceResolver.ResolveType(CreateTransformationContext(parsed),
+ (XamlAstXmlTypeReference)rootObject.Type);
if (overrideRootType != null)
diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompilerConfiguration.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompilerConfiguration.cs
index 9fc6b5d517..d5b760aa84 100644
--- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompilerConfiguration.cs
+++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompilerConfiguration.cs
@@ -17,8 +17,9 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
XamlIlClrPropertyInfoEmitter clrPropertyEmitter,
XamlIlPropertyInfoAccessorFactoryEmitter accessorFactoryEmitter,
XamlIlTrampolineBuilder trampolineBuilder,
- IXamlIdentifierGenerator identifierGenerator = null)
- : base(typeSystem, defaultAssembly, typeMappings, xmlnsMappings, customValueConverter, identifierGenerator)
+ IXamlIdentifierGenerator identifierGenerator,
+ XamlDiagnosticsHandler diagnosticsHandler)
+ : base(typeSystem, defaultAssembly, typeMappings, xmlnsMappings, customValueConverter, identifierGenerator, diagnosticsHandler)
{
ClrPropertyEmitter = clrPropertyEmitter;
AccessorFactoryEmitter = accessorFactoryEmitter;
diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguageParseIntrinsics.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguageParseIntrinsics.cs
index f9a121443e..b11086e74d 100644
--- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguageParseIntrinsics.cs
+++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguageParseIntrinsics.cs
@@ -6,6 +6,7 @@ using Avalonia.Controls;
using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.AstNodes;
using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers;
using Avalonia.Media;
+using XamlX;
using XamlX.Ast;
using XamlX.Transform;
using XamlX.TypeSystem;
@@ -16,6 +17,21 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
{
public static bool TryConvert(AstTransformationContext context, IXamlAstValueNode node, string text, IXamlType type, AvaloniaXamlIlWellKnownTypes types, out IXamlAstValueNode result)
{
+ bool ReturnOnParseError(string title, out IXamlAstValueNode result)
+ {
+ context.ReportDiagnostic(new XamlDiagnostic(
+ AvaloniaXamlDiagnosticCodes.AvaloniaIntrinsicsError,
+ XamlDiagnosticSeverity.Error,
+ title,
+ node)
+ {
+ // Only one instance when we can lower Error to a Warning
+ MinSeverity = XamlDiagnosticSeverity.Warning
+ });
+ result = null;
+ return false;
+ }
+
if (type.FullName == "System.TimeSpan")
{
var tsText = text.Trim();
@@ -24,11 +40,13 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
{
// // shorthand seconds format (ie. "0.25")
if (!tsText.Contains(":") && double.TryParse(tsText,
- NumberStyles.Float | NumberStyles.AllowThousands,
- CultureInfo.InvariantCulture, out var seconds))
+ NumberStyles.Float | NumberStyles.AllowThousands,
+ CultureInfo.InvariantCulture, out var seconds))
timeSpan = TimeSpan.FromSeconds(seconds);
else
- throw new XamlX.XamlLoadException($"Unable to parse {text} as a time span", node);
+ {
+ return ReturnOnParseError($"Unable to parse {text} as a time span", out result);
+ }
}
result = new XamlStaticOrTargetedReturnMethodCallNode(node,
@@ -56,7 +74,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
}
catch
{
- throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a thickness", node);
+ return ReturnOnParseError($"Unable to parse \"{text}\" as a thickness", out result);
}
}
@@ -73,7 +91,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
}
catch
{
- throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a point", node);
+ return ReturnOnParseError($"Unable to parse \"{text}\" as a point", out result);
}
}
@@ -90,7 +108,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
}
catch
{
- throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a vector", node);
+ return ReturnOnParseError($"Unable to parse \"{text}\" as a vector", out result);
}
}
@@ -107,7 +125,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
}
catch
{
- throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a size", node);
+ return ReturnOnParseError($"Unable to parse \"{text}\" as a size", out result);
}
}
@@ -124,7 +142,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
}
catch
{
- throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a matrix", node);
+ return ReturnOnParseError($"Unable to parse \"{text}\" as a matrix", out result);
}
}
@@ -141,7 +159,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
}
catch
{
- throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a corner radius", node);
+ return ReturnOnParseError($"Unable to parse \"{text}\" as a corner radius", out result);
}
}
@@ -149,7 +167,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
{
if (!Color.TryParse(text, out Color color))
{
- throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a color", node);
+ return ReturnOnParseError($"Unable to parse \"{text}\" as a color", out result);
}
result = new XamlStaticOrTargetedReturnMethodCallNode(node,
@@ -179,7 +197,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
}
catch
{
- throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a relative point", node);
+ return ReturnOnParseError($"Unable to parse \"{text}\" as a relative point", out result);
}
}
@@ -195,7 +213,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
}
catch
{
- throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a grid length", node);
+ return ReturnOnParseError($"Unable to parse \"{text}\" as a grid length", out result);
}
}
@@ -218,7 +236,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
}
catch
{
- throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a grid length", node);
+ return ReturnOnParseError($"Unable to parse \"{text}\" as a grid length", out result);
}
}
@@ -295,7 +313,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
if (string.IsNullOrWhiteSpace(uriText) || !Uri.TryCreate(uriText, kind, out var _))
{
- throw new XamlX.XamlLoadException($"Unable to parse text {uriText} as a {kind} uri.", node);
+ return ReturnOnParseError($"Unable to parse text \"{uriText}\" as a {kind} uri", out result);
}
result = new XamlAstNewClrObjectNode(node
, new(node, types.Uri, false)
@@ -341,7 +359,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
}
else
{
- throw new XamlX.XamlLoadException($"Invalid PointsList.", node);
+ return ReturnOnParseError($"Unable to parse text \"{text}\" as a Points list", out result);
}
}
else
@@ -388,6 +406,14 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
nodes[index] = itemNode;
}
+ foreach (var element in nodes)
+ {
+ if (!elementType.IsAssignableFrom(element.Type.GetClrType()))
+ {
+ return ReturnOnParseError($"x:Array element {element.Type.GetClrType().Name} is not assignable to the array element type {elementType.Name}", out result);
+ }
+ }
+
if (types.AvaloniaList.MakeGenericType(elementType).IsAssignableFrom(type))
{
result = new AvaloniaXamlIlAvaloniaListConstantAstNode(node, types, type, elementType, nodes);
diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/GroupTransformers/IXamlAstGroupTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/GroupTransformers/IXamlAstGroupTransformer.cs
index eeb5293325..37b1952c49 100644
--- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/GroupTransformers/IXamlAstGroupTransformer.cs
+++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/GroupTransformers/IXamlAstGroupTransformer.cs
@@ -9,55 +9,32 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.GroupTransformers;
internal class AstGroupTransformationContext : AstTransformationContext
{
- public AstGroupTransformationContext(IReadOnlyCollection documents, TransformerConfiguration configuration, bool strictMode = true)
- : base(configuration, null, strictMode)
+ public AstGroupTransformationContext(
+ IReadOnlyCollection documents,
+ TransformerConfiguration configuration)
+ : base(configuration, null)
{
Documents = documents;
}
+ public override string Document => CurrentDocument?.FileSource?.FilePath ?? "{unknown document}";
public IXamlDocumentResource CurrentDocument { get; set; }
public IReadOnlyCollection Documents { get; }
- public new IXamlAstNode ParseError(string message, IXamlAstNode node) =>
- Error(node, new XamlDocumentParseException(CurrentDocument?.FileSource?.FilePath, message, node));
-
- public new IXamlAstNode ParseError(string message, IXamlAstNode offender, IXamlAstNode ret) =>
- Error(ret, new XamlDocumentParseException(CurrentDocument?.FileSource?.FilePath, message, offender));
-
- class Visitor : IXamlAstVisitor
+ class Visitor : ContextXamlAstVisitor
{
- private readonly AstGroupTransformationContext _context;
private readonly IXamlAstGroupTransformer _transformer;
- public Visitor(AstGroupTransformationContext context, IXamlAstGroupTransformer transformer)
+ public Visitor(AstGroupTransformationContext context, IXamlAstGroupTransformer transformer) : base(context)
{
- _context = context;
_transformer = transformer;
}
-
- public IXamlAstNode Visit(IXamlAstNode node)
- {
-#if Xaml_DEBUG
- return _transformer.Transform(_context, node);
-#else
- try
- {
- return _transformer.Transform(_context, node);
- }
- catch (Exception e) when (!(e is XmlException))
- {
- throw new XamlDocumentParseException(
- _context.CurrentDocument?.FileSource?.FilePath,
- "Internal compiler error while transforming node " + node + ":\n" + e,
- node);
- }
-#endif
- }
- public void Push(IXamlAstNode node) => _context.PushParent(node);
+ public override string GetTransformerInfo() => _transformer.GetType().Name;
- public void Pop() => _context.PopParent();
+ public override IXamlAstNode VisitCore(AstTransformationContext context, IXamlAstNode node) =>
+ _transformer.Transform((AstGroupTransformationContext)context, node);
}
public IXamlAstNode Visit(IXamlAstNode root, IXamlAstGroupTransformer transformer)
diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/GroupTransformers/XamlIncludeGroupTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/GroupTransformers/XamlIncludeGroupTransformer.cs
index 28a48e5855..fe96b43d2f 100644
--- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/GroupTransformers/XamlIncludeGroupTransformer.cs
+++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/GroupTransformers/XamlIncludeGroupTransformer.cs
@@ -38,8 +38,7 @@ internal class AvaloniaXamlIncludeTransformer : IXamlAstGroupTransformer
if (valueNode.Manipulation is not XamlObjectInitializationNode initializationNode)
{
- throw new XamlDocumentParseException(context.CurrentDocument,
- $"Invalid \"{nodeTypeName}\" node initialization.", valueNode);
+ throw new InvalidOperationException($"Invalid \"{nodeTypeName}\" node initialization.");
}
var additionalProperties = new List();
@@ -56,8 +55,9 @@ internal class AvaloniaXamlIncludeTransformer : IXamlAstGroupTransformer
}
else
{
- throw new XamlDocumentParseException(context.CurrentDocument,
+ context.ReportTransformError(
$"Source property must be set on the \"{nodeTypeName}\" node.", valueNode);
+ return node;
}
}
@@ -89,22 +89,25 @@ internal class AvaloniaXamlIncludeTransformer : IXamlAstGroupTransformer
return FromType(context, targetDocument.ClassType, sourceUriNode, expectedLoadedType, node, assetPathUri, assembly, additionalProperties);
}
- return context.ParseError(
+ context.ReportTransformError(
$"Unable to resolve XAML resource \"{assetPathUri}\" in the current assembly.",
- sourceUriNode, node);
+ sourceUriNode);
+ return node;
}
// If resource wasn't found in the current assembly, search in the others.
if (context.Configuration.TypeSystem.FindAssembly(assembly) is not { } assetAssembly)
{
- return context.ParseError($"Assembly \"{assembly}\" was not found from the \"{assetPathUri}\" source.", sourceUriNode, node);
+ context.ReportTransformError($"Assembly \"{assembly}\" was not found from the \"{assetPathUri}\" source.", sourceUriNode);
+ return node;
}
var avaResType = assetAssembly.FindType("CompiledAvaloniaXaml.!AvaloniaResources");
if (avaResType is null)
{
- return context.ParseError(
- $"Unable to resolve \"!AvaloniaResources\" type on \"{assembly}\" assembly.", sourceUriNode, node);
+ context.ReportTransformError(
+ $"Unable to resolve \"!AvaloniaResources\" type on \"{assembly}\" assembly.", sourceUriNode);
+ return node;
}
var relativeName = "Build:" + assetPath.Substring(assemblyNameSeparator);
@@ -118,20 +121,22 @@ internal class AvaloniaXamlIncludeTransformer : IXamlAstGroupTransformer
return FromType(context, type, sourceUriNode, expectedLoadedType, node, assetPathUri, assembly, additionalProperties);
}
- return context.ParseError(
+ context.ReportTransformError(
$"Unable to resolve XAML resource \"{assetPathUri}\" in the \"{assembly}\" assembly. Make sure this file exists and is public.",
- sourceUriNode, node);
+ sourceUriNode);
+ return node;
}
- private static IXamlAstNode FromType(AstTransformationContext context, IXamlType type, IXamlAstNode li,
+ private static IXamlAstNode FromType(AstGroupTransformationContext context, IXamlType type, IXamlAstNode li,
IXamlType expectedLoadedType, IXamlAstNode fallbackNode, string assetPathUri, string assembly,
IEnumerable manipulationNodes)
{
if (!expectedLoadedType.IsAssignableFrom(type))
{
- return context.ParseError(
+ context.ReportTransformError(
$"Resource \"{assetPathUri}\" is defined as \"{type}\" type in the \"{assembly}\" assembly, but expected \"{expectedLoadedType}\".",
- li, fallbackNode);
+ li);
+ return fallbackNode;
}
IXamlAstNode newObjNode = new XamlAstObjectNode(li, new XamlAstClrTypeReference(li, type, false));
@@ -141,15 +146,16 @@ internal class AvaloniaXamlIncludeTransformer : IXamlAstGroupTransformer
return new NewObjectTransformer().Transform(context, newObjNode);
}
- private static IXamlAstNode FromMethod(AstTransformationContext context, IXamlMethod method, IXamlAstNode li,
+ private static IXamlAstNode FromMethod(AstGroupTransformationContext context, IXamlMethod method, IXamlAstNode li,
IXamlType expectedLoadedType, IXamlAstNode fallbackNode, string assetPathUri, string assembly,
IEnumerable manipulationNodes)
{
if (!expectedLoadedType.IsAssignableFrom(method.ReturnType))
{
- return context.ParseError(
+ context.ReportTransformError(
$"Resource \"{assetPathUri}\" is defined as \"{method.ReturnType}\" type in the \"{assembly}\" assembly, but expected \"{expectedLoadedType}\".",
- li, fallbackNode);
+ li);
+ return fallbackNode;
}
var sp = context.Configuration.TypeMappings.ServiceProvider;
@@ -164,6 +170,13 @@ internal class AvaloniaXamlIncludeTransformer : IXamlAstGroupTransformer
AstGroupTransformationContext context, string nodeTypeName, XamlPropertyAssignmentNode sourceProperty,
bool strictSourceValueType)
{
+ void OnInvalidSource(IXamlAstNode node) =>
+ context.ReportDiagnostic(
+ AvaloniaXamlDiagnosticCodes.TransformError,
+ strictSourceValueType ? XamlDiagnosticSeverity.Error : XamlDiagnosticSeverity.Warning,
+ $"\"{nodeTypeName}.Source\" supports only \"avares://\" absolute or relative uri. This {nodeTypeName} will be resolved in runtime instead.",
+ node);
+
// We expect that AvaloniaXamlIlLanguageParseIntrinsics has already parsed the Uri and created node like: `new Uri(assetPath, uriKind)`.
if (sourceProperty.Values.OfType().FirstOrDefault() is not { } sourceUriNode
|| sourceUriNode.Type.GetClrType() != context.GetAvaloniaTypes().Uri
@@ -172,16 +185,7 @@ internal class AvaloniaXamlIncludeTransformer : IXamlAstGroupTransformer
{
// Source value can be set with markup extension instead of the Uri object node, we don't support it here yet.
var anyPropValue = sourceProperty.Values.FirstOrDefault();
- if (strictSourceValueType)
- {
- context.Error(anyPropValue,
- new XamlDocumentParseException(context.CurrentDocument,
- $"\"{nodeTypeName}.Source\" supports only \"avares://\" absolute or relative uri.", anyPropValue));
- }
- else
- {
- // TODO: make it a compiler warning
- }
+ OnInvalidSource(anyPropValue);
return (null, anyPropValue);
}
@@ -193,9 +197,7 @@ internal class AvaloniaXamlIncludeTransformer : IXamlAstGroupTransformer
}
else if (!uriPath.Scheme.Equals("avares", StringComparison.CurrentCultureIgnoreCase))
{
- context.Error(sourceUriNode,
- new XamlDocumentParseException(context.CurrentDocument,
- $"\"{nodeTypeName}.Source\" supports only \"avares://\" absolute or relative uri.", sourceUriNode));
+ OnInvalidSource(sourceUriNode);
return (null, sourceUriNode);
}
diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/GroupTransformers/XamlMergeResourceGroupTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/GroupTransformers/XamlMergeResourceGroupTransformer.cs
index f135ed5ffb..61a12aa72d 100644
--- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/GroupTransformers/XamlMergeResourceGroupTransformer.cs
+++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/GroupTransformers/XamlMergeResourceGroupTransformer.cs
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers;
+using XamlX;
using XamlX.Ast;
using XamlX.TypeSystem;
@@ -22,6 +23,7 @@ internal class XamlMergeResourceGroupTransformer : IXamlAstGroupTransformer
var mergeResourceIncludeType = context.GetAvaloniaTypes().MergeResourceInclude;
var mergeSourceNodes = new List();
+ var shouldExit = false; // if any manipulation node has an error, we should stop processing further.
foreach (var manipulationNode in resourceDictionaryManipulation.Children.ToArray())
{
void ProcessXamlPropertyAssignmentNode(XamlManipulationGroupNode parent, XamlPropertyAssignmentNode assignmentNode)
@@ -39,14 +41,16 @@ internal class XamlMergeResourceGroupTransformer : IXamlAstGroupTransformer
}
else
{
- throw new XamlDocumentParseException(context.CurrentDocument,
+ shouldExit = true;
+ context.ReportTransformError(
"Invalid MergeResourceInclude node found. Make sure that Source property is set.",
valueNode);
}
}
else if (mergeSourceNodes.Any())
{
- throw new XamlDocumentParseException(context.CurrentDocument,
+ shouldExit = true;
+ context.ReportTransformError(
"MergeResourceInclude should always be included last when mixing with other dictionaries inside of the ResourceDictionary.MergedDictionaries.",
valueNode);
}
@@ -66,7 +70,7 @@ internal class XamlMergeResourceGroupTransformer : IXamlAstGroupTransformer
}
}
- if (!mergeSourceNodes.Any())
+ if (shouldExit || !mergeSourceNodes.Any())
{
return node;
}
@@ -78,7 +82,7 @@ internal class XamlMergeResourceGroupTransformer : IXamlAstGroupTransformer
AvaloniaXamlIncludeTransformer.ResolveSourceFromXamlInclude(context, "MergeResourceInclude", sourceNode, true);
if (originalAssetPath is null)
{
- return context.ParseError(
+ return context.ReportTransformError(
$"Node MergeResourceInclude is unable to resolve \"{originalAssetPath}\" path.", propertyNode, node);
}
@@ -86,7 +90,7 @@ internal class XamlMergeResourceGroupTransformer : IXamlAstGroupTransformer
string.Equals(d.Uri, originalAssetPath, StringComparison.InvariantCultureIgnoreCase));
if (targetDocument?.XamlDocument.Root is not XamlValueWithManipulationNode targetDocumentRoot)
{
- return context.ParseError(
+ return context.ReportTransformError(
$"Node MergeResourceInclude is unable to resolve \"{originalAssetPath}\" path.", propertyNode, node);
}
@@ -94,7 +98,7 @@ internal class XamlMergeResourceGroupTransformer : IXamlAstGroupTransformer
.Children.OfType().Single();
if (singleRootObject.Type != resourceDictionaryType)
{
- return context.ParseError(
+ return context.ReportTransformError(
"MergeResourceInclude can only include another ResourceDictionary", propertyNode, node);
}
diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaBindingExtensionTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaBindingExtensionTransformer.cs
index de2c0eab96..e530b39a65 100644
--- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaBindingExtensionTransformer.cs
+++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaBindingExtensionTransformer.cs
@@ -1,3 +1,4 @@
+using System;
using System.Linq;
using XamlX;
using XamlX.Ast;
@@ -8,6 +9,14 @@ using XamlParseException = XamlX.XamlParseException;
namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
{
+ class XamlBindingsTransformException : XamlTransformException
+ {
+ public XamlBindingsTransformException(string message, IXamlLineInfo lineInfo, Exception innerException = null)
+ : base(message, lineInfo, innerException)
+ {
+ }
+ }
+
class AvaloniaBindingExtensionTransformer : IXamlAstTransformer
{
public bool CompileBindingsByDefault { get; set; }
@@ -32,7 +41,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
if (!(directive.Values[0] is XamlAstTextNode text
&& bool.TryParse(text.Text, out var compileBindings)))
{
- throw new XamlParseException("The value of x:CompileBindings must be a literal boolean value.", directive.Values[0]);
+ throw new XamlBindingsTransformException("The value of x:CompileBindings must be a literal boolean value.", directive.Values[0]);
}
obj.Children.Remove(directive);
diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPathParser.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPathParser.cs
index 736e764aa7..b62066e916 100644
--- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPathParser.cs
+++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPathParser.cs
@@ -104,12 +104,12 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
}
else if (elementNameProperty != null)
{
- throw new XamlParseException($"Invalid ElementName '{elementNameProperty.Values[0]}'.", elementNameProperty.Values[0]);
+ throw new XamlBindingsTransformException($"Invalid ElementName '{elementNameProperty.Values[0]}'.", elementNameProperty.Values[0]);
}
if (sourceProperty != null && convertedNode != null)
{
- throw new XamlParseException("Only one of ElementName, Source, or RelativeSource specified as a binding source. Only one property is allowed.", binding);
+ throw new XamlBindingsTransformException("Only one of ElementName, Source, or RelativeSource specified as a binding source. Only one property is allowed.", binding);
}
if (GetRelativeSourceObjectFromAssignment(
@@ -119,7 +119,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
{
if (convertedNode != null)
{
- throw new XamlParseException("Only one of ElementName, Source, or RelativeSource specified as a binding source. Only one property is allowed.", binding);
+ throw new XamlBindingsTransformException("Only one of ElementName, Source, or RelativeSource specified as a binding source. Only one property is allowed.", binding);
}
var modeProperty = relativeSourceObject.Children
@@ -159,14 +159,14 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
true).GetClrType(),
XamlTypeExtensionNode typeExtensionNode => typeExtensionNode.Value.GetClrType(),
null => null,
- _ => throw new XamlParseException($"Unsupported node for AncestorType property", relativeSourceObject)
+ _ => throw new XamlBindingsTransformException($"Unsupported node for AncestorType property", relativeSourceObject)
};
if (ancestorType is null)
{
if (treeType == "Visual")
{
- throw new XamlParseException("AncestorType must be set for RelativeSourceMode.FindAncestor when searching the visual tree.", relativeSourceObject);
+ throw new XamlBindingsTransformException("AncestorType must be set for RelativeSourceMode.FindAncestor when searching the visual tree.", relativeSourceObject);
}
else if (treeType == "Logical")
{
@@ -180,7 +180,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
if (ancestorType is null)
{
- throw new XamlX.XamlParseException("Unable to resolve implicit ancestor type based on XAML tree.", relativeSourceObject);
+ throw new XamlBindingsTransformException("Unable to resolve implicit ancestor type based on XAML tree.", relativeSourceObject);
}
}
}
@@ -203,7 +203,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
}
else
{
- throw new XamlParseException($"Unknown tree type '{treeType}'.", binding);
+ throw new XamlBindingsTransformException($"Unknown tree type '{treeType}'.", binding);
}
}
else if (mode == "DataContext")
@@ -221,20 +221,20 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
x.ScopeType == AvaloniaXamlIlTargetTypeMetadataNode.ScopeTypes.ControlTemplate);
if (contentTemplateNode is null)
{
- throw new XamlParseException("A binding with a TemplatedParent RelativeSource has to be in a ControlTemplate.", binding);
+ throw new XamlBindingsTransformException("A binding with a TemplatedParent RelativeSource has to be in a ControlTemplate.", binding);
}
var parentType = contentTemplateNode.TargetType.GetClrType();
if (parentType is null)
{
- throw new XamlParseException("TargetType has to be set on ControlTemplate or it should be defined inside of a Style.", binding);
+ throw new XamlBindingsTransformException("TargetType has to be set on ControlTemplate or it should be defined inside of a Style.", binding);
}
convertedNode = new TemplatedParentBindingExpressionNode { Type = parentType };
}
else
{
- throw new XamlParseException($"Unknown RelativeSource mode '{mode}'.", binding);
+ throw new XamlBindingsTransformException($"Unknown RelativeSource mode '{mode}'.", binding);
}
}
@@ -265,7 +265,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
{
if (me.Type.GetClrType() != context.GetAvaloniaTypes().RelativeSource)
{
- throw new XamlParseException($"Expected an object of type 'Avalonia.Data.RelativeSource'. Found a object of type '{me.Type.GetClrType().GetFqn()}'", me);
+ throw new XamlBindingsTransformException($"Expected an object of type 'Avalonia.Data.RelativeSource'. Found a object of type '{me.Type.GetClrType().GetFqn()}'", me);
}
relativeSourceObject = (XamlAstObjectNode)me.Value;
@@ -276,7 +276,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
{
if (on.Type.GetClrType() != context.GetAvaloniaTypes().RelativeSource)
{
- throw new XamlParseException($"Expected an object of type 'Avalonia.Data.RelativeSource'. Found a object of type '{on.Type.GetClrType().GetFqn()}'", on);
+ throw new XamlBindingsTransformException($"Expected an object of type 'Avalonia.Data.RelativeSource'. Found a object of type '{on.Type.GetClrType().GetFqn()}'", on);
}
relativeSourceObject = on;
diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPathTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPathTransformer.cs
index 3edc458fd2..fa697972e9 100644
--- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPathTransformer.cs
+++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPathTransformer.cs
@@ -116,7 +116,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
var parentDataContextNode = context.ParentNodes().OfType().FirstOrDefault();
if (parentDataContextNode is null)
{
- throw new XamlX.XamlParseException("Cannot parse a compiled binding without an explicit x:DataType directive to give a starting data type for bindings.", binding);
+ throw new XamlBindingsTransformException("Cannot parse a compiled binding without an explicit x:DataType directive to give a starting data type for bindings.", binding);
}
return parentDataContextNode.DataContextType;
diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlControlThemeTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlControlThemeTransformer.cs
index 51fe58d1c9..09b6779fa3 100644
--- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlControlThemeTransformer.cs
+++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlControlThemeTransformer.cs
@@ -21,7 +21,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
var targetTypeNode = on.Children.OfType()
.FirstOrDefault(p => p.Property.GetClrProperty().Name == "TargetType") ??
- throw new XamlParseException("ControlTheme must have a TargetType.", node);
+ throw new XamlTransformException("ControlTheme must have a TargetType.", node);
IXamlType targetType;
@@ -30,7 +30,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
else if (targetTypeNode.Values[0] is XamlAstTextNode text)
targetType = TypeReferenceResolver.ResolveType(context, text.Text, false, text, true).GetClrType();
else
- throw new XamlParseException("Could not determine TargetType for ControlTheme.", targetTypeNode);
+ throw new XamlTransformException("Could not determine TargetType for ControlTheme.", targetTypeNode);
return new AvaloniaXamlIlTargetTypeMetadataNode(on,
new XamlAstClrTypeReference(targetTypeNode, targetType, false),
diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDataContextTypeTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDataContextTypeTransformer.cs
index a24d4eb6e9..488fcef46c 100644
--- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDataContextTypeTransformer.cs
+++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDataContextTypeTransformer.cs
@@ -10,6 +10,14 @@ using XamlX.TypeSystem;
namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
{
+ class XamlDataContextException : XamlTransformException
+ {
+ public XamlDataContextException(string message, IXamlLineInfo lineInfo, Exception innerException = null)
+ : base(message, lineInfo, innerException)
+ {
+ }
+ }
+
class AvaloniaXamlIlDataContextTypeTransformer : IXamlAstTransformer
{
public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node)
@@ -43,7 +51,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
}
else
{
- throw new XamlX.XamlParseException("x:DataType should be set to a type name.", directive.Values[0]);
+ throw new XamlDataContextException("x:DataType should be set to a type name.", directive.Values[0]);
}
}
}
@@ -136,7 +144,9 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
var parentItemsDataContext = context.ParentNodes().SkipWhile(n => n != parentObject).OfType().FirstOrDefault();
if (parentItemsDataContext != null)
{
- itemsCollectionType = XamlIlBindingPathHelper.UpdateCompiledBindingExtension(context, parentItemsBinding, () => parentItemsDataContext.DataContextType, parentObject.Type.GetClrType());
+ itemsCollectionType = XamlIlBindingPathHelper.UpdateCompiledBindingExtension(context,
+ parentItemsBinding, () => parentItemsDataContext.DataContextType,
+ parentObject.Type.GetClrType());
}
}
}
@@ -179,13 +189,15 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
var parentDataContextNode = context.ParentNodes().OfType().FirstOrDefault();
if (parentDataContextNode is null)
{
- throw new XamlX.XamlParseException("Cannot parse a compiled binding without an explicit x:DataType directive to give a starting data type for bindings.", obj);
+ throw new XamlDataContextException("Cannot parse a compiled binding without an explicit x:DataType directive to give a starting data type for bindings.", obj);
}
return parentDataContextNode.DataContextType;
};
- var bindingResultType = XamlIlBindingPathHelper.UpdateCompiledBindingExtension(context, obj, startTypeResolver, on.Type.GetClrType());
+ var bindingResultType =
+ XamlIlBindingPathHelper.UpdateCompiledBindingExtension(context, obj, startTypeResolver,
+ on.Type.GetClrType());
return new AvaloniaXamlIlDataContextTypeMetadataNode(on, bindingResultType);
}
@@ -222,6 +234,6 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
{
}
- public override IXamlType DataContextType => throw new XamlTransformException("Unable to infer DataContext type for compiled bindings nested within this element. Please set x:DataType on the Binding or parent.", Value);
+ public override IXamlType DataContextType => XamlPseudoType.Unknown;
}
}
diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDuplicateSettersChecker.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDuplicateSettersChecker.cs
index 4ab9594cd8..c019ca6853 100644
--- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDuplicateSettersChecker.cs
+++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDuplicateSettersChecker.cs
@@ -36,7 +36,10 @@ class AvaloniaXamlIlDuplicateSettersChecker : IXamlAstTransformer
{
if (!index.Add(property))
{
- throw new XamlParseException($"Duplicate setter encountered for property '{property}'", node);
+ context.ReportDiagnostic(new XamlDiagnostic(
+ AvaloniaXamlDiagnosticCodes.DuplicateSetterError,
+ XamlDiagnosticSeverity.Warning,
+ $"Duplicate setter encountered for property '{property}'", node));
}
}
diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlOptionMarkupExtensionTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlOptionMarkupExtensionTransformer.cs
index 13de455b96..06db71f3eb 100644
--- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlOptionMarkupExtensionTransformer.cs
+++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlOptionMarkupExtensionTransformer.cs
@@ -38,7 +38,7 @@ internal class AvaloniaXamlIlOptionMarkupExtensionTransformer : IXamlAstTransfor
{
if (objectNode.Arguments.Count > 1)
{
- throw new XamlParseException("Options MarkupExtensions allow only single argument", objectNode);
+ throw new XamlTransformException("Options MarkupExtensions allow only single argument", objectNode);
}
defaultValue = TransformNode(new[] { argument }, typeArgument, objectNode);
@@ -68,20 +68,20 @@ internal class AvaloniaXamlIlOptionMarkupExtensionTransformer : IXamlAstTransfor
?? Array.Empty();
if (options.Length == 0)
{
- throw new XamlParseException("On.Options string must be set", onObj);
+ throw new XamlTransformException("On.Options string must be set", onObj);
}
var content = onObj.Children.OfType()
.SingleOrDefault(v => v.Property.GetClrProperty().Name == "Content");
if (content is null)
{
- throw new XamlParseException("On content object must be set", onObj);
+ throw new XamlTransformException("On content object must be set", onObj);
}
var propertiesSet = options
.Select(o => type.GetAllProperties()
.FirstOrDefault(p => o.Equals(p.Name, StringComparison.Ordinal))
- ?? throw new XamlParseException($"Property \"{o}\" wasn't found on the \"{type.Name}\" type", onObj))
+ ?? throw new XamlTransformException($"Property \"{o}\" wasn't found on the \"{type.Name}\" type", onObj))
.ToArray();
foreach (var propertySet in propertiesSet)
{
@@ -102,7 +102,7 @@ internal class AvaloniaXamlIlOptionMarkupExtensionTransformer : IXamlAstTransfor
if (defaultValue is null && !values.Any())
{
- throw new XamlParseException("Options markup extension requires at least one option to be set", objectNode);
+ throw new XamlTransformException("Options markup extension requires at least one option to be set", objectNode);
}
return new OptionsMarkupExtensionNode(
@@ -125,7 +125,7 @@ internal class AvaloniaXamlIlOptionMarkupExtensionTransformer : IXamlAstTransfor
var option = optAttr.Parameters.Single();
if (option is null)
{
- throw new XamlParseException("MarkupExtension option must not be null", li);
+ throw new XamlTransformException("MarkupExtension option must not be null", li);
}
var optionAsString = option.ToString();
@@ -173,7 +173,7 @@ internal class AvaloniaXamlIlOptionMarkupExtensionTransformer : IXamlAstTransfor
}
}
- throw new XamlParseException($"Option value \"{optionAsString}\" is not assignable to any of existing ShouldProvideOption methods", li);
+ throw new XamlTransformException($"Option value \"{optionAsString}\" is not assignable to any of existing ShouldProvideOption methods", li);
}
return false;
@@ -198,7 +198,7 @@ internal class AvaloniaXamlIlOptionMarkupExtensionTransformer : IXamlAstTransfor
if (values.Count > 1)
{
- throw new XamlParseException("Options markup extension supports only a singular value", line);
+ throw new XamlTransformException("Options markup extension supports only a singular value", line);
}
return values.Single();
diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlPropertyPathTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlPropertyPathTransformer.cs
index 44b469c34e..067e01e671 100644
--- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlPropertyPathTransformer.cs
+++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlPropertyPathTransformer.cs
@@ -12,6 +12,14 @@ using XamlX.IL;
namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
{
+ class XamlPropertyPathException : XamlTransformException
+ {
+ public XamlPropertyPathException(string message, IXamlLineInfo lineInfo, Exception innerException = null)
+ : base(message, lineInfo, innerException)
+ {
+ }
+ }
+
class AvaloniaXamlIlPropertyPathTransformer : IXamlAstTransformer
{
public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node)
@@ -26,9 +34,9 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
var parentScope = context.ParentNodes().OfType()
.FirstOrDefault();
if(parentScope == null)
- throw new XamlX.XamlParseException("No target type scope found for property path", text);
+ throw new XamlPropertyPathException("No target type scope found for property path", text);
if (parentScope.ScopeType != AvaloniaXamlIlTargetTypeMetadataNode.ScopeTypes.Style)
- throw new XamlX.XamlParseException("PropertyPath is currently only valid for styles", pv);
+ throw new XamlPropertyPathException("PropertyPath is currently only valid for styles", pv);
IEnumerable parsed;
@@ -38,7 +46,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
}
catch (Exception e)
{
- throw new XamlX.XamlParseException("Unable to parse PropertyPath: " + e.Message, text);
+ throw new XamlPropertyPathException("Unable to parse PropertyPath: " + e.Message, text, innerException: e);
}
var elements = new List();
@@ -59,7 +67,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
void HandleProperty(string name, string typeNamespace, string typeName)
{
if(!expectProperty || currentType == null)
- throw new XamlX.XamlParseException("Unexpected property node", text);
+ throw new XamlPropertyPathException("Unexpected property node", text);
var propertySearchType =
typeName != null ? GetType(typeNamespace, typeName) : currentType;
@@ -80,7 +88,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
}
if (prop == null)
- throw new XamlX.XamlParseException(
+ throw new XamlPropertyPathException(
$"Unable to resolve property {name} on type {propertySearchType.GetFqn()}",
text);
@@ -95,7 +103,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
if (ge is PropertyPathGrammar.ChildTraversalSyntax)
{
if (!expectTraversal)
- throw new XamlX.XamlParseException("Unexpected child traversal .", text);
+ throw new XamlPropertyPathException("Unexpected child traversal .", text);
elements.Add(new XamlIlChildTraversalPropertyPathElementNode());
expectTraversal = expectCast = false;
expectProperty = true;
@@ -103,7 +111,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
else if (ge is PropertyPathGrammar.EnsureTypeSyntax ets)
{
if(!expectCast)
- throw new XamlX.XamlParseException("Unexpected cast node", text);
+ throw new XamlPropertyPathException("Unexpected cast node", text);
currentType = GetType(ets.TypeNamespace, ets.TypeName);
elements.Add(new XamlIlCastPropertyPathElementNode(currentType, true));
expectProperty = false;
@@ -112,7 +120,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
else if (ge is PropertyPathGrammar.CastTypeSyntax cts)
{
if(!expectCast)
- throw new XamlX.XamlParseException("Unexpected cast node", text);
+ throw new XamlPropertyPathException("Unexpected cast node", text);
//TODO: Check if cast can be done
currentType = GetType(cts.TypeNamespace, cts.TypeName);
elements.Add(new XamlIlCastPropertyPathElementNode(currentType, false));
@@ -128,12 +136,12 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
HandleProperty(tqps.Name, tqps.TypeNamespace, tqps.TypeName);
}
else
- throw new XamlX.XamlParseException("Unexpected node " + ge, text);
+ throw new XamlPropertyPathException("Unexpected node " + ge, text);
}
var propertyPathNode = new XamlIlPropertyPathNode(text, elements, types);
if (propertyPathNode.Type == null)
- throw new XamlX.XamlParseException("Unexpected end of the property path", text);
+ throw new XamlPropertyPathException("Unexpected end of the property path", text);
pv.Values[0] = propertyPathNode;
}
diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSelectorTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSelectorTransformer.cs
index edddc5424a..5292aadab2 100644
--- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSelectorTransformer.cs
+++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSelectorTransformer.cs
@@ -13,8 +13,14 @@ using XamlX.TypeSystem;
namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
{
- using XamlParseException = XamlX.XamlParseException;
- using XamlLoadException = XamlX.XamlLoadException;
+ internal class XamlSelectorsTransformException : XamlTransformException
+ {
+ public XamlSelectorsTransformException(string message, IXamlLineInfo lineInfo, Exception innerException = null)
+ : base(message, lineInfo, innerException)
+ {
+ }
+ }
+
class AvaloniaXamlIlSelectorTransformer : IXamlAstTransformer
{
public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node)
@@ -30,14 +36,15 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
return node;
if (pn.Values.Count != 1)
- throw new XamlParseException("Selector property should should have exactly one value", node);
+ throw new XamlSelectorsTransformException("Selector property should should have exactly one value",
+ node);
if (pn.Values[0] is XamlIlSelectorNode)
//Deja vu. I've just been in this place before
return node;
if (!(pn.Values[0] is XamlAstTextNode tn))
- throw new XamlParseException("Selector property should be a text node", node);
+ throw new XamlSelectorsTransformException("Selector property should be a text node", node);
var selectorType = pn.Property.GetClrProperty().Getter.ReturnType;
var initialNode = new XamlIlSelectorInitialNode(node, selectorType);
@@ -69,18 +76,18 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
var type = result?.TargetType;
if (type == null)
- throw new XamlParseException("Property selectors must be applied to a type.", node);
+ throw new XamlTransformException("Property selectors must be applied to a type.", node);
var targetProperty =
type.GetAllProperties().FirstOrDefault(p => p.Name == property.Property);
if (targetProperty == null)
- throw new XamlParseException($"Cannot find '{property.Property}' on '{type}", node);
+ throw new XamlTransformException($"Cannot find '{property.Property}' on '{type}", node);
if (!XamlTransformHelpers.TryGetCorrectlyTypedValue(context,
new XamlAstTextNode(node, property.Value, type: context.Configuration.WellKnownTypes.String),
targetProperty.PropertyType, out var typedValue))
- throw new XamlParseException(
+ throw new XamlTransformException(
$"Cannot convert '{property.Value}' to '{targetProperty.PropertyType.GetFqn()}",
node);
@@ -92,13 +99,13 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
var targetType = result?.TargetType;
if (targetType == null)
{
- throw new XamlParseException("Attached Property selectors must be applied to a type.",node);
+ throw new XamlTransformException("Attached Property selectors must be applied to a type.",node);
}
var attachedPropertyOwnerType = typeResolver(attachedProperty.Xmlns, attachedProperty.TypeName).Type;
if (attachedPropertyOwnerType is null)
{
- throw new XamlParseException($"Cannot find '{attachedProperty.Xmlns}:{attachedProperty.TypeName}",node);
+ throw new XamlTransformException($"Cannot find '{attachedProperty.Xmlns}:{attachedProperty.TypeName}",node);
}
var attachedPropertyName = attachedProperty.Property + "Property";
@@ -112,7 +119,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
if (targetPropertyField is null)
{
- throw new XamlParseException($"Cannot find '{attachedProperty.Property}' on '{attachedPropertyOwnerType.GetFqn()}", node);
+ throw new XamlTransformException($"Cannot find '{attachedProperty.Property}' on '{attachedPropertyOwnerType.GetFqn()}", node);
}
var targetPropertyType = XamlIlAvaloniaPropertyHelper
@@ -121,7 +128,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
if (!XamlTransformHelpers.TryGetCorrectlyTypedValue(context,
new XamlAstTextNode(node, attachedProperty.Value, type: context.Configuration.WellKnownTypes.String),
targetPropertyType, out var typedValue))
- throw new XamlParseException(
+ throw new XamlTransformException(
$"Cannot convert '{attachedProperty.Value}' to '{targetPropertyType.GetFqn()}",
node);
@@ -156,12 +163,12 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
var parentTargetType = context.ParentNodes().OfType().FirstOrDefault();
if (parentTargetType is null)
- throw new XamlParseException($"Cannot find parent style for nested selector.", node);
+ throw new XamlTransformException($"Cannot find parent style for nested selector.", node);
result = new XamlIlNestingSelector(result, parentTargetType.TargetType.GetClrType());
break;
default:
- throw new XamlParseException($"Unsupported selector grammar '{i.GetType()}'.", node);
+ throw new XamlTransformException($"Unsupported selector grammar '{i.GetType()}'.", node);
}
}
@@ -180,11 +187,12 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
}
catch (Exception e)
{
- throw new XamlParseException("Unable to parse selector: " + e.Message, node);
+ throw new XamlSelectorsTransformException("Unable to parse selector: " + e.Message, node, e);
}
var selector = Create(parsed, (p, n)
- => TypeReferenceResolver.ResolveType(context, $"{p}:{n}", true, node, true));
+ => TypeReferenceResolver.ResolveType(context, $"{p}:{n}", true, node, true)
+ ?? new XamlAstClrTypeReference(node, XamlPseudoType.Unknown, false));
pn.Values[0] = selector;
var templateType = GetLastTemplateTypeFromSelector(selector);
@@ -402,7 +410,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
protected override void DoEmit(XamlEmitContext context, IXamlILEmitter codeGen)
{
if (!XamlIlAvaloniaPropertyHelper.Emit(context, codeGen, Property))
- throw new XamlLoadException(
+ throw new XamlX.XamlLoadException(
$"{Property.Name} of {(Property.Setter ?? Property.Getter).DeclaringType.GetFqn()} doesn't seem to be an AvaloniaProperty",
this);
context.Emit(Value, codeGen, context.Configuration.WellKnownTypes.Object);
@@ -486,7 +494,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
protected override void DoEmit(XamlEmitContext context, IXamlILEmitter codeGen)
{
if (_selectors.Count == 0)
- throw new XamlLoadException("Invalid selector count", this);
+ throw new XamlX.XamlLoadException("Invalid selector count", this);
if (_selectors.Count == 1)
{
_selectors[0].Emit(context, codeGen);
diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSetterTargetTypeMetadataTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSetterTargetTypeMetadataTransformer.cs
index ebc6c01ba8..07a10f0875 100644
--- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSetterTargetTypeMetadataTransformer.cs
+++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSetterTargetTypeMetadataTransformer.cs
@@ -25,7 +25,7 @@ internal class AvaloniaXamlIlSetterTargetTypeMetadataTransformer : IXamlAstTrans
if (type is null)
{
- throw new XamlParseException("Unable to resolve SetterTargetType type", typeDirective);
+ throw new XamlTransformException("Unable to resolve SetterTargetType type", typeDirective);
}
return new AvaloniaXamlIlTargetTypeMetadataNode(on, type, AvaloniaXamlIlTargetTypeMetadataNode.ScopeTypes.Style);
}
diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSetterTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSetterTransformer.cs
index 5a6fd8246d..8b5a893884 100644
--- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSetterTransformer.cs
+++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSetterTransformer.cs
@@ -1,3 +1,4 @@
+using System;
using System.Collections.Generic;
using System.Linq;
using XamlX;
@@ -9,6 +10,14 @@ using XamlX.TypeSystem;
namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
{
+ class XamlStyleTransformException : XamlTransformException
+ {
+ public XamlStyleTransformException(string message, IXamlLineInfo lineInfo, Exception innerException = null)
+ : base(message, lineInfo, innerException)
+ {
+ }
+ }
+
class AvaloniaXamlIlSetterTransformer : IXamlAstTransformer
{
public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node)
@@ -27,13 +36,13 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
if (styleParent != null)
{
targetType = styleParent.TargetType.GetClrType()
- ?? throw new XamlParseException("Can not find parent Style Selector or ControlTemplate TargetType. If setter is not part of the style, you can set x:SetterTargetType directive on its parent.", node);
+ ?? throw new XamlStyleTransformException("Can not find parent Style Selector or ControlTemplate TargetType. If setter is not part of the style, you can set x:SetterTargetType directive on its parent.", node);
lineInfo = on;
}
if (targetType == null)
{
- throw new XamlParseException("Could not determine target type of Setter", node);
+ throw new XamlStyleTransformException("Could not determine target type of Setter", node);
}
IXamlType propType = null;
@@ -43,7 +52,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
{
var propertyName = property.Values.OfType().FirstOrDefault()?.Text;
if (propertyName == null)
- throw new XamlParseException("Setter.Property must be a string", node);
+ throw new XamlStyleTransformException("Setter.Property must be a string", node);
var avaloniaPropertyNode = XamlIlAvaloniaPropertyHelper.CreateNode(context, propertyName,
new XamlAstClrTypeReference(lineInfo, targetType, false), property.Values[0]);
@@ -55,12 +64,12 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
var propertyPath = on.Children.OfType()
.FirstOrDefault(x => x.Property.GetClrProperty().Name == "PropertyPath");
if (propertyPath == null)
- throw new XamlX.XamlParseException("Setter without a property or property path is not valid", node);
+ throw new XamlStyleTransformException("Setter without a property or property path is not valid", node);
if (propertyPath.Values[0] is IXamlIlPropertyPathNode ppn
&& ppn.PropertyType != null)
propType = ppn.PropertyType;
else
- throw new XamlX.XamlParseException("Unable to get the property path property type", node);
+ throw new XamlStyleTransformException("Unable to get the property path property type", node);
}
var valueProperty = on.Children
@@ -69,7 +78,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
{
if (!XamlTransformHelpers.TryGetCorrectlyTypedValue(context, valueProperty.Values[0],
propType, out var converted))
- throw new XamlParseException(
+ throw new XamlStyleTransformException(
$"Unable to convert property value to {propType.GetFqn()}",
valueProperty.Values[0]);
diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlStyleValidatorTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlStyleValidatorTransformer.cs
new file mode 100644
index 0000000000..87528649e6
--- /dev/null
+++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlStyleValidatorTransformer.cs
@@ -0,0 +1,34 @@
+using System.Linq;
+using XamlX;
+using XamlX.Ast;
+using XamlX.Transform;
+
+namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers;
+
+internal class AvaloniaXamlIlStyleValidatorTransformer : IXamlAstTransformer
+{
+ // See https://github.com/AvaloniaUI/Avalonia/issues/7461
+ public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node)
+ {
+ if (!(node is XamlAstObjectNode on
+ && context.GetAvaloniaTypes().IStyle.IsAssignableFrom(on.Type.GetClrType())))
+ return node;
+
+ if (context.ParentNodes().FirstOrDefault() is XamlAstXamlPropertyValueNode propertyValueNode
+ && propertyValueNode.Property.GetClrProperty() is { } clrProperty
+ && clrProperty.Name == "MergedDictionaries"
+ && clrProperty.DeclaringType == context.GetAvaloniaTypes().ResourceDictionary)
+ {
+ var nodeName = on.Type.GetClrType().Name;
+ context.ReportDiagnostic(new XamlDiagnostic(
+ AvaloniaXamlDiagnosticCodes.StyleInMergedDictionaries,
+ XamlDiagnosticSeverity.Warning,
+ // Keep it single line, as MSBuild splits multiline warnings into two warnings.
+ $"Including {nodeName} as part of MergedDictionaries will ignore any nested styles." +
+ $"Instead, you can add {nodeName} to the Styles collection on the same control or application.",
+ node));
+ }
+
+ return node;
+ }
+}
diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlDocumentParseException.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlDocumentParseException.cs
deleted file mode 100644
index 0532287a67..0000000000
--- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlDocumentParseException.cs
+++ /dev/null
@@ -1,26 +0,0 @@
-using XamlX;
-using XamlX.Ast;
-
-namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions;
-
-internal class XamlDocumentParseException : XamlParseException
-{
- public string FilePath { get; }
-
- public XamlDocumentParseException(string path, XamlParseException parseException)
- : base(parseException.Message, parseException.LineNumber, parseException.LinePosition)
- {
- FilePath = path;
- }
-
- public XamlDocumentParseException(string path, string message, IXamlLineInfo lineInfo)
- : base(message, lineInfo.Line, lineInfo.Position)
- {
- FilePath = path;
- }
-
- public XamlDocumentParseException(IXamlDocumentResource document, string message, IXamlLineInfo lineInfo)
- : this(document.FileSource?.FilePath, message, lineInfo)
- {
- }
-}
diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlAvaloniaPropertyHelper.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlAvaloniaPropertyHelper.cs
index 264361e743..5ae8a8f182 100644
--- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlAvaloniaPropertyHelper.cs
+++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlAvaloniaPropertyHelper.cs
@@ -83,7 +83,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
var found = tref.Type.GetAllFields()
.FirstOrDefault(f => f.IsStatic && f.IsPublic && f.Name == propertyFieldName);
if (found == null)
- throw new XamlX.XamlParseException(
+ throw new XamlX.XamlTransformException(
$"Unable to find {propertyFieldName} field on type {tref.Type.GetFullName()}", lineInfo);
return new XamlIlAvaloniaPropertyFieldNode(context.GetAvaloniaTypes(), lineInfo, found);
}
@@ -110,7 +110,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
avaloniaPropertyType = avaloniaPropertyType.BaseType;
}
- throw new XamlX.XamlParseException(
+ throw new XamlX.XamlTransformException(
$"{field.Name}'s type {field.FieldType} doesn't inherit from AvaloniaProperty, make sure to use typed properties",
lineInfo);
diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlBindingPathHelper.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlBindingPathHelper.cs
index 37a028d7b1..965d6cb98a 100644
--- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlBindingPathHelper.cs
+++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlBindingPathHelper.cs
@@ -87,8 +87,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
return transformed;
}
- var lastElement =
- transformed.Elements[transformed.Elements.Count - 1];
+ var lastElement = transformed.Elements.LastOrDefault();
if (parentNode.Property?.Getter?.ReturnType == context.GetAvaloniaTypes().ICommand && lastElement is XamlIlClrMethodPathElementNode methodPathElement)
{
@@ -158,7 +157,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
{
break;
}
- throw new XamlX.XamlParseException($"Compiled bindings do not support stream bindings for objects of type {targetType.FullName}.", lineInfo);
+ throw new XamlX.XamlTransformException($"Compiled bindings do not support stream bindings for objects of type {targetType.FullName}.", lineInfo);
}
case BindingExpressionGrammar.PropertyNameNode propName:
{
@@ -182,7 +181,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
}
else
{
- throw new XamlX.XamlParseException($"Unable to resolve property or method of name '{propName.PropertyName}' on type '{targetType}'.", lineInfo);
+ throw new XamlX.XamlTransformException($"Unable to resolve property or method of name '{propName.PropertyName}' on type '{targetType}'.", lineInfo);
}
break;
}
@@ -207,7 +206,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
}
if (property is null)
{
- throw new XamlX.XamlParseException($"The type '${targetType}' does not have an indexer.", lineInfo);
+ throw new XamlX.XamlTransformException($"The type '${targetType}' does not have an indexer.", lineInfo);
}
IEnumerable parameters = property.IndexerParameters;
@@ -219,7 +218,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
var textNode = new XamlAstTextNode(lineInfo, indexer.Arguments[currentParamIndex], type: context.Configuration.WellKnownTypes.String);
if (!XamlTransformHelpers.TryGetCorrectlyTypedValue(context, textNode,
param, out var converted))
- throw new XamlX.XamlParseException(
+ throw new XamlX.XamlTransformException(
$"Unable to convert indexer parameter value of '{indexer.Arguments[currentParamIndex]}' to {param.GetFqn()}",
textNode);
@@ -267,7 +266,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
if (ancestorType is null)
{
- throw new XamlX.XamlParseException("Unable to resolve implicit ancestor type based on XAML tree.", lineInfo);
+ throw new XamlX.XamlTransformException("Unable to resolve implicit ancestor type based on XAML tree.", lineInfo);
}
nodes.Add(new FindAncestorPathElementNode(ancestorType, ancestor.Level));
@@ -294,7 +293,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
if (elementType is null)
{
- throw new XamlX.XamlParseException($"Unable to find element '{elementName.Name}' in the current namescope. Unable to use a compiled binding with a name binding if the name cannot be found at compile time.", lineInfo);
+ throw new XamlX.XamlTransformException($"Unable to find element '{elementName.Name}' in the current namescope. Unable to use a compiled binding with a name binding if the name cannot be found at compile time.", lineInfo);
}
nodes.Add(new ElementNamePathElementNode(elementName.Name, elementType));
break;
@@ -306,7 +305,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
if (castType is null)
{
- throw new XamlX.XamlParseException($"Unable to resolve cast to type {typeCastNode.Namespace}:{typeCastNode.TypeName} based on XAML tree.", lineInfo);
+ throw new XamlX.XamlTransformException($"Unable to resolve cast to type {typeCastNode.Namespace}:{typeCastNode.TypeName} based on XAML tree.", lineInfo);
}
nodes.Add(new TypeCastPathElementNode(castType));
@@ -785,7 +784,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
{
if (!int.TryParse(item, out var index))
{
- throw new XamlX.XamlParseException($"Unable to convert '{item}' to an integer.", lineInfo.Line, lineInfo.Position);
+ throw new XamlX.XamlTransformException($"Unable to convert '{item}' to an integer.", lineInfo);
}
_values.Add(index);
}
@@ -866,10 +865,10 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
Elements = elements;
}
- public IXamlType BindingResultType
- => _transformElements.Count > 0
- ? _transformElements[0].Type
- : Elements[Elements.Count - 1].Type;
+ public IXamlType BindingResultType =>
+ _transformElements.FirstOrDefault()?.Type
+ ?? Elements.LastOrDefault()?.Type
+ ?? XamlPseudoType.Unknown;
public IXamlAstTypeReference Type { get; }
diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github b/src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github
index aa2223dec1..b7ed273273 160000
--- a/src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github
+++ b/src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github
@@ -1 +1 @@
-Subproject commit aa2223dec1e7c70679fdb73f9d364363a0285adb
+Subproject commit b7ed273273949a5dd9f01e682ab97f61b43697ad
diff --git a/src/Markup/Avalonia.Markup.Xaml/RuntimeXamlLoaderConfiguration.cs b/src/Markup/Avalonia.Markup.Xaml/RuntimeXamlLoaderConfiguration.cs
index 1cd48f87d7..90e607ddcc 100644
--- a/src/Markup/Avalonia.Markup.Xaml/RuntimeXamlLoaderConfiguration.cs
+++ b/src/Markup/Avalonia.Markup.Xaml/RuntimeXamlLoaderConfiguration.cs
@@ -1,3 +1,4 @@
+using System;
using System.Reflection;
namespace Avalonia.Markup.Xaml;
@@ -20,4 +21,50 @@ public class RuntimeXamlLoaderConfiguration
/// Default is 'false'.
///
public bool DesignMode { get; set; } = false;
+
+ ///
+ /// XAML diagnostics handler.
+ ///
+ ///
+ /// Defines if any diagnostic severity should be overriden.
+ /// Note, severity cannot be set lower than minimal for specific diagnostic.
+ ///
+ public XamlDiagnosticFunc? DiagnosticHandler { get; set; }
+
+ ///
+ /// Delegate for property.
+ ///
+ public delegate RuntimeXamlDiagnosticSeverity XamlDiagnosticFunc(RuntimeXamlDiagnostic diagnostic);
+}
+
+public enum RuntimeXamlDiagnosticSeverity
+{
+ None = 0,
+
+ ///
+ /// Diagnostic is reported as a warning.
+ ///
+ Warning = 1,
+
+ ///
+ /// Diagnostic is reported as an error.
+ /// Compilation process is continued until the end of the parsing and transforming stage, throwing an aggregated exception of all errors.
+ ///
+ Error,
+
+ ///
+ /// Diagnostic is reported as an fatal error.
+ /// Compilation process is stopped right after this error.
+ ///
+ Fatal
+}
+
+public record RuntimeXamlDiagnostic(
+ string Id,
+ RuntimeXamlDiagnosticSeverity Severity,
+ string Title,
+ int? LineNumber,
+ int? LinePosition)
+{
+ public string? Document { get; init; }
}
diff --git a/src/tools/Avalonia.Designer.HostApp/Avalonia.Designer.HostApp.csproj b/src/tools/Avalonia.Designer.HostApp/Avalonia.Designer.HostApp.csproj
index c8608451e5..ead954d641 100644
--- a/src/tools/Avalonia.Designer.HostApp/Avalonia.Designer.HostApp.csproj
+++ b/src/tools/Avalonia.Designer.HostApp/Avalonia.Designer.HostApp.csproj
@@ -22,6 +22,7 @@
+
diff --git a/src/tools/Avalonia.Generators/Compiler/MiniCompiler.cs b/src/tools/Avalonia.Generators/Compiler/MiniCompiler.cs
index 71f34d173c..b9fbdb537d 100644
--- a/src/tools/Avalonia.Generators/Compiler/MiniCompiler.cs
+++ b/src/tools/Avalonia.Generators/Compiler/MiniCompiler.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using XamlX;
using XamlX.Compiler;
using XamlX.Emit;
using XamlX.Transform;
@@ -19,10 +20,13 @@ internal sealed class MiniCompiler : XamlCompiler