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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Loom.CLI/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,6 @@
var result = compilationUnit.Compile();
var debugInfo = result.Files
.Where(f => !f.SourceFile.IsDeclaration)
.Select(f => f.GetDebugInfo(rebuilt: false, debugDiagnostics: false));
.Select(f => f.GetDebugInfo(rebuilt: false, debugDiagnostics: loomConfig.Debug));

Console.WriteLine(string.Join(Environment.NewLine, debugInfo));
5 changes: 4 additions & 1 deletion Loom.Config/LoomConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@

[TomlPropertyName("no_emit")]
public bool NoEmit { get; set; }


[TomlPropertyName("debug")]
public bool Debug { get; set; }

Check notice on line 14 in Loom.Config/LoomConfig.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Property can be made init-only (non-private accessibility)

Property can be made init-only

[TomlPropertyName("project_type")]
[TomlConverter(typeof(ProjectTypeConverter))]
public ProjectType ProjectType { get; init; }
Expand Down
11 changes: 10 additions & 1 deletion Loom.Core/Diagnostics/Diagnostic.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
private int StartCharacter => Span.Start.Character;
private int EndCharacter => Span.End.Character;
private int LineDigits => EndLine.ToString().Length;
private string[]? _sourceLines;

Check notice on line 15 in Loom.Core/Diagnostics/Diagnostic.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Replace with 'field' keyword

Replace with 'field' keyword
private string[] SourceLines => _sourceLines ??= Span.File.SourceText.Replace(Environment.NewLine, "\n").Split('\n');
private string GutterIndent => new(' ', LineDigits);
private string Gutter => $"{Colors.Dim}{GutterIndent} │{Colors.Reset}";
Expand Down Expand Up @@ -56,7 +56,16 @@
: $"expected operand of type '{suggestion.OperandType}', not '{operand}'";
}

public override string ToString()
public override string ToString() =>
Severity == DiagnosticSeverity.Debug
? FormatCompact()
: FormatFrame();

private string FormatCompact() =>
$"{_severityStyle.PrimaryColor}{Colors.Bold}{_severityStyle.Label}{Colors.Reset} "
+ $"{Colors.Dim}[{Span}]{Colors.Reset} {Colors.Gray}{Message}{Colors.Reset}";

private string FormatFrame()
{
var lines = new List<string> { FormatHeader(), FormatLocation(), Gutter };
AppendSource(lines);
Expand Down
31 changes: 19 additions & 12 deletions Loom.Core/Resolving/Resolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@ public sealed class Resolver(ParserResult parserResult, CompilationUnit compilat
[MemberNotNull(nameof(_semanticModel))]
public SemanticModel Resolve()
{
_semanticModel = new SemanticModel(parserResult.Tree, _diagnostics, _allDeclarations, _allReferences);
_semanticModel = new SemanticModel(parserResult.Tree, _diagnostics, _allDeclarations, _allReferences)
{
EmitDebugDiagnostics = compilationUnit.Config.Debug
};
PushScope();
DeclareIntrinsicSymbols();
DeclareGlobalSymbols();
Expand Down Expand Up @@ -699,24 +702,28 @@ private void DeclareSymbol(Symbol symbol)
);
}

if (TypeChecker.EmitDebugDiagnostics)
_diagnostics.Debug(symbol.Declaration, $"Declared symbol: {symbol}");

if (IsDeclarationFile())
{
symbol.IsGlobal = true;
if (TypeChecker.EmitDebugDiagnostics)
_diagnostics.Debug(symbol.Declaration, $"{symbol} is global");
}

if (_context == ResolverContext.Ambient)
symbol.IsAmbient = true;

if (!parserResult.Tree.File.IsIntrinsic) return;
if (parserResult.Tree.File.IsIntrinsic)
symbol.IsIntrinsic = true;

if (_semanticModel.EmitDebugDiagnostics)
_diagnostics.Debug(symbol.Declaration, DescribeDeclaration(symbol));
}

private static string DescribeDeclaration(Symbol symbol)
{
var flags = new List<string>();
if (symbol.IsGlobal) flags.Add("global");
if (symbol.IsAmbient) flags.Add("ambient");
if (symbol.IsIntrinsic) flags.Add("intrinsic");

symbol.IsIntrinsic = true;
if (TypeChecker.EmitDebugDiagnostics)
_diagnostics.Debug(symbol.Declaration, $"{symbol} is intrinsic");
var suffix = flags.Count > 0 ? $" [{string.Join(", ", flags)}]" : "";
return $"Declared '{symbol.Name}' ({symbol.Kind}){suffix}";
}

private void AddToLookup(Symbol symbol)
Expand Down
1 change: 1 addition & 0 deletions Loom.Core/Resolving/SemanticModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
: DiagnosedResult(Diagnostics)
{
public bool DisableRuntimeLibraryImport { get; set; }
public bool EmitDebugDiagnostics { get; set; }

Check notice on line 15 in Loom.Core/Resolving/SemanticModel.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Property can be made init-only (non-private accessibility)

Property can be made init-only
public bool MustImportRuntimeLibrary =>
!DisableRuntimeLibraryImport
&& !Tree.File.IsIntrinsic
Expand Down
4 changes: 1 addition & 3 deletions Loom.Core/TypeChecking/TypeChecker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@ namespace Loom.Core.TypeChecking;
public sealed partial class TypeChecker
: Visitor<Type>
{
public static bool EmitDebugDiagnostics { get; set; }

private readonly DiagnosticBag _diagnostics = new();
private readonly Dictionary<Node, FlowState> _exitStates = [];
private readonly Stack<List<FlowState>> _loopExitScopes = [];
Expand Down Expand Up @@ -1184,7 +1182,7 @@ private T BindType<T>(Node node, T type)
where T : Type
{
_semanticModel.TypeSolver.SetType(node, type);
if (!EmitDebugDiagnostics || node is Tree or ExpressionStatement)
if (!_semanticModel.EmitDebugDiagnostics || node is Tree or ExpressionStatement)
return type;

var simplified = TypeSimplifier.Simplify(type);
Expand Down
23 changes: 23 additions & 0 deletions Loom.Testing/ConfigReaderTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using Loom.Config;
using Tomlyn;

namespace Loom.Testing;

public class ConfigReaderTest
{
[Fact]
public void Debug_DefaultsToFalse()
{
var config = TomlSerializer.Deserialize<LoomConfig>("project_type = \"game\"");
Assert.NotNull(config);
Assert.False(config.Debug);
}

[Fact]
public void Debug_ParsesTrue()
{
var config = TomlSerializer.Deserialize<LoomConfig>("project_type = \"game\"\ndebug = true");
Assert.NotNull(config);
Assert.True(config.Debug);
}
}
19 changes: 19 additions & 0 deletions Loom.Testing/DiagnosticMessageTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
[Collection("Assembly")]
public class DiagnosticMessageTest
{
private static readonly SourceFile _testFile = new("test.loom", $"let x: number = 5;\nlet y = x + 10;\nprint(y);");

Check notice on line 9 in Loom.Testing/DiagnosticMessageTest.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Redundant string interpolation

Redundant string interpolation

[Fact]
public void ToString_ErrorDiagnostic_FormatsCorrectly()
Expand Down Expand Up @@ -355,4 +355,23 @@
Assert.DoesNotContain("\n", result.Replace(Environment.NewLine, ""));
Assert.DoesNotContain("\r", result.Replace(Environment.NewLine, ""));
}

[Fact]
public void ToString_DebugDiagnostic_RendersCompactSingleLine()
{
var start = new Location(_testFile, 4);
var end = new Location(_testFile, 9);
var span = new LocationSpan(start, end);
var diagnostic = new Diagnostic(span, DiagnosticSeverity.Debug, null, "Declared 'x' (Variable)", null);
var result = diagnostic.ToString();

Assert.Equal(
$"{Colors.Magenta}{Colors.Bold}debug{Colors.Reset} {Colors.Dim}[{span}]{Colors.Reset} {Colors.Gray}Declared 'x' (Variable){Colors.Reset}",
result
);

Assert.DoesNotContain(Environment.NewLine, result);
Assert.DoesNotContain("╭─", result);
Assert.DoesNotContain("│", result);
}
}
43 changes: 43 additions & 0 deletions Loom.Testing/ResolverTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1476,4 +1476,47 @@ public void Allows_SelfAsDeclarationName()
Assert.Null(model.Diagnostics.Find(d => d.Code == InternalCodes.ReservedLuauKeyword));
}
#endregion ReservedLuauKeywords

#region DebugDiagnostics
[Fact]
public void Debug_False_ProducesNoDebugDiagnostics()
{
var model = Utility.GetSemanticModel("let x = 1;", debug: false);
Assert.Null(model.Diagnostics.Find(d => d.Severity == DiagnosticSeverity.Debug));
}

[Fact]
public void Debug_True_ProducesConsolidatedDeclarationDiagnostic()
{
var model = Utility.GetSemanticModel("let x = 1;", debug: true);
var diag = model.Diagnostics.Find(d => d.Severity == DiagnosticSeverity.Debug && d.Message.Contains("'x'"));

Assert.NotNull(diag);
Assert.Equal("Declared 'x' (Variable)", diag.Message);
}

[Fact]
public void Debug_True_AppliesGlobalFlag_InDeclarationFile()
{
var model = Utility.GetSemanticModel("interface Foo;", isDeclaration: true, debug: true);
var diag = model.Diagnostics.Find(d => d.Severity == DiagnosticSeverity.Debug && d.Message.Contains("'Foo'") && d.Message.Contains("Interface"));

Assert.NotNull(diag);
Assert.Equal("Declared 'Foo' (Interface) [global]", diag.Message);
}

[Fact]
public void Debug_True_SetsEmitDebugDiagnosticsOnSemanticModel()
{
var model = Utility.GetSemanticModel("let x = 1;", debug: true);
Assert.True(model.EmitDebugDiagnostics);
}

[Fact]
public void Debug_False_ClearsEmitDebugDiagnosticsOnSemanticModel()
{
var model = Utility.GetSemanticModel("let x = 1;", debug: false);
Assert.False(model.EmitDebugDiagnostics);
}
#endregion DebugDiagnostics
}
4 changes: 2 additions & 2 deletions Loom.Testing/Utility.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
private static LuauGeneratorResult Generate(string source, bool typeCheck = false, bool disableRuntimeLib = true)
{
var result = FlowAnalyze(source, disableRuntimeLib);
if (typeCheck)

Check notice on line 33 in Loom.Testing/Utility.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Invert 'if' statement to reduce nesting

Invert 'if' statement to reduce nesting
{
var typeChecker = new TypeChecker(result.SemanticModel, result.Analyzer);
typeChecker.Check();
Expand All @@ -43,13 +43,13 @@
public static ParserResult Parse(string source) => new Parser(Tokenize(source)).Parse();
public static DiagnosticBag GetParserDiagnostics(string source) => Parse(source).Diagnostics;

public static Core.Resolving.SemanticModel GetSemanticModel(string source, bool isDeclaration = false, bool disableRuntimeLib = true)
public static Core.Resolving.SemanticModel GetSemanticModel(string source, bool isDeclaration = false, bool disableRuntimeLib = true, bool debug = false)
{
var parserResult = Parse(source);
if (isDeclaration)
parserResult.Tree.File.IsDeclaration = true;

var compilationUnit = new CompilationUnit(new LoomConfig());
var compilationUnit = new CompilationUnit(new LoomConfig { Debug = debug });
var semanticModel = new Resolver(parserResult, compilationUnit).Resolve();
semanticModel.DisableRuntimeLibraryImport = disableRuntimeLib;

Expand Down
Loading