using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis;
namespace Vellum.Generator
{
///
/// Describes a class that derives from others, as plain data, so it can be checked for a converter of its own.
///
public sealed class UnsavedSubtype : IEquatable
{
public readonly string Name;
///
/// Holds the class's name as matched against the types something has a converter for.
///
public readonly string ConvertedName;
///
/// Holds the name of each class it derives from, nearest first, as matched against the same.
///
public readonly EquatableArray BaseNames;
public readonly SourcePlace Place;
///
/// Holds the name of the class and of each class it derives from, as matched against the base classes of
/// the family converters.
///
public readonly EquatableArray FamilyNames;
public UnsavedSubtype(string name, string convertedName, EquatableArray baseNames, SourcePlace place,
EquatableArray familyNames = default)
{
FamilyNames = familyNames;
Name = name;
ConvertedName = convertedName;
BaseNames = baseNames;
Place = place;
}
///
/// Returns as plain data, or null if it can never be a value that's saved: it is
/// abstract or static, or derives from nothing but object.
///
public static UnsavedSubtype Of(INamedTypeSymbol type)
{
if (type.IsAbstract || type.IsStatic || type.TypeKind != TypeKind.Class)
{
return null;
}
ImmutableArray.Builder baseNames = ImmutableArray.CreateBuilder();
for (INamedTypeSymbol current = type.BaseType; current != null
&& current.SpecialType != SpecialType.System_Object; current = current.BaseType)
{
baseNames.Add(SaveableTypes.NameOf(current));
// A generic converter is matched by the generic type it converts, not by each type made from it.
if (current.IsGenericType)
{
baseNames.Add(SaveableTypes.NameOf(current.OriginalDefinition));
}
}
if (baseNames.Count == 0)
{
return null;
}
return new UnsavedSubtype(type.Name, SaveableTypes.NameOf(type),
new EquatableArray(baseNames.ToImmutable()), SourcePlace.Of(type.Locations.FirstOrDefault()),
SaveableTypes.FamilyNamesOf(type));
}
///
/// Returns what is wrong with the class in , given the names
/// of what converters convert and the base classes
/// of the family converters, which save every class derived from them.
///
public List Check(Compilation compilation, HashSet converted,
Dictionary> families)
{
var returnMe = new List();
if (converted.Contains(ConvertedName) || FamilyNames.Items.Any(families.ContainsKey))
{
return returnMe;
}
string saved = BaseNames.Items.FirstOrDefault(converted.Contains);
if (saved != null)
{
returnMe.Add(Diagnostic.Create(VellumDiagnostics.UnsavedSubtype, Place.LocationIn(compilation), Name,
saved));
}
return returnMe;
}
public bool Equals(UnsavedSubtype other)
{
return other != null
&& Name == other.Name
&& ConvertedName == other.ConvertedName
&& BaseNames.Equals(other.BaseNames)
&& FamilyNames.Equals(other.FamilyNames)
&& Place.Equals(other.Place);
}
public override bool Equals(object obj)
{
return Equals(obj as UnsavedSubtype);
}
public override int GetHashCode()
{
return ConvertedName.GetHashCode() * 31 + BaseNames.GetHashCode();
}
}
}