Skip to content

Commit

Permalink
Improvements (#454)
Browse files Browse the repository at this point in the history
* BCL docs, crashfix

* Added base types to system array

* Added enum equality

* Test fix

* Update CompilingCodeTests.cs

* Update TypeCheckingTests.cs

* Update TypeCheckingTests.cs

* Fixed syntax errors not bubbling up in scripts

* Update ConstraintSolver_Rules.cs

* Update TypeCheckingTests.cs

* Added explicit impl detection

* Update MetadataMethodSymbol.cs

* Moved array into its own folder

* Formatting fixes

* One fix, one unfix

* Update ArrayTypeSymbol.cs

* Cleanup

* Simplification, correctness

* Update XmlDocumentationExtractor.cs
  • Loading branch information
LPeter1997 authored Aug 30, 2024
1 parent 74d8f59 commit e63ad2f
Show file tree
Hide file tree
Showing 45 changed files with 407 additions and 159 deletions.
4 changes: 2 additions & 2 deletions src/Draco.Compiler.Cli/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ private static void CompileCommand(FileInfo[] input, FileInfo output, DirectoryI
var compilation = Compilation.Create(
syntaxTrees: syntaxTrees,
metadataReferences: references
.Select(r => MetadataReference.FromPeStream(r.OpenRead()))
.Select(r => MetadataReference.FromFile(r.FullName))
.ToImmutableArray(),
rootModulePath: rootModule?.FullName,
outputPath: path,
Expand All @@ -133,7 +133,7 @@ private static void RunCommand(FileInfo[] input, DirectoryInfo? rootModule, File
var compilation = Compilation.Create(
syntaxTrees: syntaxTrees,
metadataReferences: references
.Select(r => MetadataReference.FromPeStream(r.OpenRead()))
.Select(r => MetadataReference.FromFile(r.FullName))
.ToImmutableArray(),
rootModulePath: rootModule?.FullName);
var execResult = Script.ExecuteAsProgram(compilation);
Expand Down
21 changes: 21 additions & 0 deletions src/Draco.Compiler.Tests/EndToEnd/CompilingCodeTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -799,4 +799,25 @@ public void GlobalInferredFromBlock()
Assert.Single(l);
Assert.Equal(1, l[0]);
}

[Fact]
public void EnumEqualityOperators()
{
var assembly = Compile("""
import System;
public func equate(a: StringComparison, b: StringComparison): bool = a == b;
public func inequate(a: StringComparison, b: StringComparison): bool = a != b;
""");

var eq1 = Invoke<bool>(assembly, "equate", StringComparison.Ordinal, StringComparison.Ordinal);
var eq2 = Invoke<bool>(assembly, "equate", StringComparison.Ordinal, StringComparison.OrdinalIgnoreCase);
var neq1 = Invoke<bool>(assembly, "inequate", StringComparison.Ordinal, StringComparison.Ordinal);
var neq2 = Invoke<bool>(assembly, "inequate", StringComparison.Ordinal, StringComparison.OrdinalIgnoreCase);

Assert.True(eq1);
Assert.False(eq2);
Assert.False(neq1);
Assert.True(neq2);
}
}
19 changes: 19 additions & 0 deletions src/Draco.Compiler.Tests/Scripting/ScriptTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,23 @@ public void BasicAssignmentAndAddition()
Assert.True(result.Success);
Assert.Equal(7, result.Value);
}

[Fact]
public void SyntaxErrorInScript()
{
// Arrange
var script = Script.Create<int>("""
var x = ;
""",
metadataReferences: Basic.Reference.Assemblies.Net80.ReferenceInfos.All
.Select(r => MetadataReference.FromPeStream(new MemoryStream(r.ImageBytes)))
.ToImmutableArray());

// Act
var result = script.Execute();

// Assert
Assert.False(result.Success);
Assert.NotEmpty(result.Diagnostics);
}
}
18 changes: 9 additions & 9 deletions src/Draco.Compiler.Tests/Semantics/DocumentationCommentsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ public void TypeDocumentationFromMetadata()
var testRef = CompileCSharpToMetadataRef($$"""
/// {{docs}}
public class TestClass { }
""", xmlStream: xmlStream).WithDocumentation(xmlStream);
""", xmlStream: xmlStream);

var call = tree.FindInChildren<NameExpressionSyntax>(0);

Expand Down Expand Up @@ -258,7 +258,7 @@ public class TestClass
/// {{docs}}
public class NestedTestClass { }
}
""", xmlStream: xmlStream).WithDocumentation(xmlStream);
""", xmlStream: xmlStream);

var call = tree.FindInChildren<NameExpressionSyntax>(0);

Expand Down Expand Up @@ -304,7 +304,7 @@ public static class TestClass
// Just so i can use it in draco
public static int foo = 0;
}
""", xmlStream: xmlStream).WithDocumentation(xmlStream);
""", xmlStream: xmlStream);

var @class = tree.FindInChildren<MemberExpressionSyntax>(0).Accessed;

Expand Down Expand Up @@ -348,7 +348,7 @@ public class TestClass
/// {{docs}}
public void TestMethod(int arg1, string arg2) { }
}
""", xmlStream: xmlStream).WithDocumentation(xmlStream);
""", xmlStream: xmlStream);

var call = tree.FindInChildren<NameExpressionSyntax>(0);

Expand Down Expand Up @@ -395,7 +395,7 @@ public class TestClass
/// {{docs}}
public void TestMethod() { }
}
""", xmlStream: xmlStream).WithDocumentation(xmlStream);
""", xmlStream: xmlStream);

var call = tree.FindInChildren<NameExpressionSyntax>(0);

Expand Down Expand Up @@ -440,7 +440,7 @@ public class TestClass
/// {{docs}}
public int TestField = 5;
}
""", xmlStream: xmlStream).WithDocumentation(xmlStream);
""", xmlStream: xmlStream);

var call = tree.FindInChildren<NameExpressionSyntax>(0);

Expand Down Expand Up @@ -485,7 +485,7 @@ public class TestClass
/// {{docs}}
public int TestProperty { get; }
}
""", xmlStream: xmlStream).WithDocumentation(xmlStream);
""", xmlStream: xmlStream);

var call = tree.FindInChildren<NameExpressionSyntax>(0);

Expand Down Expand Up @@ -534,7 +534,7 @@ public class TestClass<T>
/// {{methodDocs}}
public void TestMethod<U>(T arg1, T arg2, U arg3) { }
}
""", xmlStream: xmlStream).WithDocumentation(xmlStream);
""", xmlStream: xmlStream);

var call = tree.FindInChildren<CallExpressionSyntax>(0);

Expand Down Expand Up @@ -594,7 +594,7 @@ public class TestClass
{{CreateXmlDocComment(originalDocs)}}
public int TestMethod<T>(int arg1, int arg2) => arg1 + arg2;
}
""", xmlStream: xmlStream).WithDocumentation(xmlStream);
""", xmlStream: xmlStream);

var call = tree.FindInChildren<NameExpressionSyntax>(0);

Expand Down
102 changes: 102 additions & 0 deletions src/Draco.Compiler.Tests/Semantics/TypeCheckingTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2287,4 +2287,106 @@ public void InferredArrayElementTypeFromUsage()
var intArray = compilation.WellKnownTypes.InstantiateArray(compilation.WellKnownTypes.SystemInt32);
Assert.True(SymbolEqualityComparer.Default.Equals(intArray, aSym.Type));
}

[Fact]
public void ArrayIndexResultHasTheRightType()
{
// func main() {
// val a = Array<int32>(3);
// val x = a[0];
// }

var main = SyntaxTree.Create(CompilationUnit(FunctionDeclaration(
"main",
ParameterList(),
null,
BlockFunctionBody(
DeclarationStatement(ImmutableVariableDeclaration(
"a",
null,
CallExpression(GenericExpression(NameExpression("Array"), NameType("int32")), LiteralExpression(3)))),
DeclarationStatement(ImmutableVariableDeclaration(
"x",
null,
IndexExpression(NameExpression("a"), LiteralExpression(0))))))));

// Act
var compilation = CreateCompilation(main);
var semanticModel = compilation.GetSemanticModel(main);

var diags = semanticModel.Diagnostics;
var xDecl = main.FindInChildren<VariableDeclarationSyntax>(1);
var xSym = GetInternalSymbol<LocalSymbol>(semanticModel.GetDeclaredSymbol(xDecl));

// Assert
Assert.Empty(diags);
Assert.False(xSym.IsError);
Assert.Equal(compilation.WellKnownTypes.SystemInt32, xSym.Type);
}

[Fact]
public void ArrayIteratorVariableCorrectlyInferredInForLoop()
{
// func main() {
// val a = Array<int32>(3);
// for (x in a) { }
// }

var main = SyntaxTree.Create(CompilationUnit(FunctionDeclaration(
"main",
ParameterList(),
null,
BlockFunctionBody(
DeclarationStatement(ImmutableVariableDeclaration(
"a",
null,
CallExpression(GenericExpression(NameExpression("Array"), NameType("int32")), LiteralExpression(3)))),
ExpressionStatement(ForExpression(
"x",
NameExpression("a"),
BlockExpression()))))));

// Act
var compilation = CreateCompilation(main);
var semanticModel = compilation.GetSemanticModel(main);

var diags = semanticModel.Diagnostics;
var xDecl = main.FindInChildren<ForExpressionSyntax>().Iterator;
var xSym = GetInternalSymbol<LocalSymbol>(semanticModel.GetDeclaredSymbol(xDecl));

// Assert
Assert.Empty(diags);
Assert.False(xSym.IsError);
Assert.Equal(compilation.WellKnownTypes.SystemInt32, xSym.Type);
}

[Fact]
public void ErrorExpressionsDoNotCascadeErrorsInOverloading()
{
// import System.Numerics;
//
// func foo(): Vector3 = Vector3() + Vector3();

var main = SyntaxTree.Create(CompilationUnit(
ImportDeclaration("System", "Numerics"),
FunctionDeclaration(
"foo",
ParameterList(),
NameType("Vector3"),
InlineFunctionBody(BinaryExpression(
CallExpression(NameExpression("Vector3")),
Plus,
CallExpression(NameExpression("Vector3")))))));

// Act
var compilation = CreateCompilation(main);
var semanticModel = compilation.GetSemanticModel(main);
var diags = semanticModel.Diagnostics;

// Assert
Assert.Equal(2, diags.Length);
AssertDiagnostic(diags, TypeCheckingErrors.NoMatchingOverload);

Assert.True(diags.All(d => !d.ToString().Contains("operator", StringComparison.OrdinalIgnoreCase)));
}
}
2 changes: 1 addition & 1 deletion src/Draco.Compiler.Tests/TestUtilities.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ internal static class TestUtilities
public static MetadataReference CompileCSharpToMetadataRef(string code, string assemblyName = DefaultAssemblyName, IEnumerable<Stream>? aditionalReferences = null, Stream? xmlStream = null)
{
var stream = CompileCSharpToStream(code, assemblyName, aditionalReferences, xmlStream);
return MetadataReference.FromPeStream(stream);
return MetadataReference.FromPeStream(stream, xmlStream);
}

public static Stream CompileCSharpToStream(string code, string assemblyName = DefaultAssemblyName, IEnumerable<Stream>? aditionalReferences = null, Stream? xmlStream = null)
Expand Down
48 changes: 36 additions & 12 deletions src/Draco.Compiler/Api/MetadataReference.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,28 @@ public abstract class MetadataReference
/// </summary>
public abstract XmlDocument? Documentation { get; }

/// <summary>
/// Creates a metadata reference reading up the given file.
/// </summary>
/// <param name="path">The path to the file to read.</param>
/// <returns>The <see cref="MetadataReference"/> created from the file at <paramref name="path"/>.</returns>
public static MetadataReference FromFile(string path)
{
// NOTE: We could add a bool to turn off fetching the docs
var peStream = File.OpenRead(path);
// We assume the docs are under the same path, same name but with .xml extension
var docPath = Path.ChangeExtension(path, ".xml");
var docStream = File.Exists(docPath) ? File.OpenRead(docPath) : null;
return FromPeStream(peStream, docStream);
}

/// <summary>
/// Creates a metadata reference from the given assembly.
/// </summary>
/// <param name="assembly">The assembly to create a metadata reader from.</param>
/// <param name="documentation">The XML documentation for the assembly.</param>
/// <returns>The <see cref="MetadataReference"/> created from <paramref name="assembly"/>.</returns>
public static MetadataReference FromAssembly(Assembly assembly)
public static MetadataReference FromAssembly(Assembly assembly, XmlDocument? documentation = null)
{
unsafe
{
Expand All @@ -37,37 +53,45 @@ public static MetadataReference FromAssembly(Assembly assembly)
}

var reader = new MetadataReader(blob, length);
return new MetadataReaderReference(reader);
return new MetadataReaderReference(reader, documentation);
}
}

/// <summary>
/// Creates a metadata reference from the given PE stream.
/// </summary>
/// <param name="peStream">The PE stream to create the metadata reference from.</param>
/// <param name="documentation">The XML documentation for the assembly.</param>
/// <returns>The <see cref="MetadataReference"/> reading up from <paramref name="peStream"/>.</returns>
public static MetadataReference FromPeStream(Stream peStream)
public static MetadataReference FromPeStream(Stream peStream, XmlDocument? documentation = null)
{
var peReader = new PEReader(peStream);
var metadataReader = peReader.GetMetadataReader();
return new MetadataReaderReference(metadataReader);
return new MetadataReaderReference(metadataReader, documentation);
}

/// <summary>
/// Adds xml documentation to this metadata reference.
/// Creates a metadata reference from the given PE stream.
/// </summary>
/// <param name="xmlStream">The stream with the xml documentation.</param>
/// <returns>New metadata reference containing xml documentation.</returns>
public MetadataReference WithDocumentation(Stream xmlStream)
/// <param name="peStream">The PE stream to create the metadata reference from.</param>
/// <param name="xmlDocStream">The stream to read up XML documentation from for the assembly.</param>
/// <returns>The <see cref="MetadataReference"/> reading up from <paramref name="peStream"/>.</returns>
public static MetadataReference FromPeStream(Stream peStream, Stream? xmlDocStream)
{
var doc = new XmlDocument();
doc.Load(xmlStream);
return new MetadataReaderReference(this.MetadataReader, doc);
var peReader = new PEReader(peStream);
var metadataReader = peReader.GetMetadataReader();
var docXml = null as XmlDocument;
if (xmlDocStream is not null)
{
docXml = new XmlDocument();
docXml.Load(xmlDocStream);
}
return new MetadataReaderReference(metadataReader, docXml);
}

private sealed class MetadataReaderReference(
MetadataReader metadataReader,
XmlDocument? documentation = null) : MetadataReference
XmlDocument? documentation) : MetadataReference
{
public override MetadataReader MetadataReader { get; } = metadataReader;
public override XmlDocument? Documentation { get; } = documentation;
Expand Down
2 changes: 1 addition & 1 deletion src/Draco.Compiler/Api/Syntax/SyntaxTree.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ internal static SyntaxTree ParseScript(ISourceReader sourceReader)
// Parse a repl entry
var node = parser.ParseScriptEntry();
// Make it into a tree
return Create(node);
return Create(node, syntaxDiagnostics: syntaxDiagnostics);
}

/// <summary>
Expand Down
2 changes: 1 addition & 1 deletion src/Draco.Compiler/Internal/Binding/BinderFacts.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
using System.Text;
using Draco.Compiler.Api.Syntax;
using Draco.Compiler.Internal.Symbols;
using Draco.Compiler.Internal.Symbols.Synthetized;
using Draco.Compiler.Internal.Symbols.Synthetized.Array;

namespace Draco.Compiler.Internal.Binding;

Expand Down
Loading

0 comments on commit e63ad2f

Please sign in to comment.