public void AddCppSyntax() { // // Params Syntaxes // project.ParamSyntax.Add(new ParamSyntax( @"^(?:\s*)(?:description[s]?)?\:\n(?<content>(?!\n{2,})(?:.|\n[^\n])*)", @"^(?:[\n\s]*)(?<content>[^\n]+)", @"description", @"cpp") ); project.ParamSyntax.Add(new ParamSyntax( @"^(?:\s*)(?:exemple|example|sample?)?\:\n(?<content>(?!\n{2,})(?:.|\n[^\n])*)", @"(?<content>[^\0]*)", @"exemple", @"cpp") ); project.ParamSyntax.Add(new ParamSyntax( @"^(?:\s*)(?:param[eè]tre[s]|parameter[s]?)?\:\n(?<content>(?!\n{2,})(?:.|\n[^\n])*)", @"^(?:[\n\s]*)(?<content>[^\n]+)", @"param", @"cpp") ); project.ParamSyntax.Add(new ParamSyntax( @"^(?:\s*)(?:remarque[s]?|remark[s]?)?\:\n(?<content>(?!\n{2,})(?:.|\n[^\n])*)", @"^(?:[\n\s]*)(?<content>[^\n]+)", @"remark", @"cpp") ); project.ParamSyntax.Add(new ParamSyntax( @"^(?:\s*)(?:retourne|return)?\:\n(?<content>(?!\n{2,})(?:.|\n[^\n])*)", @"^(?:[\n\s]*)(?<content>[^\n]+)", @"return", @"cpp") ); // // Objets Syntaxes // ObjectSyntax objSyntax; // Fonction objSyntax = new ObjectSyntax( @"^(?:\s*\/\*\*)(?:[\n\s]+)(?<description>[^\n]*)[\n]+(?<content>(?:[^*]|\*[^\/])+)(?:\*\/)(?:[\n\s]*)(?<return_type>[A-Za-z_]+)(?:[\n\s]+)(?<name>[A-Za-z_]+)(?:[\n\s]*)\((?<params>[^\)]*)\)", @"^(?:\s*)\@(?<type>[A-Za-z]+)(?:\s+)(?<content>[^@])+", @"function", @"Fonction", @"cpp"); project.ObjectSyntax.Add(objSyntax); // Structure objSyntax = new ObjectSyntax( @"^(?:\s*\/\*\*)(?:[\n\s]+)(?<description>[^\n]*)[\n]+(?<content>(?:[^*]|\*[^\/])*)(?:\*\/)(?:[\n\s]*)typedef(?:[\n\s]+)struct(?:[\n\s]+)(?<name>[A-Za-z_]+)(?:[\n\s]*)\{", @"^(?:\s*)\@(?<type>[A-Za-z]+)(?:\s+)(?<content>[^@])+", @"struct", @"Structure de données", @"cpp"); project.ObjectSyntax.Add(objSyntax); }
private static TypeSymbol NarrowDiscriminatedObjectType(ITypeManager typeManager, ObjectSyntax expression, DiscriminatedObjectType targetType, IDiagnosticWriter diagnosticWriter, bool skipConstantCheck) { // if we have parse errors, there's no point to check assignability // we should not return the parse errors however because they will get double collected if (expression.HasParseErrors()) { return(LanguageConstants.Any); } var discriminatorProperty = expression.Properties.FirstOrDefault(x => LanguageConstants.IdentifierComparer.Equals(x.TryGetKeyText(), targetType.DiscriminatorKey)); if (discriminatorProperty == null) { // object doesn't contain the discriminator field diagnosticWriter.Write(DiagnosticBuilder.ForPosition(expression).MissingRequiredProperty(ShouldWarn(targetType), targetType.DiscriminatorKey, targetType.DiscriminatorKeysUnionType)); var propertyKeys = expression.Properties .Select(x => x.TryGetKeyText()) .Where(key => !string.IsNullOrEmpty(key)) .Select(key => key !); // do a reverse lookup to check if there's any misspelled discriminator key var misspelledDiscriminatorKey = SpellChecker.GetSpellingSuggestion(targetType.DiscriminatorKey, propertyKeys); if (misspelledDiscriminatorKey != null) { diagnosticWriter.Write(DiagnosticBuilder.ForPosition(expression).DisallowedPropertyWithSuggestion(ShouldWarn(targetType), misspelledDiscriminatorKey, targetType.DiscriminatorKeysUnionType, targetType.DiscriminatorKey)); } return(LanguageConstants.Any); } // At some point in the future we may want to relax the expectation of a string literal key, and allow a generic string. // In this case, the best we can do is validate against the union of all the settable properties. // Let's not do this just yet, and see if a use-case arises. var discriminatorType = typeManager.GetTypeInfo(discriminatorProperty.Value); if (!(discriminatorType is StringLiteralType stringLiteralDiscriminator)) { diagnosticWriter.Write(DiagnosticBuilder.ForPosition(expression).PropertyTypeMismatch(ShouldWarn(targetType), targetType.DiscriminatorKey, targetType.DiscriminatorKeysUnionType, discriminatorType)); return(LanguageConstants.Any); } if (!targetType.UnionMembersByKey.TryGetValue(stringLiteralDiscriminator.Name, out var selectedObjectReference)) { // no matches var discriminatorCandidates = targetType.UnionMembersByKey.Keys.OrderBy(x => x); string?suggestedDiscriminator = SpellChecker.GetSpellingSuggestion(stringLiteralDiscriminator.Name, discriminatorCandidates); var builder = DiagnosticBuilder.ForPosition(discriminatorProperty.Value); bool shouldWarn = ShouldWarn(targetType); diagnosticWriter.Write(suggestedDiscriminator != null ? builder.PropertyStringLiteralMismatchWithSuggestion(shouldWarn, targetType.DiscriminatorKey, targetType.DiscriminatorKeysUnionType, stringLiteralDiscriminator.Name, suggestedDiscriminator) : builder.PropertyTypeMismatch(shouldWarn, targetType.DiscriminatorKey, targetType.DiscriminatorKeysUnionType, discriminatorType)); return(LanguageConstants.Any); } if (!(selectedObjectReference.Type is ObjectType selectedObjectType)) { throw new InvalidOperationException($"Discriminated type {targetType.Name} contains non-object member"); } // we have a match! return(NarrowObjectType(typeManager, expression, selectedObjectType, diagnosticWriter, skipConstantCheck)); }
static object[] CreateRow(string name, ObjectSyntax @object) => new object[] { name, @object };
private static void GetObjectAssignmentDiagnostics(ITypeManager typeManager, ObjectSyntax expression, ObjectType targetType, IList <Diagnostic> diagnostics, bool skipConstantCheck) { // TODO: Short-circuit on any object to avoid unnecessary processing? // TODO: Consider doing the schema check even if there are parse errors // if we have parse errors, there's no point to check assignability // we should not return the parse errors however because they will get double collected if (expression.HasParseErrors()) { return; } var propertyMap = expression.ToPropertyDictionary(); var missingRequiredProperties = targetType.Properties.Values .Where(p => p.Flags.HasFlag(TypePropertyFlags.Required) && propertyMap.ContainsKey(p.Name) == false) .Select(p => p.Name) .OrderBy(p => p) .ConcatString(LanguageConstants.ListSeparator); if (string.IsNullOrEmpty(missingRequiredProperties) == false) { diagnostics.Add(DiagnosticBuilder.ForPosition(expression).MissingRequiredProperties(missingRequiredProperties)); } foreach (var declaredProperty in targetType.Properties.Values) { if (propertyMap.TryGetValue(declaredProperty.Name, out var declaredPropertySyntax)) { bool skipConstantCheckForProperty = skipConstantCheck; // is the property marked as requiring compile-time constants and has the parent already validated this? if (skipConstantCheck == false && declaredProperty.Flags.HasFlag(TypePropertyFlags.Constant)) { // validate that values are compile-time constants diagnostics.AddRange(GetCompileTimeConstantViolation(declaredPropertySyntax.Value)); // disable compile-time constant validation for children skipConstantCheckForProperty = true; } if (declaredProperty.Flags.HasFlag(TypePropertyFlags.ReadOnly)) { // the declared property is read-only // value cannot be assigned to a read-only property diagnostics.Add(DiagnosticBuilder.ForPosition(declaredPropertySyntax.Key).CannotAssignToReadOnlyProperty(declaredProperty.Name)); } // declared property is specified in the value object // validate type GetExpressionAssignmentDiagnosticsInternal( typeManager, declaredPropertySyntax.Value, declaredProperty.TypeReference.Type, diagnostics, (expectedType, actualType, errorExpression) => DiagnosticBuilder.ForPosition(errorExpression).PropertyTypeMismatch(declaredProperty.Name, expectedType, actualType), skipConstantCheckForProperty, skipTypeErrors: true); } } // find properties that are specified on in the expression object but not declared in the schema var extraProperties = expression.Properties .Select(p => p.GetKeyText()) .Except(targetType.Properties.Values.Select(p => p.Name), LanguageConstants.IdentifierComparer) .Select(name => propertyMap[name]); if (targetType.AdditionalProperties == null) { var validUnspecifiedProperties = targetType.Properties.Values .Where(p => !p.Flags.HasFlag(TypePropertyFlags.ReadOnly)) .Select(p => p.Name) .Except(expression.Properties.Select(p => p.GetKeyText()), LanguageConstants.IdentifierComparer) .OrderBy(x => x); // extra properties are not allowed by the type foreach (var extraProperty in extraProperties) { var error = validUnspecifiedProperties.Any() ? DiagnosticBuilder.ForPosition(extraProperty.Key).DisallowedPropertyWithPermissibleProperties(extraProperty.GetKeyText(), targetType.Name, validUnspecifiedProperties) : DiagnosticBuilder.ForPosition(extraProperty.Key).DisallowedProperty(extraProperty.GetKeyText(), targetType.Name); diagnostics.AddRange(error.AsEnumerable()); } } else { // extra properties must be assignable to the right type foreach (ObjectPropertySyntax extraProperty in extraProperties) { bool skipConstantCheckForProperty = skipConstantCheck; // is the property marked as requiring compile-time constants and has the parent already validated this? if (skipConstantCheckForProperty == false && targetType.AdditionalPropertiesFlags.HasFlag(TypePropertyFlags.Constant)) { // validate that values are compile-time constants diagnostics.AddRange(GetCompileTimeConstantViolation(extraProperty.Value)); // disable compile-time constant validation for children skipConstantCheckForProperty = true; } GetExpressionAssignmentDiagnosticsInternal( typeManager, extraProperty.Value, targetType.AdditionalProperties.Type, diagnostics, (expectedType, actualType, errorExpression) => DiagnosticBuilder.ForPosition(errorExpression).PropertyTypeMismatch(extraProperty.GetKeyText(), expectedType, actualType), skipConstantCheckForProperty, skipTypeErrors: true); } } }
private static TypeSymbol NarrowObjectType(ITypeManager typeManager, ObjectSyntax expression, ObjectType targetType, IDiagnosticWriter diagnosticWriter, bool skipConstantCheck) { // TODO: Short-circuit on any object to avoid unnecessary processing? // TODO: Consider doing the schema check even if there are parse errors // if we have parse errors, there's no point to check assignability // we should not return the parse errors however because they will get double collected if (expression.HasParseErrors()) { return(targetType); } var namedPropertyMap = expression.ToNamedPropertyDictionary(); var missingRequiredProperties = targetType.Properties.Values .Where(p => p.Flags.HasFlag(TypePropertyFlags.Required) && !namedPropertyMap.ContainsKey(p.Name)) .Select(p => p.Name) .OrderBy(p => p); if (missingRequiredProperties.Any()) { IPositionable positionable = expression; string blockName = "object"; var parent = typeManager.GetParent(expression); if (parent is ObjectPropertySyntax objectPropertyParent) { positionable = objectPropertyParent.Key; blockName = "object"; } else if (parent is INamedDeclarationSyntax declarationParent) { positionable = declarationParent.Name; blockName = declarationParent.Keyword.Text; } diagnosticWriter.Write(DiagnosticBuilder.ForPosition(positionable).MissingRequiredProperties(ShouldWarn(targetType), missingRequiredProperties, blockName)); } var narrowedProperties = new List <TypeProperty>(); foreach (var declaredProperty in targetType.Properties.Values) { if (namedPropertyMap.TryGetValue(declaredProperty.Name, out var declaredPropertySyntax)) { bool skipConstantCheckForProperty = skipConstantCheck; // is the property marked as requiring compile-time constants and has the parent already validated this? if (skipConstantCheck == false && declaredProperty.Flags.HasFlag(TypePropertyFlags.Constant)) { // validate that values are compile-time constants GetCompileTimeConstantViolation(declaredPropertySyntax.Value, diagnosticWriter); // disable compile-time constant validation for children skipConstantCheckForProperty = true; } if (declaredProperty.Flags.HasFlag(TypePropertyFlags.ReadOnly)) { // the declared property is read-only // value cannot be assigned to a read-only property diagnosticWriter.Write(DiagnosticBuilder.ForPosition(declaredPropertySyntax.Key).CannotAssignToReadOnlyProperty(ShouldWarn(targetType), declaredProperty.Name)); narrowedProperties.Add(new TypeProperty(declaredProperty.Name, declaredProperty.TypeReference.Type, declaredProperty.Flags)); continue; } // declared property is specified in the value object // validate type var narrowedType = NarrowTypeInternal( typeManager, declaredPropertySyntax.Value, declaredProperty.TypeReference.Type, diagnosticWriter, GetPropertyMismatchErrorFactory(ShouldWarn(targetType), declaredProperty.Name), skipConstantCheckForProperty, skipTypeErrors: true); narrowedProperties.Add(new TypeProperty(declaredProperty.Name, narrowedType, declaredProperty.Flags)); } else { narrowedProperties.Add(declaredProperty); } } // find properties that are specified on in the expression object but not declared in the schema var extraProperties = expression.Properties .Where(p => !(p.TryGetKeyText() is string keyName) || !targetType.Properties.ContainsKey(keyName)); if (targetType.AdditionalPropertiesType == null) { bool shouldWarn = ShouldWarn(targetType); var validUnspecifiedProperties = targetType.Properties.Values .Where(p => !p.Flags.HasFlag(TypePropertyFlags.ReadOnly) && !namedPropertyMap.ContainsKey(p.Name)) .Select(p => p.Name) .OrderBy(x => x); // extra properties are not allowed by the type foreach (var extraProperty in extraProperties) { Diagnostic error; var builder = DiagnosticBuilder.ForPosition(extraProperty.Key); if (extraProperty.TryGetKeyText() is string keyName) { error = validUnspecifiedProperties.Any() switch { true => SpellChecker.GetSpellingSuggestion(keyName, validUnspecifiedProperties) switch { string suggestedKeyName when suggestedKeyName != null => builder.DisallowedPropertyWithSuggestion(shouldWarn, keyName, targetType, suggestedKeyName), _ => builder.DisallowedPropertyWithPermissibleProperties(shouldWarn, keyName, targetType, validUnspecifiedProperties) },
public void Variousobjects_ShouldProduceAnErrorWhenAssignedToString(string displayName, ObjectSyntax @object) { var errors = TypeValidator.GetExpressionAssignmentDiagnostics(CreateTypeManager(), @object, LanguageConstants.Int).ToList(); errors.Should().HaveCount(1); errors.Single().Message.Should().Be("Expected a value of type int but the provided value is of type object."); }
public void Variousobjects_ShouldProduceAnErrorWhenAssignedToString(string displayName, ObjectSyntax @object) { var hierarchy = new SyntaxHierarchy(); hierarchy.AddRoot(@object); var(narrowedType, diagnostics) = NarrowTypeAndCollectDiagnostics(hierarchy, @object, LanguageConstants.Int); diagnostics.Should().HaveCount(1); diagnostics.Single().Message.Should().Be("Expected a value of type \"int\" but the provided value is of type \"object\"."); }
private void VisitResourceOrModuleDeclaration(SyntaxBase declaringSyntax, ObjectSyntax body) { var dependsOnProperty = body.SafeGetPropertyByName(LanguageConstants.ResourceDependsOnPropertyName); if (dependsOnProperty?.Value is ArraySyntax declaredDependencies) { if (model.GetSymbolInfo(declaringSyntax) is DeclaredSymbol thisResource) { // If this resource has no implicit dependencies, than all explicit dependsOn entries must be valid, so don't bother checking if (inferredDependenciesMap.Value.TryGetValue(thisResource, out ImmutableHashSet <ResourceDependency>?inferredDependencies)) { foreach (ArrayItemSyntax declaredDependency in declaredDependencies.Items) { // Is this a simple reference to a resource collection? if (model.GetSymbolInfo(declaredDependency.Value) is ResourceSymbol referencedResource) { if (referencedResource.IsCollection) { // Ignore dependsOn entries pointing to a resource collection - dependency analyis would // be complex and user probably knows what they're doing. continue; } if (inferredDependencies.Any(d => d.Resource == referencedResource)) { this.diagnostics.Add( parent.CreateDiagnosticForSpan( declaredDependency.Span, referencedResource.Name)); } } } } } } }
/// <summary> /// Importe des syntaxes d'objets depuis un dossier /// </summary> /// <param name="path"></param> public void ImportSyntaxDirectory(string path) { string groupName = Path.GetFileNameWithoutExtension(path); // Scan les objets if (Directory.Exists(path)) { string[] groupsPaths = Directory.GetFiles(path, "*"); foreach (var syntaxFile in groupsPaths) { using (StreamReader streamReader = new StreamReader(syntaxFile, Encoding.UTF8)) { try { ObjectSyntax syntax = new ObjectSyntax(); this.Add(syntax); syntax.ObjectType = Path.GetFileNameWithoutExtension(syntaxFile); syntax.ContentRegEx = streamReader.ReadLine(); syntax.ParamRegEx = streamReader.ReadLine(); syntax.ObjectDesc = String.Empty; syntax.GroupName = groupName; project.AddObjectSyntax(syntax); Console.WriteLine("Add syntax object " + syntax.ObjectType); } catch (Exception ex) { Console.WriteLine("Ignore object file '" + syntaxFile + "'. " + ex.Message); } streamReader.Close(); } } } // Scan les groupes if (Directory.Exists(path + @"\groups")) { string[] groupsPaths = Directory.GetFiles(path + @"\groups", "*"); foreach (var syntaxFile in groupsPaths) { using (StreamReader streamReader = new StreamReader(syntaxFile, Encoding.UTF8)) { try { ParamSyntax syntax = new ParamSyntax(); this.Add(syntax); syntax.ParamType = Path.GetFileNameWithoutExtension(syntaxFile); syntax.ContentRegEx = streamReader.ReadLine(); syntax.ParamRegEx = streamReader.ReadLine(); syntax.GroupName = groupName; project.AddParamSyntax(syntax); Console.WriteLine("Add syntax param " + syntax.ParamType); } catch (Exception ex) { Console.WriteLine("Ignore param file '" + syntaxFile + "'. " + ex.Message); } streamReader.Close(); } } } }
public void VariousObjects_ShouldProduceNoDiagnosticsWhenAssignedToObjectType(string displayName, ObjectSyntax @object) { var hierarchy = new SyntaxHierarchy(); hierarchy.AddRoot(@object); var(narrowedType, diagnostics) = NarrowTypeAndCollectDiagnostics(hierarchy, @object, LanguageConstants.Object); diagnostics.Should().BeEmpty(); }
/// <summary> /// Scan un fichier à la recherche d'objets /// </summary> /// <param name="groupName">Nom du groupe de syntaxe à scanner</param> /// <param name="text">Texte du code à analyser</param> /// <param name="filePath">Chemin d'accès relatif au fichier analysé</param> /// <param name="syntax">Syntaxe utilisé pour scaner le texte</param> public void ScanFile(string groupName, string text, string filePath, ObjectSyntax syntax, List <ObjectContent> objList) { // Convertie en expression reguliere Regex content = new Regex(syntax.ContentRegEx, RegexOptions.Multiline | RegexOptions.IgnoreCase); Regex param = new Regex(syntax.ParamRegEx, RegexOptions.Multiline | RegexOptions.IgnoreCase); MatchCollection matches = content.Matches(text); foreach (Match match in matches) { // Initialise l'objet ObjectContent o = new ObjectContent(); this.project.AddObjectContent(o); // this.Add(o); o.ObjectType = syntax.ObjectType; o.Filename = filePath; o.Position = match.Index; o.Id = Guid.NewGuid().ToString("N"); // Extrer les paramètres implicite de l'expression régulière foreach (string regexGroupName in content.GetGroupNames()) { if (regexGroupName != "content" && regexGroupName != "0") { o.AddParamContent(new ParamContent(Guid.NewGuid().ToString("N"), regexGroupName, match.Groups[regexGroupName].Value)); //Log(String.Format("\tAdd param '{0}' as '{1}'", groupName, match.Groups[groupName].Value)); } } // Recherche des paramètres dans le contenu de l'objet string objet_text = match.Groups["content"].Value; // Extrer les groupes de parametres foreach (ParamSyntax g in project.ParamSyntax.Where(p => p.GroupName.ToLower() == groupName.ToLower()).ToList()) { // Convertie en expression reguliere Regex pContent = new Regex(g.ContentRegEx, RegexOptions.Multiline | RegexOptions.IgnoreCase); Regex pParam = new Regex(g.ParamRegEx, RegexOptions.Multiline | RegexOptions.IgnoreCase); MatchCollection pMatches = pContent.Matches(objet_text); foreach (Match pMatch in pMatches) { MatchCollection gParamMatches = pParam.Matches(pMatch.Groups["content"].Value); foreach (Match paramMatch in gParamMatches) { o.AddParamContent(new ParamContent(Guid.NewGuid().ToString("N"), g.ParamType, paramMatch.Groups["content"].Value)); } } } // Extrer les parametres d'objet MatchCollection paramMatches = param.Matches(objet_text); foreach (Match paramMatch in paramMatches) { o.AddParamContent(new ParamContent(Guid.NewGuid().ToString("N"), paramMatch.Groups["type"].Value, paramMatch.Groups["content"].Value)); } // Ajoute à la liste des objets objList.Add(o); } }
protected override SyntaxBase ReplaceResourceDeclarationSyntax(ResourceDeclarationSyntax syntax) { if (syntax.TryGetBody() is not ObjectSyntax resourceBody || resourceBody.SafeGetPropertyByName("name") is not ObjectPropertySyntax resourceNameProp || resourceNameProp.Value is not StringSyntax resourceName) { return(syntax); } if (semanticModel.GetSymbolInfo(syntax) is not ResourceSymbol resourceSymbol || resourceSymbol.Type is not ResourceType resourceType) { return(syntax); } if (resourceType.TypeReference.Types.Length < 2) { // we're only looking for child resources here return(syntax); } foreach (var otherResource in semanticModel.AllResources) { var otherResourceSymbol = otherResource.Symbol; if (otherResourceSymbol.Type is not ResourceType otherResourceType || otherResourceType.TypeReference.Types.Length != resourceType.TypeReference.Types.Length - 1 || !resourceType.TypeReference.TypesString.StartsWith($"{otherResourceType.TypeReference.TypesString}/", StringComparison.OrdinalIgnoreCase)) { continue; } // The other resource is a parent type to this one. check if we can refactor the name. if (otherResourceSymbol.DeclaringResource.TryGetBody() is not ObjectSyntax otherResourceBody || otherResourceBody.SafeGetPropertyByName("name") is not ObjectPropertySyntax otherResourceNameProp) { continue; } if (TryGetReplacementChildName(resourceName, otherResourceNameProp.Value, otherResourceSymbol) is not { } newName) { continue; } var replacementNameProp = new ObjectPropertySyntax(resourceNameProp.Key, resourceNameProp.Colon, newName); var parentProp = new ObjectPropertySyntax( SyntaxFactory.CreateIdentifier(LanguageConstants.ResourceParentPropertyName), SyntaxFactory.ColonToken, SyntaxFactory.CreateVariableAccess(otherResourceSymbol.Name)); var replacementBody = new ObjectSyntax( resourceBody.OpenBrace, // parent prop comes first! parentProp.AsEnumerable().Concat(resourceBody.Children.Replace(resourceNameProp, replacementNameProp)), resourceBody.CloseBrace); // at the top we just checked if there is a legitimate body // but to do the replacement correctly we may need to wrap it inside an IfConditionSyntax SyntaxBase replacementValue = syntax.Value switch { ObjectSyntax => replacementBody, IfConditionSyntax ifCondition => new IfConditionSyntax(ifCondition.Keyword, ifCondition.ConditionExpression, replacementBody), // should not be possible _ => throw new NotImplementedException($"Unexpected resource value type '{syntax.Value.GetType().Name}'.") }; return(new ResourceDeclarationSyntax( syntax.LeadingNodes, syntax.Keyword, syntax.Name, syntax.Type, syntax.ExistingKeyword, syntax.Assignment, replacementValue)); } return(syntax); }
//----------------------------------------------------------------------------------------- // Evénements //----------------------------------------------------------------------------------------- #region IEventProcess // Traite les événements public void ProcessEvent(object from, object _this, IEvent e) { // // Model Change // Après le changement du model // if (e is ModelChangeEvent) { ModelChangeEvent ev = e as ModelChangeEvent; //Actualise l'interface UpdateUI(); // Notifie les vues this.NotifyEvent(e); } // // Entity Change // Après le changement d'une entité // if (e is EntityChangeEvent) { EntityChangeEvent ev = e as EntityChangeEvent; //Actualise l'interface OnEntityChange(ev.Model, ev.Entity, ev.BaseEvent); // Notifie les vues this.NotifyEvent(e); } // // Pré-Create // Préprare la création d'une nouvelle entité (avant édition des champs) // if (e is EntityPreCreateEvent) { EntityPreCreateEvent ev = e as EntityPreCreateEvent; // Allocation if (ev.Entity == null) { ev.Entity = app.appModel.CreateEntity(ev.EntityName); } // Affecte le status de création ev.Entity.EntityState = EntityState.Added; // Initialise les données par défaut if (ev.Entity is IEntityPersistent) { IEntityPersistent p = ev.Entity as IEntityPersistent; //Affecte la source de données à la nouvelle entitée p.Factory = app.appModel.project.Factory; } if (ev.Entity is ObjectSyntax) { ObjectSyntax entity = ev.Entity as ObjectSyntax; // Ajoute au projet en cours app.Project.AddObjectSyntax(entity); } if (ev.Entity is ParamSyntax) { ParamSyntax entity = ev.Entity as ParamSyntax; // Ajoute au projet en cours app.Project.AddParamSyntax(entity); } if (ev.Entity is ObjectContent) { ObjectContent entity = ev.Entity as ObjectContent; // Ajoute au projet en cours app.Project.AddObjectContent(entity); // Génére un identifiant unique entity.Id = Guid.NewGuid().ToString("N"); foreach (var p in entity.ParamContent) { p.Id = Guid.NewGuid().ToString("N"); } } if (ev.Entity is ParamContent) { ParamContent entity = ev.Entity as ParamContent; CurObjectContent.AddParamContent(entity); // Génére un identifiant unique entity.Id = Guid.NewGuid().ToString("N"); } if (ev.Entity is SearchParams) { SearchParams entity = ev.Entity as SearchParams; app.Project.AddSearchParams(entity); } if (ev.Entity is DatabaseSource) { DatabaseSource entity = ev.Entity as DatabaseSource; app.Project.AddDatabaseSource(entity); if (app.States.SelectedDatabaseSourceId == null) { app.States.SelectedDatabaseSourceId = app.Project.DatabaseSource.First().Id; } } // Ajoute l'instance au model (notifie le controleur) app.appModel.Add(ev.Entity); } // Implémente la gestion du copier coller EventProcess.ProcessCopyPasteEvents(app, this, app.appModel, from, _this, e); /* * * // * // Change * // Action après le changement d'une entité * // * if (e is EntityChangeEvent) * { * EntityChangeEvent ev = e as EntityChangeEvent; * // Concerne ce model ? * if (ev.Model == app.appModel) * { * if (ev.Entity is ObjectContent) * { * OnPropertyChanged("ObjectContentList"); * } * if (ev.Entity is ParamContent) * { * CurParamContentList = new ObservableCollection<ParamContent>(curObjectContent.ParamContent); * } * if (ev.Entity is DatabaseSource) * { * OnPropertyChanged("CurDatabaseSource"); * } * } * }*/ }
private static TypeSymbol NarrowObjectType(ITypeManager typeManager, ObjectSyntax expression, ObjectType targetType, IList <Diagnostic> diagnostics, bool skipConstantCheck) { // TODO: Short-circuit on any object to avoid unnecessary processing? // TODO: Consider doing the schema check even if there are parse errors // if we have parse errors, there's no point to check assignability // we should not return the parse errors however because they will get double collected if (expression.HasParseErrors()) { return(targetType); } var namedPropertyMap = expression.ToNamedPropertyDictionary(); var missingRequiredProperties = targetType.Properties.Values .Where(p => p.Flags.HasFlag(TypePropertyFlags.Required) && !namedPropertyMap.ContainsKey(p.Name)) .Select(p => p.Name) .OrderBy(p => p); if (missingRequiredProperties.Any()) { diagnostics.Add(DiagnosticBuilder.ForPosition(expression).MissingRequiredProperties(ShouldWarn(targetType), missingRequiredProperties)); } var narrowedProperties = new List <TypeProperty>(); foreach (var declaredProperty in targetType.Properties.Values) { if (namedPropertyMap.TryGetValue(declaredProperty.Name, out var declaredPropertySyntax)) { bool skipConstantCheckForProperty = skipConstantCheck; // is the property marked as requiring compile-time constants and has the parent already validated this? if (skipConstantCheck == false && declaredProperty.Flags.HasFlag(TypePropertyFlags.Constant)) { // validate that values are compile-time constants diagnostics.AddRange(GetCompileTimeConstantViolation(declaredPropertySyntax.Value)); // disable compile-time constant validation for children skipConstantCheckForProperty = true; } if (declaredProperty.Flags.HasFlag(TypePropertyFlags.ReadOnly)) { // the declared property is read-only // value cannot be assigned to a read-only property diagnostics.Add(DiagnosticBuilder.ForPosition(declaredPropertySyntax.Key).CannotAssignToReadOnlyProperty(ShouldWarn(targetType), declaredProperty.Name)); } // declared property is specified in the value object // validate type var narrowedType = NarrowTypeInternal( typeManager, declaredPropertySyntax.Value, declaredProperty.TypeReference.Type, diagnostics, (expectedType, actualType, errorExpression) => DiagnosticBuilder.ForPosition(errorExpression).PropertyTypeMismatch(ShouldWarn(targetType), declaredProperty.Name, expectedType, actualType), skipConstantCheckForProperty, skipTypeErrors: true); narrowedProperties.Add(new TypeProperty(declaredProperty.Name, narrowedType, declaredProperty.Flags)); } else { narrowedProperties.Add(declaredProperty); } } // find properties that are specified on in the expression object but not declared in the schema var extraProperties = expression.Properties .Where(p => !(p.TryGetKeyText() is string keyName) || !targetType.Properties.ContainsKey(keyName)); if (targetType.AdditionalPropertiesType == null) { var validUnspecifiedProperties = targetType.Properties.Values .Where(p => !p.Flags.HasFlag(TypePropertyFlags.ReadOnly)) .Where(p => !namedPropertyMap.ContainsKey(p.Name)) .Select(p => p.Name) .OrderBy(x => x); // extra properties are not allowed by the type foreach (var extraProperty in extraProperties) { Diagnostic error; if (extraProperty.TryGetKeyText() is string keyName) { error = validUnspecifiedProperties.Any() ? DiagnosticBuilder.ForPosition(extraProperty.Key).DisallowedPropertyWithPermissibleProperties(ShouldWarn(targetType), keyName, targetType, validUnspecifiedProperties) : DiagnosticBuilder.ForPosition(extraProperty.Key).DisallowedProperty(ShouldWarn(targetType), keyName, targetType); } else { error = validUnspecifiedProperties.Any() ? DiagnosticBuilder.ForPosition(extraProperty.Key).DisallowedInterpolatedKeyPropertyWithPermissibleProperties(ShouldWarn(targetType), targetType, validUnspecifiedProperties) : DiagnosticBuilder.ForPosition(extraProperty.Key).DisallowedInterpolatedKeyProperty(ShouldWarn(targetType), targetType); } diagnostics.AddRange(error.AsEnumerable()); } } else { // extra properties must be assignable to the right type foreach (ObjectPropertySyntax extraProperty in extraProperties) { bool skipConstantCheckForProperty = skipConstantCheck; // is the property marked as requiring compile-time constants and has the parent already validated this? if (skipConstantCheckForProperty == false && targetType.AdditionalPropertiesFlags.HasFlag(TypePropertyFlags.Constant)) { // validate that values are compile-time constants diagnostics.AddRange(GetCompileTimeConstantViolation(extraProperty.Value)); // disable compile-time constant validation for children skipConstantCheckForProperty = true; } TypeMismatchErrorFactory typeMismatchErrorFactory; if (extraProperty.TryGetKeyText() is string keyName) { typeMismatchErrorFactory = (expectedType, actualType, errorExpression) => DiagnosticBuilder.ForPosition(errorExpression).PropertyTypeMismatch(ShouldWarn(targetType), keyName, expectedType, actualType); } else { typeMismatchErrorFactory = (expectedType, actualType, errorExpression) => DiagnosticBuilder.ForPosition(errorExpression).ExpectedValueTypeMismatch(ShouldWarn(targetType), expectedType, actualType); } var narrowedProperty = NarrowTypeInternal( typeManager, extraProperty.Value, targetType.AdditionalPropertiesType.Type, diagnostics, typeMismatchErrorFactory, skipConstantCheckForProperty, skipTypeErrors: true); // TODO should we try and narrow the additional properties type? May be difficult } } return(new NamedObjectType(targetType.Name, targetType.ValidationFlags, narrowedProperties, targetType.AdditionalPropertiesType, targetType.AdditionalPropertiesFlags)); }
private static IEnumerable <ErrorDiagnostic> GetDiscriminatedObjectAssignmentDiagnostics(ITypeManager typeManager, ObjectSyntax expression, DiscriminatedObjectType targetType, bool skipConstantCheck) { // if we have parse errors, there's no point to check assignability // we should not return the parse errors however because they will get double collected if (expression.HasParseErrors()) { yield break; } var propertyMap = expression.ToPropertyDictionary(); if (!propertyMap.TryGetValue(targetType.DiscriminatorKey, out var discriminatorProperty)) { // object doesn't contain the discriminator field yield return(DiagnosticBuilder.ForPosition(expression).MissingRequiredProperty(targetType.DiscriminatorKey, targetType.DiscriminatorKeysUnionType)); yield break; } // At some point in the future we may want to relax the expectation of a string literal key, and allow a generic string. // In this case, the best we can do is validate against the union of all the settable properties. // Let's not do this just yet, and see if a use-case arises. var discriminatorType = typeManager.GetTypeInfo(discriminatorProperty.Value, new TypeManagerContext()); if (!(discriminatorType is StringLiteralType stringLiteralDiscriminator)) { yield return(DiagnosticBuilder.ForPosition(expression).PropertyTypeMismatch(targetType.DiscriminatorKey, targetType.DiscriminatorKeysUnionType, discriminatorType)); yield break; } if (!targetType.UnionMembersByKey.TryGetValue(stringLiteralDiscriminator.Name, out var selectedObjectType)) { // no matches yield return(DiagnosticBuilder.ForPosition(discriminatorProperty.Value).PropertyTypeMismatch(targetType.DiscriminatorKey, targetType.DiscriminatorKeysUnionType, discriminatorType)); yield break; } // we have a match! foreach (var diagnostic in GetObjectAssignmentDiagnostics(typeManager, expression, selectedObjectType, skipConstantCheck)) { yield return(diagnostic); } yield break; }
private static TypeSymbol NarrowObjectType(ITypeManager typeManager, ObjectSyntax expression, ObjectType targetType, IList <Diagnostic> diagnostics, bool skipConstantCheck) { // TODO: Short-circuit on any object to avoid unnecessary processing? // TODO: Consider doing the schema check even if there are parse errors // if we have parse errors, there's no point to check assignability // we should not return the parse errors however because they will get double collected if (expression.HasParseErrors()) { return(targetType); } var namedPropertyMap = expression.ToNamedPropertyDictionary(); var missingRequiredProperties = targetType.Properties.Values .Where(p => p.Flags.HasFlag(TypePropertyFlags.Required) && !namedPropertyMap.ContainsKey(p.Name)) .Select(p => p.Name) .OrderBy(p => p); if (missingRequiredProperties.Any()) { diagnostics.Add(DiagnosticBuilder.ForPosition(expression).MissingRequiredProperties(ShouldWarn(targetType), missingRequiredProperties)); } var narrowedProperties = new List <TypeProperty>(); foreach (var declaredProperty in targetType.Properties.Values) { if (namedPropertyMap.TryGetValue(declaredProperty.Name, out var declaredPropertySyntax)) { bool skipConstantCheckForProperty = skipConstantCheck; // is the property marked as requiring compile-time constants and has the parent already validated this? if (skipConstantCheck == false && declaredProperty.Flags.HasFlag(TypePropertyFlags.Constant)) { // validate that values are compile-time constants diagnostics.AddRange(GetCompileTimeConstantViolation(declaredPropertySyntax.Value)); // disable compile-time constant validation for children skipConstantCheckForProperty = true; } if (declaredProperty.Flags.HasFlag(TypePropertyFlags.ReadOnly)) { // the declared property is read-only // value cannot be assigned to a read-only property diagnostics.Add(DiagnosticBuilder.ForPosition(declaredPropertySyntax.Key).CannotAssignToReadOnlyProperty(ShouldWarn(targetType), declaredProperty.Name)); } TypeMismatchErrorFactory typeMismatchErrorFactory = (expectedType, actualType, errorExpression) => { var builder = DiagnosticBuilder.ForPosition(errorExpression); var shouldWarn = ShouldWarn(targetType); if (actualType is StringLiteralType) { string?suggestedStringLiteral = null; if (expectedType is StringLiteralType) { suggestedStringLiteral = SpellChecker.GetSpellingSuggestion(actualType.Name, expectedType.Name.AsEnumerable()); } if (expectedType is UnionType unionType && unionType.Members.All(typeReference => typeReference.Type is StringLiteralType)) { var stringLiteralCandidates = unionType.Members.Select(typeReference => typeReference.Type.Name).OrderBy(x => x); suggestedStringLiteral = SpellChecker.GetSpellingSuggestion(actualType.Name, stringLiteralCandidates); } if (suggestedStringLiteral != null) { return(builder.PropertyStringLiteralMismatchWithSuggestion(shouldWarn, declaredProperty.Name, expectedType, actualType.Name, suggestedStringLiteral)); } } return(builder.PropertyTypeMismatch(shouldWarn, declaredProperty.Name, expectedType, actualType)); }; // declared property is specified in the value object // validate type var narrowedType = NarrowTypeInternal( typeManager, declaredPropertySyntax.Value, declaredProperty.TypeReference.Type, diagnostics, typeMismatchErrorFactory, skipConstantCheckForProperty, skipTypeErrors: true); narrowedProperties.Add(new TypeProperty(declaredProperty.Name, narrowedType, declaredProperty.Flags)); } else { narrowedProperties.Add(declaredProperty); } } // find properties that are specified on in the expression object but not declared in the schema var extraProperties = expression.Properties .Where(p => !(p.TryGetKeyText() is string keyName) || !targetType.Properties.ContainsKey(keyName)); if (targetType.AdditionalPropertiesType == null) { bool shouldWarn = ShouldWarn(targetType); var validUnspecifiedProperties = targetType.Properties.Values .Where(p => !p.Flags.HasFlag(TypePropertyFlags.ReadOnly) && !namedPropertyMap.ContainsKey(p.Name)) .Select(p => p.Name) .OrderBy(x => x); // extra properties are not allowed by the type foreach (var extraProperty in extraProperties) { Diagnostic error; var builder = DiagnosticBuilder.ForPosition(extraProperty.Key); if (extraProperty.TryGetKeyText() is string keyName) { error = validUnspecifiedProperties.Any() switch { true => SpellChecker.GetSpellingSuggestion(keyName, validUnspecifiedProperties) switch { string suggestedKeyName when suggestedKeyName != null => builder.DisallowedPropertyWithSuggestion(shouldWarn, keyName, targetType, suggestedKeyName), _ => builder.DisallowedPropertyWithPermissibleProperties(shouldWarn, keyName, targetType, validUnspecifiedProperties) },
public void VariousObjects_ShouldProduceNoDiagnosticsWhenAssignedToObjectType(string displayName, ObjectSyntax @object) { TypeValidator.GetExpressionAssignmentDiagnostics(CreateTypeManager(), @object, LanguageConstants.Object).Should().BeEmpty(); }
private static TypeSymbol NarrowDiscriminatedObjectType(ITypeManager typeManager, ObjectSyntax expression, DiscriminatedObjectType targetType, IList <Diagnostic> diagnostics, bool skipConstantCheck) { // if we have parse errors, there's no point to check assignability // we should not return the parse errors however because they will get double collected if (expression.HasParseErrors()) { return(LanguageConstants.Any); } var discriminatorProperty = expression.Properties.FirstOrDefault(x => LanguageConstants.IdentifierComparer.Equals(x.TryGetKeyText(), targetType.DiscriminatorKey)); if (discriminatorProperty == null) { // object doesn't contain the discriminator field diagnostics.Add(DiagnosticBuilder.ForPosition(expression).MissingRequiredProperty(ShouldWarn(targetType), targetType.DiscriminatorKey, targetType.DiscriminatorKeysUnionType)); return(LanguageConstants.Any); } // At some point in the future we may want to relax the expectation of a string literal key, and allow a generic string. // In this case, the best we can do is validate against the union of all the settable properties. // Let's not do this just yet, and see if a use-case arises. var discriminatorType = typeManager.GetTypeInfo(discriminatorProperty.Value); if (!(discriminatorType is StringLiteralType stringLiteralDiscriminator)) { diagnostics.Add(DiagnosticBuilder.ForPosition(expression).PropertyTypeMismatch(ShouldWarn(targetType), targetType.DiscriminatorKey, targetType.DiscriminatorKeysUnionType, discriminatorType)); return(LanguageConstants.Any); } if (!targetType.UnionMembersByKey.TryGetValue(stringLiteralDiscriminator.Name, out var selectedObjectReference)) { // no matches diagnostics.Add(DiagnosticBuilder.ForPosition(discriminatorProperty.Value).PropertyTypeMismatch(ShouldWarn(targetType), targetType.DiscriminatorKey, targetType.DiscriminatorKeysUnionType, discriminatorType)); return(LanguageConstants.Any); } if (!(selectedObjectReference.Type is ObjectType selectedObjectType)) { throw new InvalidOperationException($"Discriminated type {targetType.Name} contains non-object member"); } // we have a match! return(NarrowObjectType(typeManager, expression, selectedObjectType, diagnostics, skipConstantCheck)); }
public static void AnalyzeObjectCaption(SyntaxNodeAnalysisContext context) { ObjectSyntax syntax = context.Node as ObjectSyntax; AnalyzeObjectCaptions(context, syntax); }