From 914b232d7a7916ae8bd36f1bd472f5e708c7fc33 Mon Sep 17 00:00:00 2001 From: Josef Pihrt Date: Fri, 25 Aug 2023 21:14:57 +0200 Subject: [PATCH] Add analyzer 'Add/remove trailing comma' (#931) --- .editorconfig | 2 + ChangeLog.md | 3 + ...AddOrRemoveTrailingCommaCodeFixProvider.cs | 139 ++++++++ src/Analyzers.xml | 40 +++ .../AddOrRemoveTrailingCommaAnalyzer.cs | 185 ++++++++++ .../CSharp/DiagnosticIdentifiers.Generated.cs | 1 + .../CSharp/DiagnosticRules.Generated.cs | 14 +- .../CSharp/CodeStyle/TrailingCommaStyle.cs | 11 + .../CSharp/Extensions/CodeStyleExtensions.cs | 23 ++ src/Common/ConfigOptionKeys.Generated.cs | 1 + src/Common/ConfigOptionValues.Generated.cs | 3 + src/Common/ConfigOptions.Generated.cs | 7 + src/ConfigOptions.xml | 8 + .../RemoveRedundantCommaInInitializer.cs | 12 - .../RCS1260AddOrRemoveTrailingCommaTests.cs | 332 ++++++++++++++++++ .../Markdown/MarkdownGenerator.cs | 65 ++-- src/Tools/Metadata/MetadataFile.cs | 10 +- src/Tools/Metadata/SampleMetadata.cs | 4 +- .../src/configurationFiles.generated.ts | 10 +- 19 files changed, 829 insertions(+), 41 deletions(-) create mode 100644 src/Analyzers.CodeFixes/CSharp/CodeFixes/AddOrRemoveTrailingCommaCodeFixProvider.cs create mode 100644 src/Analyzers/CSharp/Analysis/AddOrRemoveTrailingCommaAnalyzer.cs create mode 100644 src/Common/CSharp/CodeStyle/TrailingCommaStyle.cs delete mode 100644 src/Tests.Old/Analyzers.Tests.Old/RemoveRedundantCommaInInitializer.cs create mode 100644 src/Tests/Analyzers.Tests/RCS1260AddOrRemoveTrailingCommaTests.cs diff --git a/.editorconfig b/.editorconfig index ab81a354fb..140f358dc2 100644 --- a/.editorconfig +++ b/.editorconfig @@ -45,6 +45,7 @@ roslynator_doc_comment_summary_style = multi_line roslynator_enum_flag_value_style = shift_operator roslynator_blank_line_after_file_scoped_namespace_declaration = true roslynator_null_check_style = pattern_matching +roslynator_trailing_comma_style = omit_when_single_line dotnet_diagnostic.RCS0001.severity = suggestion dotnet_diagnostic.RCS0003.severity = suggestion @@ -146,6 +147,7 @@ dotnet_diagnostic.RCS1252.severity = suggestion dotnet_diagnostic.RCS1253.severity = suggestion dotnet_diagnostic.RCS1254.severity = suggestion dotnet_diagnostic.RCS1255.severity = none +dotnet_diagnostic.RCS1260.severity = suggestion dotnet_diagnostic.IDE0007.severity = none dotnet_diagnostic.IDE0007WithoutSuggestion.severity = none diff --git a/ChangeLog.md b/ChangeLog.md index ac8853a0b1..2d6059069a 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -27,6 +27,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Remove empty region directive ([RCS1091](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1091)) - Remove empty destructor ([RCS1106](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1106)) - [CLI] Add glob pattern matching (`--include` or/and `--exclude`) ([#1178](https://github.com/josefpihrt/roslynator/pull/1178), [#1183](https://github.com/josefpihrt/roslynator/pull/1183)). +- Add analyzer "Include/omit trailing comma" ([RCS1256](https://github.com/JosefPihrt/Roslynator/blob/main/docs/analyzers/RCS1256.md)) ([#931](https://github.com/JosefPihrt/Roslynator/pull/931)). + - Required option: `roslynator_trailing_comma_style = include|omit|omit_when_single_line` + - Not enabled by default ### Changed diff --git a/src/Analyzers.CodeFixes/CSharp/CodeFixes/AddOrRemoveTrailingCommaCodeFixProvider.cs b/src/Analyzers.CodeFixes/CSharp/CodeFixes/AddOrRemoveTrailingCommaCodeFixProvider.cs new file mode 100644 index 0000000000..37db2fc2ab --- /dev/null +++ b/src/Analyzers.CodeFixes/CSharp/CodeFixes/AddOrRemoveTrailingCommaCodeFixProvider.cs @@ -0,0 +1,139 @@ +// Copyright (c) Josef Pihrt and Contributors. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Immutable; +using System.Composition; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Text; +using Roslynator.CodeFixes; + +namespace Roslynator.CSharp.CodeFixes; + +[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(AddOrRemoveTrailingCommaCodeFixProvider))] +[Shared] +public sealed class AddOrRemoveTrailingCommaCodeFixProvider : BaseCodeFixProvider +{ + public override ImmutableArray FixableDiagnosticIds + { + get { return ImmutableArray.Create(DiagnosticIdentifiers.AddOrRemoveTrailingComma); } + } + + public override async Task RegisterCodeFixesAsync(CodeFixContext context) + { + SyntaxNode root = await context.GetSyntaxRootAsync().ConfigureAwait(false); + + if (!TryFindFirstAncestorOrSelf( + root, + context.Span, + out SyntaxNode node, + predicate: f => f.IsKind( + SyntaxKind.ArrayInitializerExpression, + SyntaxKind.ObjectInitializerExpression, + SyntaxKind.CollectionInitializerExpression, + SyntaxKind.EnumDeclaration, + SyntaxKind.AnonymousObjectCreationExpression))) + { + return; + } + + Diagnostic diagnostic = context.Diagnostics[0]; + Document document = context.Document; + + if (node is InitializerExpressionSyntax initializer) + { + SeparatedSyntaxList expressions = initializer.Expressions; + + int count = expressions.Count; + + if (count == expressions.SeparatorCount) + { + CodeAction codeAction = CodeAction.Create( + "Remove comma", + ct => RemoveTrailingComma(document, expressions.GetSeparator(count - 1), ct), + GetEquivalenceKey(diagnostic)); + + context.RegisterCodeFix(codeAction, diagnostic); + } + else + { + CodeAction codeAction = CodeAction.Create( + "Add comma", + ct => AddTrailingComma(document, expressions.Last(), ct), + GetEquivalenceKey(diagnostic)); + + context.RegisterCodeFix(codeAction, diagnostic); + } + } + else if (node is AnonymousObjectCreationExpressionSyntax objectCreation) + { + SeparatedSyntaxList initializers = objectCreation.Initializers; + + int count = initializers.Count; + + if (count == initializers.SeparatorCount) + { + CodeAction codeAction = CodeAction.Create( + "Remove comma", + ct => RemoveTrailingComma(document, initializers.GetSeparator(count - 1), ct), + GetEquivalenceKey(diagnostic)); + + context.RegisterCodeFix(codeAction, diagnostic); + } + else + { + CodeAction codeAction = CodeAction.Create( + "Add comma", + ct => AddTrailingComma(document, initializers.Last(), ct), + GetEquivalenceKey(diagnostic)); + + context.RegisterCodeFix(codeAction, diagnostic); + } + } + else if (node is EnumDeclarationSyntax enumDeclaration) + { + SeparatedSyntaxList members = enumDeclaration.Members; + + int count = members.Count; + + if (count == members.SeparatorCount) + { + CodeAction codeAction = CodeAction.Create( + "Remove comma", + ct => RemoveTrailingComma(document, members.GetSeparator(count - 1), ct), + GetEquivalenceKey(diagnostic)); + + context.RegisterCodeFix(codeAction, diagnostic); + } + else + { + CodeAction codeAction = CodeAction.Create( + "Add comma", + ct => AddTrailingComma(document, members.Last(), ct), + GetEquivalenceKey(diagnostic)); + + context.RegisterCodeFix(codeAction, diagnostic); + } + } + } + + private static Task RemoveTrailingComma( + Document document, + SyntaxToken comma, + CancellationToken cancellationToken) + { + return document.WithTextChangeAsync(new TextChange(comma.Span, ""), cancellationToken); + } + + private static Task AddTrailingComma( + Document document, + SyntaxNode lastNode, + CancellationToken cancellationToken) + { + return document.WithTextChangeAsync(new TextChange(new TextSpan(lastNode.Span.End, 0), ","), cancellationToken); + } +} diff --git a/src/Analyzers.xml b/src/Analyzers.xml index 24a119220a..fbffed7b28 100644 --- a/src/Analyzers.xml +++ b/src/Analyzers.xml @@ -2336,10 +2336,13 @@ if (f) RCS1035 RemoveRedundantCommaInInitializer + Obsolete + Use RCS1260 instead Remove redundant comma in initializer. Hidden false true + HideFromConfiguration + + RCS1260 + Add/remove trailing comma. + {0} trailing comma. + General + Info + false + + + + + + + + + + + + + + + + + RCS9001 UsePatternMatching diff --git a/src/Analyzers/CSharp/Analysis/AddOrRemoveTrailingCommaAnalyzer.cs b/src/Analyzers/CSharp/Analysis/AddOrRemoveTrailingCommaAnalyzer.cs new file mode 100644 index 0000000000..73808677db --- /dev/null +++ b/src/Analyzers/CSharp/Analysis/AddOrRemoveTrailingCommaAnalyzer.cs @@ -0,0 +1,185 @@ +// Copyright (c) Josef Pihrt and Contributors. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Text; +using Roslynator.CSharp.CodeStyle; + +namespace Roslynator.CSharp.Analysis; + +[DiagnosticAnalyzer(LanguageNames.CSharp)] +public sealed class AddOrRemoveTrailingCommaAnalyzer : BaseDiagnosticAnalyzer +{ + private static ImmutableArray _supportedDiagnostics; + + public override ImmutableArray SupportedDiagnostics + { + get + { + if (_supportedDiagnostics.IsDefault) + Immutable.InterlockedInitialize(ref _supportedDiagnostics, DiagnosticRules.AddOrRemoveTrailingComma); + + return _supportedDiagnostics; + } + } + + public override void Initialize(AnalysisContext context) + { + base.Initialize(context); + + context.RegisterSyntaxNodeAction(f => AnalyzeEnumDeclaration(f), SyntaxKind.EnumDeclaration); + context.RegisterSyntaxNodeAction(f => AnalyzeAnonymousObjectCreationExpression(f), SyntaxKind.AnonymousObjectCreationExpression); + + context.RegisterSyntaxNodeAction( + f => AnalyzeInitializerExpression(f), + SyntaxKind.ArrayInitializerExpression, + SyntaxKind.ObjectInitializerExpression, + SyntaxKind.CollectionInitializerExpression); + } + + private static void AnalyzeInitializerExpression(SyntaxNodeAnalysisContext context) + { + TrailingCommaStyle style = context.GetTrailingCommaStyle(); + + if (style == TrailingCommaStyle.None) + return; + + var initializer = (InitializerExpressionSyntax)context.Node; + + SeparatedSyntaxList expressions = initializer.Expressions; + + if (!expressions.Any()) + return; + + int count = expressions.Count; + int separatorCount = expressions.SeparatorCount; + + if (count == separatorCount) + { + if (style == TrailingCommaStyle.Omit) + { + ReportRemove(context, expressions.GetSeparator(count - 1)); + } + else if (style == TrailingCommaStyle.OmitWhenSingleLine + && expressions.IsSingleLine(cancellationToken: context.CancellationToken)) + { + ReportRemove(context, expressions.GetSeparator(count - 1)); + } + } + else if (separatorCount == count - 1) + { + if (style == TrailingCommaStyle.Include) + { + ReportAdd(context, expressions.Last()); + } + else if (style == TrailingCommaStyle.OmitWhenSingleLine + && !expressions.IsSingleLine(cancellationToken: context.CancellationToken)) + { + ReportAdd(context, expressions.Last()); + } + } + } + + private static void AnalyzeEnumDeclaration(SyntaxNodeAnalysisContext context) + { + TrailingCommaStyle style = context.GetTrailingCommaStyle(); + + if (style == TrailingCommaStyle.None) + return; + + var enumDeclaration = (EnumDeclarationSyntax)context.Node; + + SeparatedSyntaxList members = enumDeclaration.Members; + + if (!members.Any()) + return; + + int count = members.Count; + int separatorCount = members.SeparatorCount; + + if (count == separatorCount) + { + if (style == TrailingCommaStyle.Omit) + { + ReportRemove(context, members.GetSeparator(count - 1)); + } + else if (style == TrailingCommaStyle.OmitWhenSingleLine + && members.IsSingleLine(cancellationToken: context.CancellationToken)) + { + ReportRemove(context, members.GetSeparator(count - 1)); + } + } + else if (separatorCount == count - 1) + { + if (style == TrailingCommaStyle.Include) + { + ReportAdd(context, members.Last()); + } + else if (style == TrailingCommaStyle.OmitWhenSingleLine + && !members.IsSingleLine(cancellationToken: context.CancellationToken)) + { + ReportAdd(context, members.Last()); + } + } + } + + private static void AnalyzeAnonymousObjectCreationExpression(SyntaxNodeAnalysisContext context) + { + TrailingCommaStyle style = context.GetTrailingCommaStyle(); + + if (style == TrailingCommaStyle.None) + return; + + var objectCreation = (AnonymousObjectCreationExpressionSyntax)context.Node; + + SeparatedSyntaxList initializers = objectCreation.Initializers; + + if (!initializers.Any()) + return; + + int count = initializers.Count; + int separatorCount = initializers.SeparatorCount; + + if (count == separatorCount) + { + if (style == TrailingCommaStyle.Omit) + { + ReportRemove(context, initializers.GetSeparator(count - 1)); + } + else if (style == TrailingCommaStyle.OmitWhenSingleLine + && initializers.IsSingleLine(cancellationToken: context.CancellationToken)) + { + ReportRemove(context, initializers.GetSeparator(count - 1)); + } + } + else if (separatorCount == count - 1) + { + if (style == TrailingCommaStyle.Include) + { + ReportAdd(context, initializers.Last()); + } + else if (style == TrailingCommaStyle.OmitWhenSingleLine + && !initializers.IsSingleLine(cancellationToken: context.CancellationToken)) + { + ReportAdd(context, initializers.Last()); + } + } + } + + private static void ReportAdd(SyntaxNodeAnalysisContext context, SyntaxNode lastNode) + { + DiagnosticHelpers.ReportDiagnostic( + context, + DiagnosticRules.AddOrRemoveTrailingComma, + Location.Create(lastNode.SyntaxTree, new TextSpan(lastNode.Span.End, 0)), + "Add"); + } + + private static void ReportRemove(SyntaxNodeAnalysisContext context, SyntaxToken token) + { + DiagnosticHelpers.ReportDiagnostic(context, DiagnosticRules.AddOrRemoveTrailingComma, token, "Remove"); + } +} diff --git a/src/Analyzers/CSharp/DiagnosticIdentifiers.Generated.cs b/src/Analyzers/CSharp/DiagnosticIdentifiers.Generated.cs index 9576d27404..1c6fa3d8d3 100644 --- a/src/Analyzers/CSharp/DiagnosticIdentifiers.Generated.cs +++ b/src/Analyzers/CSharp/DiagnosticIdentifiers.Generated.cs @@ -216,5 +216,6 @@ public static partial class DiagnosticIdentifiers public const string UseEnumFieldExplicitly = "RCS1257"; public const string UnnecessaryEnumFlag = "RCS1258"; public const string RemoveEmptySyntax = "RCS1259"; + public const string AddOrRemoveTrailingComma = "RCS1260"; } } \ No newline at end of file diff --git a/src/Analyzers/CSharp/DiagnosticRules.Generated.cs b/src/Analyzers/CSharp/DiagnosticRules.Generated.cs index 516fe48767..26be801935 100644 --- a/src/Analyzers/CSharp/DiagnosticRules.Generated.cs +++ b/src/Analyzers/CSharp/DiagnosticRules.Generated.cs @@ -305,7 +305,7 @@ public static partial class DiagnosticRules public static readonly DiagnosticDescriptor RemoveRedundantCommaInInitializer = DiagnosticDescriptorFactory.Create( id: DiagnosticIdentifiers.RemoveRedundantCommaInInitializer, title: "Remove redundant comma in initializer.", - messageFormat: "Remove redundant comma in initializer.", + messageFormat: "([deprecated] Use RCS1260 instead) Remove redundant comma in initializer.", category: DiagnosticCategories.Roslynator, defaultSeverity: DiagnosticSeverity.Hidden, isEnabledByDefault: false, @@ -2557,5 +2557,17 @@ public static partial class DiagnosticRules helpLinkUri: DiagnosticIdentifiers.RemoveEmptySyntax, customTags: WellKnownDiagnosticTags.Unnecessary); + /// RCS1260 + public static readonly DiagnosticDescriptor AddOrRemoveTrailingComma = DiagnosticDescriptorFactory.Create( + id: DiagnosticIdentifiers.AddOrRemoveTrailingComma, + title: "Add/remove trailing comma.", + messageFormat: "{0} trailing comma.", + category: DiagnosticCategories.Roslynator, + defaultSeverity: DiagnosticSeverity.Info, + isEnabledByDefault: false, + description: null, + helpLinkUri: DiagnosticIdentifiers.AddOrRemoveTrailingComma, + customTags: Array.Empty()); + } } \ No newline at end of file diff --git a/src/Common/CSharp/CodeStyle/TrailingCommaStyle.cs b/src/Common/CSharp/CodeStyle/TrailingCommaStyle.cs new file mode 100644 index 0000000000..21266ab355 --- /dev/null +++ b/src/Common/CSharp/CodeStyle/TrailingCommaStyle.cs @@ -0,0 +1,11 @@ +// Copyright (c) Josef Pihrt and Contributors. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Roslynator.CSharp.CodeStyle; + +internal enum TrailingCommaStyle +{ + None, + Include, + Omit, + OmitWhenSingleLine, +} diff --git a/src/Common/CSharp/Extensions/CodeStyleExtensions.cs b/src/Common/CSharp/Extensions/CodeStyleExtensions.cs index 00fbc39fb7..78b3e5719e 100644 --- a/src/Common/CSharp/Extensions/CodeStyleExtensions.cs +++ b/src/Common/CSharp/Extensions/CodeStyleExtensions.cs @@ -399,6 +399,29 @@ public static AccessibilityModifierStyle GetAccessModifiersStyle(this SyntaxNode return AccessibilityModifierStyle.None; } + public static TrailingCommaStyle GetTrailingCommaStyle(this SyntaxNodeAnalysisContext context) + { + AnalyzerConfigOptions configOptions = context.GetConfigOptions(); + + if (ConfigOptions.TryGetValue(configOptions, ConfigOptions.TrailingCommaStyle, out string rawValue)) + { + if (string.Equals(rawValue, ConfigOptionValues.TrailingCommaStyle_Include, StringComparison.OrdinalIgnoreCase)) + { + return TrailingCommaStyle.Include; + } + else if (string.Equals(rawValue, ConfigOptionValues.TrailingCommaStyle_Omit, StringComparison.OrdinalIgnoreCase)) + { + return TrailingCommaStyle.Omit; + } + else if (string.Equals(rawValue, ConfigOptionValues.TrailingCommaStyle_OmitWhenSingleLine, StringComparison.OrdinalIgnoreCase)) + { + return TrailingCommaStyle.OmitWhenSingleLine; + } + } + + return TrailingCommaStyle.None; + } + public static ObjectCreationTypeStyle GetObjectCreationTypeStyle(this SyntaxNodeAnalysisContext context) { AnalyzerConfigOptions configOptions = context.GetConfigOptions(); diff --git a/src/Common/ConfigOptionKeys.Generated.cs b/src/Common/ConfigOptionKeys.Generated.cs index 5c4844bfe2..7dfdb50c37 100644 --- a/src/Common/ConfigOptionKeys.Generated.cs +++ b/src/Common/ConfigOptionKeys.Generated.cs @@ -36,6 +36,7 @@ internal static partial class ConfigOptionKeys public const string PrefixFieldIdentifierWithUnderscore = "roslynator_prefix_field_identifier_with_underscore"; public const string SuppressUnityScriptMethods = "roslynator_suppress_unity_script_methods"; public const string TabLength = "roslynator_tab_length"; + public const string TrailingCommaStyle = "roslynator_trailing_comma_style"; public const string UseAnonymousFunctionOrMethodGroup = "roslynator_use_anonymous_function_or_method_group"; public const string UseBlockBodyWhenDeclarationSpansOverMultipleLines = "roslynator_use_block_body_when_declaration_spans_over_multiple_lines"; public const string UseBlockBodyWhenExpressionSpansOverMultipleLines = "roslynator_use_block_body_when_expression_spans_over_multiple_lines"; diff --git a/src/Common/ConfigOptionValues.Generated.cs b/src/Common/ConfigOptionValues.Generated.cs index c6b7f7f35d..4a9fa201d9 100644 --- a/src/Common/ConfigOptionValues.Generated.cs +++ b/src/Common/ConfigOptionValues.Generated.cs @@ -49,6 +49,9 @@ internal static partial class ConfigOptionValues public const string ObjectCreationTypeStyle_Explicit = "explicit"; public const string ObjectCreationTypeStyle_Implicit = "implicit"; public const string ObjectCreationTypeStyle_ImplicitWhenTypeIsObvious = "implicit_when_type_is_obvious"; + public const string TrailingCommaStyle_Include = "include"; + public const string TrailingCommaStyle_Omit = "omit"; + public const string TrailingCommaStyle_OmitWhenSingleLine = "omit_when_single_line"; public const string UseAnonymousFunctionOrMethodGroup_AnonymousFunction = "anonymous_function"; public const string UseAnonymousFunctionOrMethodGroup_MethodGroup = "method_group"; } diff --git a/src/Common/ConfigOptions.Generated.cs b/src/Common/ConfigOptions.Generated.cs index ca1648383a..56346a9aad 100644 --- a/src/Common/ConfigOptions.Generated.cs +++ b/src/Common/ConfigOptions.Generated.cs @@ -188,6 +188,12 @@ public static partial class ConfigOptions defaultValuePlaceholder: "", description: "A length of a tab character."); + public static readonly ConfigOptionDescriptor TrailingCommaStyle = new( + key: ConfigOptionKeys.TrailingCommaStyle, + defaultValue: null, + defaultValuePlaceholder: "include|omit|omit_when_single_line", + description: "Include/omit trailing comma in initializer or enum."); + public static readonly ConfigOptionDescriptor UseAnonymousFunctionOrMethodGroup = new( key: ConfigOptionKeys.UseAnonymousFunctionOrMethodGroup, defaultValue: null, @@ -240,6 +246,7 @@ private static IEnumerable> GetRequiredOptions() yield return new KeyValuePair("RCS1252", JoinOptionKeys(ConfigOptionKeys.InfiniteLoopStyle)); yield return new KeyValuePair("RCS1253", JoinOptionKeys(ConfigOptionKeys.DocCommentSummaryStyle)); yield return new KeyValuePair("RCS1254", JoinOptionKeys(ConfigOptionKeys.EnumFlagValueStyle)); + yield return new KeyValuePair("RCS1260", JoinOptionKeys(ConfigOptionKeys.TrailingCommaStyle)); } } } \ No newline at end of file diff --git a/src/ConfigOptions.xml b/src/ConfigOptions.xml index 06fafe0530..f40068350f 100644 --- a/src/ConfigOptions.xml +++ b/src/ConfigOptions.xml @@ -204,6 +204,14 @@ true|false Add/remove blank line after file scoped namespace declaration +