Skip to content

Commit

Permalink
Add some comments around generation (more to come), add keyword escap…
Browse files Browse the repository at this point in the history
…ing (#10)
  • Loading branch information
glennawatson authored Jun 13, 2019
1 parent 388e2c7 commit f668749
Show file tree
Hide file tree
Showing 5 changed files with 2,539 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@

namespace Pharmacist.Core.Generation.Generators
{
/// <summary>
/// Generates common code generation between both static and instance based observables for events.
/// </summary>
internal abstract class EventGeneratorBase : IEventGenerator
{
/// <summary>
Expand Down Expand Up @@ -142,6 +145,8 @@ private static (ArrowExpressionClauseSyntax, TypeSyntax) GenerateFromEventExpres

private static ArrowExpressionClauseSyntax GenerateUnitFromEventExpression(IEvent eventDetails, string dataObjectName)
{
// This produces "=> global::System.Reactive.Linq.Observable.FromEvent(x => dataObjectName.EventName += x, x => dataObjectName.EventName -= x)".
// It uses the observable Unit form of the method.
var eventName = eventDetails.Name;
return SyntaxFactory.ArrowExpressionClause(
SyntaxFactory.InvocationExpression(
Expand All @@ -162,6 +167,7 @@ private static ArrowExpressionClauseSyntax GenerateUnitFromEventExpression(IEven

private static (ArrowExpressionClauseSyntax, TypeSyntax) GenerateFromEventPatternExpressionClauseAndType(IEvent eventDetails, string dataObjectName, IMethod invokeMethod)
{
// This produces "=> global::System.Reactive.Linq.Observable.FromEventPattern<TEventType<TEventArgs>, TEventArgs>(x => dataObject.EventName += x; x => dataObject.EventName -= x)".
var param = invokeMethod.Parameters[1];

var eventArgsName = param.Type.GenerateFullGenericName();
Expand Down Expand Up @@ -204,6 +210,7 @@ private static (ArrowExpressionClauseSyntax, TypeSyntax) GenerateFromEventPatter

private static ArgumentSyntax GenerateArgumentEventAccessor(SyntaxKind accessor, string eventName, string dataObjectName)
{
// This produces "x => dataObject.EventName += x" and also "x => dataObject.EventName -= x" depending on the accessor passed in.
return SyntaxFactory.Argument(
SyntaxFactory.SimpleLambdaExpression(
SyntaxFactory.Parameter(SyntaxFactory.Identifier("x")),
Expand Down
37 changes: 17 additions & 20 deletions src/Pharmacist.Core/Generation/ReflectionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,25 +25,6 @@ internal static class ReflectionExtensions
private static readonly ConcurrentDictionary<ICompilation, ImmutableList<ITypeDefinition>> _publicNonGenericTypeMapping = new ConcurrentDictionary<ICompilation, ImmutableList<ITypeDefinition>>();
private static readonly ConcurrentDictionary<ICompilation, ImmutableList<ITypeDefinition>> _publicEventsTypeMapping = new ConcurrentDictionary<ICompilation, ImmutableList<ITypeDefinition>>();

private static readonly ConcurrentDictionary<string, string> _fullToBuiltInTypes = new ConcurrentDictionary<string, string>
{
["System.Boolean"] = "bool",
["System.Byte"] = "byte",
["System.SByte"] = "sbyte",
["System.Char"] = "char",
["System.Decimal"] = "decimal",
["System.Double"] = "double",
["System.Single"] = "float",
["System.Int32"] = "int",
["System.UInt32"] = "uint",
["System.Int64"] = "long",
["System.UInt64"] = "ulong",
["System.Object"] = "object",
["System.Int16"] = "short",
["System.UInt16"] = "ushort",
["System.String"] = "string",
};

/// <summary>
/// Get all type definitions where they have public events, aren't generic (no type parameters == 0), and they are public.
/// </summary>
Expand Down Expand Up @@ -98,6 +79,11 @@ public static IImmutableList<ITypeDefinition> GetPublicNonGenericTypeDefinitions
.ToImmutableList());
}

/// <summary>
/// Gets the type that the event.
/// </summary>
/// <param name="eventDetails">The details about the event.</param>
/// <returns>The type of the event.</returns>
public static IType GetEventType(this IEvent eventDetails)
{
ICompilation compilation = eventDetails.Compilation;
Expand All @@ -120,8 +106,19 @@ public static IType GetEventType(this IEvent eventDetails)
return type;
}

/// <summary>
/// If the type if UnknownType, it will search all the dependencies for the specified type.
/// If the type is not unknown type it will just return the type directly.
/// This gets around the problem that types are only known for the assembly they are included in.
/// </summary>
/// <param name="type">The type to check.</param>
/// <param name="compilation">The compilation which contains all the assemblies and dependencies.</param>
/// <returns>The concrete type if one can be found.</returns>
public static IType GetRealType(this IType type, ICompilation compilation)
{
// If the type is UnknownType, check other assemblies we have as dependencies first,
// since UnknownType is only if it's unknown in the current assembly only.
// This scenario is fairly common with types in the netstandard libraries, eg System.EventHandler.
if (type is UnknownType || type.Kind == TypeKind.Unknown)
{
if (type.TypeParameterCount == 0)
Expand Down Expand Up @@ -171,7 +168,7 @@ private static IImmutableList<ITypeDefinition> GetPublicTypeDefinitionsWithEvent

private static (bool isInternalType, string typeName) GetBuiltInType(string typeName)
{
if (_fullToBuiltInTypes.TryGetValue(typeName, out var builtInName))
if (TypesMetadata.FullToBuiltInTypes.TryGetValue(typeName, out var builtInName))
{
return (true, builtInName);
}
Expand Down
26 changes: 7 additions & 19 deletions src/Pharmacist.Core/Generation/RoslynGeneratorExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,31 +21,14 @@ internal static class RoslynGeneratorExtensions
/// </summary>
/// <param name="parameter">The parameter to generate the argument list for.</param>
/// <returns>The argument list.</returns>
public static ArgumentListSyntax GenerateArgumentList(this IParameter parameter) => SyntaxFactory.ArgumentList(SyntaxFactory.SingletonSeparatedList(SyntaxFactory.Argument(SyntaxFactory.IdentifierName(parameter.Name))));

/// <summary>
/// Generates a type argument list for a single type.
/// </summary>
/// <param name="type">The type to generate the type argument list for.</param>
/// <returns>The type argument list.</returns>
public static TypeArgumentListSyntax GenerateTypeArgumentList(this IType type) => SyntaxFactory.TypeArgumentList(SyntaxFactory.SingletonSeparatedList<TypeSyntax>(SyntaxFactory.IdentifierName(type.GenerateFullGenericName())));
public static ArgumentListSyntax GenerateArgumentList(this IParameter parameter) => SyntaxFactory.ArgumentList(SyntaxFactory.SingletonSeparatedList(SyntaxFactory.Argument(SyntaxFactory.IdentifierName(parameter.Name.GetKeywordSafeName()))));

/// <summary>
/// Generates a argument list for a tuple parameter.
/// </summary>
/// <param name="parameters">The parameters to generate the argument list for.</param>
/// <returns>The argument list.</returns>
public static ArgumentListSyntax GenerateTupleArgumentList(this IEnumerable<IParameter> parameters) => SyntaxFactory.ArgumentList(SyntaxFactory.SingletonSeparatedList(SyntaxFactory.Argument(SyntaxFactory.TupleExpression(SyntaxFactory.SeparatedList(parameters.Select(x => SyntaxFactory.Argument(SyntaxFactory.IdentifierName(x.Name))))))));

/// <summary>
/// Generates a type argument list for a tuple parameter.
/// </summary>
/// <param name="types">The types to generate the argument list for.</param>
/// <returns>The argument list.</returns>
public static TypeArgumentListSyntax GenerateTupleTypeArgumentList(this IEnumerable<IType> types)
{
return SyntaxFactory.TypeArgumentList(SyntaxFactory.SingletonSeparatedList(types.GenerateTupleType()));
}
public static ArgumentListSyntax GenerateTupleArgumentList(this IEnumerable<IParameter> parameters) => SyntaxFactory.ArgumentList(SyntaxFactory.SingletonSeparatedList(SyntaxFactory.Argument(SyntaxFactory.TupleExpression(SyntaxFactory.SeparatedList(parameters.Select(x => SyntaxFactory.Argument(SyntaxFactory.IdentifierName(x.Name.GetKeywordSafeName()))))))));

public static TypeSyntax GenerateTupleType(this IEnumerable<IType> types)
{
Expand Down Expand Up @@ -156,5 +139,10 @@ private static AttributeListSyntax GenerateObsoleteAttributeList(IEntity eventDe

return SyntaxFactory.AttributeList(SyntaxFactory.SingletonSeparatedList(attribute));
}

private static string GetKeywordSafeName(this string name)
{
return TypesMetadata.CSharpKeywords.Contains(name) ? '@' + name : name;
}
}
}
125 changes: 125 additions & 0 deletions src/Pharmacist.Core/Generation/TypesMetadata.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
// Copyright (c) 2019 .NET Foundation and Contributors. All rights reserved.
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for full license information.

using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;

namespace Pharmacist.Core.Generation
{
/// <summary>
/// Contains metadata about types.
/// </summary>
internal static class TypesMetadata
{
/// <summary>
/// Gets a set of CSharp keywords.
/// </summary>
public static ISet<string> CSharpKeywords { get; } = new HashSet<string>
{
"abstract",
"as",
"base",
"bool",
"break",
"byte",
"case",
"catch",
"char",
"checked",
"class",
"const",
"continue",
"decimal",
"default",
"delegate",
"do",
"double",
"else",
"enum",
"event",
"explicit",
"extern",
"false",
"finally",
"fixed",
"float",
"for",
"foreach",
"goto",
"if",
"implicit",
"in",
"int",
"interface",
"internal",
"is",
"lock",
"long",
"namespace",
"new",
"null",
"object",
"operator",
"out",
"override",
"params",
"private",
"protected",
"public",
"readonly",
"ref",
"return",
"sbyte",
"sealed",
"short",
"sizeof",
"stackalloc",
"static",
"string",
"struct",
"switch",
"this",
"throw",
"true",
"try",
"typeof",
"uint",
"ulong",
"unchecked",
"unsafe",
"ushort",
"using",
"using",
"static",
"virtual void",
"volatile",
"while"
};

/// <summary>
/// Gets a list of full type names, and their built in C# aliases.
/// </summary>
public static IReadOnlyDictionary<string, string> FullToBuiltInTypes { get; } = new ReadOnlyDictionary<string, string>(
new ConcurrentDictionary<string, string>
{
["System.Boolean"] = "bool",
["System.Byte"] = "byte",
["System.SByte"] = "sbyte",
["System.Char"] = "char",
["System.Decimal"] = "decimal",
["System.Double"] = "double",
["System.Single"] = "float",
["System.Int32"] = "int",
["System.UInt32"] = "uint",
["System.Int64"] = "long",
["System.UInt64"] = "ulong",
["System.Object"] = "object",
["System.Int16"] = "short",
["System.UInt16"] = "ushort",
["System.String"] = "string"
});
}
}
Loading

0 comments on commit f668749

Please sign in to comment.