Skip to content

Commit

Permalink
feat: Improve error reporting with Roslyn-hosted generators
Browse files Browse the repository at this point in the history
  • Loading branch information
jeromelaban committed Jul 15, 2021
1 parent a04bed2 commit 8c3a0d7
Show file tree
Hide file tree
Showing 4 changed files with 151 additions and 3 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#nullable enable
extern alias __uno;
#nullable enable

using System;
using System.Collections.Generic;
Expand All @@ -15,6 +16,8 @@
using Uno.UI.SourceGenerators.Telemetry;
using Uno.UI.Xaml;
using System.Drawing;
using __uno::Uno.Xaml;
using Microsoft.CodeAnalysis.Text;

#if NETFRAMEWORK
using Microsoft.Build.Execution;
Expand Down Expand Up @@ -49,6 +52,23 @@ internal partial class XamlCodeGeneration
private readonly bool _outputSourceComments = true;
private readonly RoslynMetadataHelper _metadataHelper;

internal const string Title = "XAML Generation Failed";
internal const string MessageFormat = "{0}";
internal const string Description = "XAML Generation Failed";
internal const string Category = "XAML";

internal static DiagnosticDescriptor GenericXamlErrroRule = new DiagnosticDescriptor(
#pragma warning disable RS2008 // Enable analyzer release tracking
"UXAML0001",
#pragma warning restore RS2008 // Enable analyzer release tracking
Title,
MessageFormat,
Category,
DiagnosticSeverity.Error,
isEnabledByDefault: true,
description: Description
);

/// <summary>
/// If set, code generated from XAML will be annotated with the source method and line # in XamlFileGenerator, for easier debugging.
/// </summary>
Expand Down Expand Up @@ -316,7 +336,11 @@ public KeyValuePair<string, string>[] Generate()
{
TrackGenerationFailed(e, stopwatch.Elapsed);

#if NETFRAMEWORK
throw;
#else
return ProcessParsingException(e);
#endif
}
finally
{
Expand All @@ -325,6 +349,74 @@ public KeyValuePair<string, string>[] Generate()
}
}

private KeyValuePair<string, string>[] ProcessParsingException(Exception e)
{
IEnumerable<Exception> Flatten(Exception ex)
{
if (ex is AggregateException agg)
{
foreach (var inner in agg.InnerExceptions)
{
foreach (var inner2 in Flatten(inner))
{
yield return inner2;
}
}
}
else
{
if (ex.InnerException != null)
{
foreach (var inner2 in Flatten(ex.InnerException))
{
yield return inner2;
}
}

yield return ex;
}
}

foreach (var exception in Flatten(e))
{
var diagnostic = Diagnostic.Create(
GenericXamlErrroRule,
GetExceptionFileLocation(exception),
exception.Message);

_generatorContext.ReportDiagnostic(diagnostic);
}

return new KeyValuePair<string, string>[0];
}

private Location? GetExceptionFileLocation(Exception exception)
{
if (exception is XamlParsingException xamlParsingException)
{
var xamlFile = _generatorContext.AdditionalFiles.FirstOrDefault(f => f.Path == xamlParsingException.FilePath);

if (xamlFile != null
&& xamlFile.GetText() is { } xamlText
&& xamlParsingException.LineNumber.HasValue
&& xamlParsingException.LinePosition.HasValue)
{
var linePosition = new LinePosition(
Math.Max(0, xamlParsingException.LineNumber.Value - 1),
Math.Max(0, xamlParsingException.LinePosition.Value - 1)
);

return Location.Create(
xamlFile.Path,
xamlText.Lines.ElementAtOrDefault(xamlParsingException.LineNumber.Value-1).Span,
new LinePositionSpan(linePosition, linePosition)
);
}
}

return null;
}

private XamlGlobalStaticResourcesMap BuildAssemblyGlobalStaticResourcesMap(XamlFileDefinition[] files, XamlFileDefinition[] filesFull, string[] links)
{
var map = new XamlGlobalStaticResourcesMap();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2269,6 +2269,7 @@ implicitContentChild.Value is string implicitValue
}
catch (Exception e)
{
#if NETFRAMEWORK
throw new InvalidOperationException(
"An error occurred when processing {0} at line {1}:{2} ({3}) : {4}"
.InvariantCultureFormat(
Expand All @@ -2280,6 +2281,15 @@ implicitContentChild.Value is string implicitValue
)
, e
);
#else
throw new XamlParsingException(
$"An error was found in {topLevelControl.Type.Name}"
, e
, topLevelControl.LineNumber
, topLevelControl.LinePosition
, _fileDefinition.FilePath
);
#endif
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#nullable enable
extern alias __uno;
#nullable enable

using System;
using System.Collections.Generic;
Expand Down Expand Up @@ -70,9 +71,17 @@ public XamlFileDefinition[] ParseFiles(string[] xamlSourceFiles)

return null;
}
catch (__uno::Uno.Xaml.XamlParseException e)
{
throw new XamlParsingException(e.Message, null, e.LineNumber, e.LinePosition, file);
}
catch (XmlException e)
{
throw new XamlParsingException(e.Message, null, e.LineNumber, e.LinePosition, file);
}
catch (Exception e)
{
throw new InvalidOperationException($"Failed to parse file {file}", e);
throw new XamlParsingException($"Failed to parse file", e, 1, 1, file);
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#nullable enable

using System;
using System.Runtime.Serialization;

namespace Uno.UI.SourceGenerators.XamlGenerator
{
/// <summary>
/// Defines a XAML parsing exception to be raised from the generator
/// </summary>
[Serializable]
internal class XamlParsingException : Exception
{
public XamlParsingException()
{
}

public XamlParsingException(string message) : base(message)
{
}

public XamlParsingException(string message, Exception? innerException, int lineNumber, int linePosition, string filePath) : base(message, innerException)
{
LineNumber = lineNumber;
LinePosition = linePosition;
FilePath = filePath;
}

protected XamlParsingException(SerializationInfo info, StreamingContext context) : base(info, context)
{
}

public int? LineNumber { get; }
public int? LinePosition { get; }
public string? FilePath { get; }
}
}

0 comments on commit 8c3a0d7

Please sign in to comment.