public static ValidationErrors IsWritableReferenceType(Expression e) { if (IsReferenceType(e) is ValidationErrors r) { return(r); } var err = e switch { Expression.AddressOfCase a => ValidationErrors.Create($"AddresOf expression returns read-only reference").Nest("AddressOf"), Expression.ArrayIndexCase a => null, Expression.BlockCase b => IsWritableReferenceType(b.Item.Result).Nest("result").Nest("Block"), Expression.BreakableCase b => null, // we don't know in this case :/ ; TODO: analyze more in-depth? Expression.ConditionalCase c => ValidationErrors.Join( IsWritableReferenceType(c.Item.IfTrue).Nest("ifTrue"), IsWritableReferenceType(c.Item.IfFalse).Nest("ifFalse") ).Nest("Conditional"), Expression.FieldAccessCase f => null, // uh oh, even readonly fields can be assigned from the constructor ;( //f.Item.Field.Signature.IsReadonly ? ValidationErrors.CreateField(new [] { "FieldAccess", "field", "signature", "isReadonly" }, $"Field {f.Item.Field} is readonly") : null, Expression.InvokeCase _ => null, // we don't know if method return readonly ref... Expression.MethodCallCase _ => null, // we don't know if method return readonly ref... Expression.LetInCase l => IsWritableReferenceType(l.Item.Target).Nest("target").Nest("LetIn"), Expression.VariableReferenceCase v => !v.Item.Variable.Mutable ? ValidationErrors.CreateField(new [] { "VariableReference", "variable", "mutable" }, $"Variable {v.Item.Variable} is not mutable") : null, Expression.ParameterCase p => null, // can't track mutability of variables :/ var x => #if DEBUG throw new NotSupportedException($"Expression {x} was supposed not to return reference") #else null #endif }; return(err); }
public static ValidationErrors Validate(ImmutableArray <Entity> entities, ImmutableArray <TypeDef> types) { // IEnumerable<string> getReferencedTypes(TypeRef t) => // t.Match( // actual: x => new [] { x.TypeName }, // nullable: x => getReferencedTypes(x.Type), // list: x => getReferencedTypes(x.Type) // ) // IEnumerable<string> getReferencedTypes(TypeDefCore t) => // t.Match( // primitive: x => new string[0], // union: x => x.Options.SelectMany(getReferencedTypes), // @interface: x => x.Fields.SelectMany(f => getReferencedTypes(f.Type)), // composite: x => x.Fields.SelectMany(f => getReferencedTypes( var declaredTypes = types.Select((a, index) => (a, index)).ToLookup(t => t.a.Name); var result = new List <ValidationErrors>(); // Type names must be unique foreach (var d in declaredTypes) { if (d.Count() > 1) { foreach (var(type, index) in d) { result.Add(ValidationErrors.CreateField(new [] { "types", index.ToString(), "name" }, $"Name of this type is not unique.")); } } } foreach (var(index, type) in types.Select((a, i) => (i, a))) { result.Add(ValidateName(type.Name).Nest("name").Nest(index.ToString()).Nest("types")); if (type.Core is TypeDefCore.CompositeCase composite) { result.Add(ValidateFields(composite.Fields).Nest("core").Nest(index.ToString()).Nest("types")); } else if (type.Core is TypeDefCore.InterfaceCase ifc) { result.Add(ValidateFields(ifc.Fields).Nest("core").Nest(index.ToString()).Nest("types")); } else if (type.Core is TypeDefCore.UnionCase union) { result.Add(ValidationErrors.Join( union.Options.Select((o, i) => (o is TypeRef.ActualTypeCase actO ? ValidateName(actO.TypeName) : ValidationErrors.Create("Union case can't have any modifiers (list, non-null).")) .Nest(i.ToString()).Nest("options").Nest("core").Nest(index.ToString()).Nest("types") ) .ToArray() )); } } return(ValidationErrors.Join(result.ToArray())); }
static ValidationErrors ValidateFields(ImmutableArray <TypeField> fields) { return(ValidationErrors.Join( fields .GroupBy(f => f.Name) .Where(g => g.Count() > 1) .SelectMany(g => g) .Select(field => ValidationErrors.CreateField(new [] { fields.IndexOf(field).ToString(), "name" }, $"Non-unique field name: {field.Name}") ) .Concat(fields.Select((f, i) => ValidationErrors.Join( ValidateName(f.Name).Nest("name"), ValidateDirectives(f.Directives) ).Nest(i.ToString()) )) .ToArray()) .Nest("fields")); }
private static IEnumerable <ValidatorUsage> AnalyzeDirectives(IEnumerable <Directive> directives, Func <string, string[]> getValidatorArgs, string fieldName = null, int?fieldIndex = null) { var defaultFields = fieldName == null ? ImmutableArray <string> .Empty : ImmutableArray.Create(fieldName); var fieldPrefix = fieldName == null ? "" : fieldName + "."; return(from xx in directives.Select((d, i) => (i, d)) let d = xx.d let dindex = xx.i where d.Name.StartsWith("validate") && d.Name.Length > "validate".Length let validatorName = char.ToLower(d.Name["validate".Length]) + d.Name.Substring("validate".Length + 1) let args = getValidatorArgs(validatorName) ?? throw new ValidationErrorException(ValidationErrors.CreateField( (fieldIndex == null ? Array.Empty <string>() : new[] { "fields", fieldIndex.ToString() }).Concat(new [] { "directives", dindex.ToString(), "name" }).ToArray(), $"Validator {validatorName} could not be found" )) let forFields = d.Args["forFields"] == null || args.Contains("forFields") ? defaultFields : d.Args["forFields"].Values <string>().Select(a => fieldPrefix + a).ToImmutableArray() let argValues = args.Select(a => (a, val: d.Args[a])) .Where(a => a.val != null) .ToDictionary(a => a.a, a => a.val) select new ValidatorUsage(validatorName, argValues, forFields, dindex, fieldIndex)); }