From 47f6f6fcbca1097b59356af89fcf8580b10028f4 Mon Sep 17 00:00:00 2001 From: Jean-Philippe Levesque Date: Tue, 3 Oct 2023 10:31:03 -0400 Subject: [PATCH] feat: Turn on warning as errors for layer libs and update analyzers. --- .editorconfig | 80 ++++++++++++++++--- CHANGELOG.md | 4 + Directory.Build.props | 4 +- .../ApplicationTemplate.Access.csproj | 2 + .../Framework/BaseMock.cs | 2 - .../IConfiguration.Extensions.cs | 12 +-- .../IServiceCollection.Extensions.cs | 6 +- .../HttpDebugger/HttpDebuggerHandler.cs | 2 +- .../Framework/Serialization/JwtData.cs | 4 +- .../ApplicationTemplate.Business.csproj | 2 + .../Posts/IPostService.cs | 4 +- .../ApplicationTemplate.Presentation.csproj | 2 + .../Configuration/ApiConfiguration.cs | 4 - .../ConfigurationConfiguration.cs | 5 +- .../Configuration/ReviewConfiguration.cs | 1 + .../MockedConnectivityProvider.cs | 2 +- .../NetworkReconnectionDataLoaderTrigger.cs | 4 +- .../Framework/Startup/StartupBase.cs | 4 +- .../Version/VersionProviderExtensions.cs | 2 +- ...dTaskDynamicPropertyFromDynamicProperty.cs | 2 +- .../ConfigurationDebuggerViewModel.cs | 5 +- .../DiagnosticsOverlayViewModel.cs | 5 +- .../SummaryDiagnosticsViewModel.cs | 1 + .../ViewModels/Posts/PostsPageViewModel.cs | 1 + .../Settings/LicensesPageViewModel.cs | 2 +- .../FormattingTextBoxBehavior.Android.cs | 2 +- .../FormattingTextBoxBehavior.iOS.cs | 1 + .../Configuration/LoggingConfiguration.cs | 2 + .../Configuration/NavigationConfiguration.cs | 1 - .../Controls/AppBarBackButton.cs | 1 + .../Framework/DiagnosticsService.cs | 2 +- .../Helpers/DispatcherQueueExtensions.cs | 1 + .../Helpers/ObservableExtensions.cs | 16 ++-- stylecop.json | 2 +- 34 files changed, 136 insertions(+), 54 deletions(-) diff --git a/.editorconfig b/.editorconfig index 5daef7a5..a7a799e9 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,4 +1,4 @@ -root = true +root = true ############################# # Core EditorConfig Options # @@ -42,7 +42,7 @@ dotnet_sort_system_directives_first = true dotnet_separate_import_directive_groups = false # Use C#10 file-scoped namespaces -csharp_style_namespace_declarations=file_scoped:suggestion +csharp_style_namespace_declarations = file_scoped:suggestion # Avoid the this. keyword dotnet_style_qualification_for_field = false:suggestion @@ -67,6 +67,9 @@ dotnet_style_prefer_auto_properties = true:suggestion dotnet_style_prefer_conditional_expression_over_assignment = true:suggestion dotnet_style_prefer_conditional_expression_over_return = true:suggestion +# Operator placement +dotnet_style_operator_placement_when_wrapping = beginning_of_line + # Parentheses preferences dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:suggestion dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:suggestion @@ -87,7 +90,7 @@ dotnet_naming_style.pascal_case_style.capitalization = pascal_case # Constant fields are PascalCase dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields -dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style +dotnet_naming_rule.constant_fields_should_be_pascal_case.style = non_private_static_field_style dotnet_naming_symbols.constant_fields.applicable_kinds = field dotnet_naming_symbols.constant_fields.applicable_accessibilities = * dotnet_naming_symbols.constant_fields.required_modifiers = const @@ -95,7 +98,7 @@ dotnet_naming_symbols.constant_fields.required_modifiers = const # Non-private readonly fields are PascalCase dotnet_naming_rule.non_private_readonly_fields_should_be_pascal_case.severity = suggestion dotnet_naming_rule.non_private_readonly_fields_should_be_pascal_case.symbols = non_private_readonly_fields -dotnet_naming_rule.non_private_readonly_fields_should_be_pascal_case.style = non_private_readonly_field_style +dotnet_naming_rule.non_private_readonly_fields_should_be_pascal_case.style = non_private_static_field_style dotnet_naming_symbols.non_private_readonly_fields.applicable_kinds = field dotnet_naming_symbols.non_private_readonly_fields.applicable_accessibilities = public, protected, internal, protected internal, private protected dotnet_naming_symbols.non_private_readonly_fields.required_modifiers = readonly @@ -113,7 +116,7 @@ dotnet_naming_style.non_private_static_field_style.capitalization = pascal_case # Private static fields are camelCase dotnet_naming_rule.static_fields_should_be_camel_case.severity = suggestion dotnet_naming_rule.static_fields_should_be_camel_case.symbols = static_fields -dotnet_naming_rule.static_fields_should_be_camel_case.style = static_field_style +dotnet_naming_rule.static_fields_should_be_camel_case.style = instance_field_style dotnet_naming_symbols.static_fields.applicable_kinds = field dotnet_naming_symbols.static_fields.required_modifiers = static dotnet_naming_style.static_field_style.capitalization = camel_case @@ -137,14 +140,14 @@ dotnet_naming_style.camel_case_style.capitalization = camel_case # Local functions are PascalCase dotnet_naming_rule.local_functions_should_be_pascal_case.severity = suggestion dotnet_naming_rule.local_functions_should_be_pascal_case.symbols = local_functions -dotnet_naming_rule.local_functions_should_be_pascal_case.style = local_function_style +dotnet_naming_rule.local_functions_should_be_pascal_case.style = non_private_static_field_style dotnet_naming_symbols.local_functions.applicable_kinds = local_function dotnet_naming_style.local_function_style.capitalization = pascal_case # By default, name items with PascalCase dotnet_naming_rule.members_should_be_pascal_case.severity = suggestion dotnet_naming_rule.members_should_be_pascal_case.symbols = all_members -dotnet_naming_rule.members_should_be_pascal_case.style = pascal_case_style +dotnet_naming_rule.members_should_be_pascal_case.style = non_private_static_field_style dotnet_naming_symbols.all_members.applicable_kinds = * dotnet_naming_style.pascal_case_style.capitalization = pascal_case @@ -152,6 +155,36 @@ dotnet_naming_style.pascal_case_style.capitalization = pascal_case # C# Code Style Rules # ####################### +csharp_using_directive_placement = outside_namespace:silent +csharp_prefer_simple_using_statement = true:suggestion +csharp_style_prefer_method_group_conversion = true:silent +csharp_style_prefer_top_level_statements = true:silent +csharp_style_throw_expression = true:suggestion +csharp_style_prefer_null_check_over_type_check = true:suggestion +csharp_style_prefer_local_over_anonymous_function = true:suggestion +csharp_style_prefer_index_operator = true:suggestion +csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion +csharp_style_prefer_range_operator = true:suggestion +csharp_style_prefer_utf8_string_literals = true:suggestion +csharp_style_prefer_tuple_swap = true:suggestion +csharp_style_unused_value_assignment_preference = discard_variable:suggestion +csharp_style_unused_value_expression_statement_preference = discard_variable:silent +csharp_prefer_static_local_function = true:suggestion +csharp_style_prefer_readonly_struct = true:suggestion +csharp_style_prefer_readonly_struct_member = true:suggestion +csharp_style_allow_embedded_statements_on_same_line_experimental = true:silent +csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true:silent +csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true:silent +csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true:silent +csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true:silent +csharp_style_conditional_delegate_call = true:suggestion +csharp_style_prefer_switch_expression = true:suggestion +csharp_style_prefer_pattern_matching = true:suggestion +csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion +csharp_style_pattern_matching_over_as_with_null_check = true:suggestion +csharp_style_prefer_not_pattern = true:suggestion +csharp_style_prefer_extended_property_pattern = true:suggestion + # Indentation preferences csharp_indent_block_contents = true csharp_indent_braces = false @@ -167,11 +200,13 @@ csharp_style_var_elsewhere = true:suggestion # Prefer property-like constructs to have an expression-body csharp_style_expression_bodied_properties = true:suggestion -csharp_style_expression_bodied_indexers = true:suggestion -csharp_style_expression_bodied_accessors = true:suggestion +csharp_style_expression_bodied_indexers = when_on_single_line:suggestion +csharp_style_expression_bodied_accessors = when_on_single_line:suggestion csharp_style_expression_bodied_methods = false:none csharp_style_expression_bodied_constructors = false:none csharp_style_expression_bodied_operators = false:none +csharp_style_expression_bodied_local_functions = false:silent +csharp_style_expression_bodied_lambdas = when_on_single_line:silent # Newline preferences csharp_new_line_before_open_brace = all @@ -200,6 +235,17 @@ csharp_space_around_binary_operators = before_and_after csharp_space_between_method_declaration_empty_parameter_list_parentheses = false csharp_space_between_method_call_name_and_opening_parenthesis = false csharp_space_between_method_call_empty_parameter_list_parentheses = false +csharp_space_after_comma = true +csharp_space_after_semicolon_in_for_statement = true +csharp_space_between_method_declaration_name_and_open_parenthesis = false +csharp_space_around_declaration_statements = false +csharp_space_before_open_square_brackets = false +csharp_space_between_empty_square_brackets = false +csharp_space_between_square_brackets = false +csharp_space_after_dot = false +csharp_space_before_comma = false +csharp_space_before_dot = false +csharp_space_before_semicolon_in_for_statement = false # Blocks are allowed csharp_prefer_braces = true:suggestion @@ -266,7 +312,7 @@ dotnet_diagnostic.SA1202.severity = suggestion # SA1600:Elements should be documented dotnet_diagnostic.SA1600.severity = none # SA1601:Elements should be documented -dotnet_diagnostic.SA1601.severity = none +dotnet_diagnostic.SA1601.severity = suggestion # SA1116:Split parameters should start on line after declaration dotnet_diagnostic.SA1116.severity = none # SA1111:Closing parenthesis should be on line of last parameter @@ -331,3 +377,17 @@ dotnet_diagnostic.CA1024.severity = none dotnet_diagnostic.SA1615.severity = suggestion # CA5394: Do not use insecure randomness dotnet_diagnostic.CA5394.severity = suggestion +# SA1623: Property summary documentation should match accessors +dotnet_diagnostic.SA1623.severity = suggestion +# SA1502: Element should not be on a single line +dotnet_diagnostic.SA1502.severity = suggestion +# SA1602: Enumeration items should be documented +dotnet_diagnostic.SA1602.severity = suggestion +# CA1032: Implement standard exception constructors +dotnet_diagnostic.CA1032.severity = suggestion +# SA1210: Using directives must be ordered alphabetically by namespace +dotnet_diagnostic.SA1210.severity = suggestion +# CA1308: Normalize strings to uppercase +dotnet_diagnostic.CA1308.severity = none +# CS1587: XML comment is not placed on a valid language element +dotnet_diagnostic.CS1587.severity = none diff --git a/CHANGELOG.md b/CHANGELOG.md index acf08e78..4d821e2c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) Prefix your items with `(Template)` if the change is about the template and not the resulting application. +## 2.1.X +- Enable `TreatWarningsAsErrors` for the Access, Business, and Presentation projects. +- Update analyzers packages and severity of rules. + ## 2.0.X - Renamed the classes providing data to use the `Repository` suffix instead of `Endpoint` or `Service`. - Renamed the Client library to Access. diff --git a/Directory.Build.props b/Directory.Build.props index 0e3085b2..7484b9ee 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,10 +1,10 @@  - + all runtime; build; native; contentfiles; analyzers - + all runtime; build; native; contentfiles; analyzers diff --git a/src/app/ApplicationTemplate.Access/ApplicationTemplate.Access.csproj b/src/app/ApplicationTemplate.Access/ApplicationTemplate.Access.csproj index b86a46fa..ff5fa60c 100644 --- a/src/app/ApplicationTemplate.Access/ApplicationTemplate.Access.csproj +++ b/src/app/ApplicationTemplate.Access/ApplicationTemplate.Access.csproj @@ -4,6 +4,8 @@ net7.0 11.0 ApplicationTemplate.DataAccess + true + true diff --git a/src/app/ApplicationTemplate.Access/Framework/BaseMock.cs b/src/app/ApplicationTemplate.Access/Framework/BaseMock.cs index f0372804..1b06afef 100644 --- a/src/app/ApplicationTemplate.Access/Framework/BaseMock.cs +++ b/src/app/ApplicationTemplate.Access/Framework/BaseMock.cs @@ -28,7 +28,6 @@ public BaseMock(JsonSerializerOptions serializerOptions) /// Deserialized value /// /// If left empty, the will implicitly be treated as "{callerTypeName}.{callerMemberName}.json". - /// If is left empty, the serializer defined in ctor. will be used". /// Note that this will deserialize the first embedded resource whose name ends with the specified . /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Globalization", "CA1307:Specify StringComparison", Justification = "Not available for Desktop")] @@ -62,7 +61,6 @@ protected T GetFromEmbeddedResource( /// /// /// If left empty, the will implicitly be treated as "{callerTypeName}.{callerMemberName}.json". - /// If is left empty, the serializer defined in ctor. will be used". /// Note that this will deserialize the first embedded resource whose name ends with the specified . /// /// Type of object diff --git a/src/app/ApplicationTemplate.Access/Framework/Configuration/IConfiguration.Extensions.cs b/src/app/ApplicationTemplate.Access/Framework/Configuration/IConfiguration.Extensions.cs index 396a53f1..15cbe9e3 100644 --- a/src/app/ApplicationTemplate.Access/Framework/Configuration/IConfiguration.Extensions.cs +++ b/src/app/ApplicationTemplate.Access/Framework/Configuration/IConfiguration.Extensions.cs @@ -14,7 +14,7 @@ public static class ApplicationTemplateConfigurationExtensions { /// /// Returns the for the options represented by . - /// The section name is either the options type name (minus the -Options prefix) or , if provided. + /// The section name is either the options type name (minus the -Options suffix) or , if provided. /// as well. /// /// The options type. @@ -28,7 +28,7 @@ public static IConfigurationSection GetSectionForOptions( /// /// Reads the current value for options. - /// The section name is either the options type name (minus the -Options prefix) or , if provided. + /// The section name is either the options type name (minus the -Options suffix) or , if provided. /// as well. /// /// The options type. @@ -44,19 +44,21 @@ public static T ReadOptions(this IConfiguration configuration, string key = n /// /// Gets the default name for options of type . - /// Removes the -Options prefix if any. + /// Removes the -Options suffix if any. /// + /// The type from which to extract a default options name. public static string DefaultOptionsName(Type optionsType) => DefaultOptionsName(optionsType.Name); /// /// Gets the default name for options of the specified . - /// Removes the -Options prefix if any. + /// Removes the -Options suffix if any. /// + /// The name of the type from which to extract a default options name. public static string DefaultOptionsName(string optionsTypeName) => Regex.Replace(optionsTypeName, @"Options$", string.Empty); /// /// Gets the default name for options of type . - /// Removes the -Options prefix if any. + /// Removes the -Options suffix if any. /// /// The Options type. public static string DefaultOptionsName() => DefaultOptionsName(typeof(T)); diff --git a/src/app/ApplicationTemplate.Access/Framework/Configuration/IServiceCollection.Extensions.cs b/src/app/ApplicationTemplate.Access/Framework/Configuration/IServiceCollection.Extensions.cs index b67f4e5f..1f7c1f80 100644 --- a/src/app/ApplicationTemplate.Access/Framework/Configuration/IServiceCollection.Extensions.cs +++ b/src/app/ApplicationTemplate.Access/Framework/Configuration/IServiceCollection.Extensions.cs @@ -17,7 +17,7 @@ public static class ApplicationTemplateServiceCollectionExtensions { /// /// Registers as an option bound to the - /// using the typename as key (minus the -Options prefix). + /// using the type name as key (minus the -Options suffix). /// The validation, based on Data Annotations, happens when options are retrieved from DI, /// not at the time of registration. /// @@ -26,8 +26,8 @@ public static class ApplicationTemplateServiceCollectionExtensions /// The . /// /// The configuration section key name to use. - /// If not provided, it will be the type name without the -Options prefix. - /// (see . + /// If not provided, it will be the type name without the -Options suffix. + /// (see . /// /// The with services registered. public static IServiceCollection BindOptionsToConfiguration( diff --git a/src/app/ApplicationTemplate.Access/Framework/HttpDebugger/HttpDebuggerHandler.cs b/src/app/ApplicationTemplate.Access/Framework/HttpDebugger/HttpDebuggerHandler.cs index a6d1ba7c..43017db0 100644 --- a/src/app/ApplicationTemplate.Access/Framework/HttpDebugger/HttpDebuggerHandler.cs +++ b/src/app/ApplicationTemplate.Access/Framework/HttpDebugger/HttpDebuggerHandler.cs @@ -34,7 +34,7 @@ protected override async Task SendAsync(HttpRequestMessage ResponseVersion = response.Version, ResponseHeaders = response.Headers, ResponseContentHeaders = response.Content.Headers, - ResponseContent = await response.Content.ReadAsStringAsync(), + ResponseContent = await response.Content.ReadAsStringAsync(CancellationToken.None), }); return response; diff --git a/src/app/ApplicationTemplate.Access/Framework/Serialization/JwtData.cs b/src/app/ApplicationTemplate.Access/Framework/Serialization/JwtData.cs index 6a5fc473..7bf97eca 100644 --- a/src/app/ApplicationTemplate.Access/Framework/Serialization/JwtData.cs +++ b/src/app/ApplicationTemplate.Access/Framework/Serialization/JwtData.cs @@ -18,10 +18,10 @@ public class JwtData private TPayload _payload; /// - /// Initialize a new JWT. + /// Initializes a new instance of the class. /// /// - /// Header & Payload will be deserialized only if a JSON serializer is supplied. + /// Header and Payload will be deserialized only if a JSON serializer is supplied. /// /// The raw token. /// Should be a JSON serializer. Using a serializer for another format won't be RFC 7519 compliant. diff --git a/src/app/ApplicationTemplate.Business/ApplicationTemplate.Business.csproj b/src/app/ApplicationTemplate.Business/ApplicationTemplate.Business.csproj index 655dcb5e..6fc3078c 100644 --- a/src/app/ApplicationTemplate.Business/ApplicationTemplate.Business.csproj +++ b/src/app/ApplicationTemplate.Business/ApplicationTemplate.Business.csproj @@ -2,6 +2,8 @@ net7.0 11.0 + true + true diff --git a/src/app/ApplicationTemplate.Business/Posts/IPostService.cs b/src/app/ApplicationTemplate.Business/Posts/IPostService.cs index 0dfd79ab..ee5aae33 100644 --- a/src/app/ApplicationTemplate.Business/Posts/IPostService.cs +++ b/src/app/ApplicationTemplate.Business/Posts/IPostService.cs @@ -35,8 +35,8 @@ public interface IPostService /// /// /// Post id - /// - /// Updated + /// + /// Updated Task Update(CancellationToken ct, long postId, Post post); /// diff --git a/src/app/ApplicationTemplate.Presentation/ApplicationTemplate.Presentation.csproj b/src/app/ApplicationTemplate.Presentation/ApplicationTemplate.Presentation.csproj index 299a90b3..d85c1e5f 100644 --- a/src/app/ApplicationTemplate.Presentation/ApplicationTemplate.Presentation.csproj +++ b/src/app/ApplicationTemplate.Presentation/ApplicationTemplate.Presentation.csproj @@ -3,6 +3,8 @@ net7.0 11.0 + true + true diff --git a/src/app/ApplicationTemplate.Presentation/Configuration/ApiConfiguration.cs b/src/app/ApplicationTemplate.Presentation/Configuration/ApiConfiguration.cs index ae7d54d7..5b4ca6ea 100644 --- a/src/app/ApplicationTemplate.Presentation/Configuration/ApiConfiguration.cs +++ b/src/app/ApplicationTemplate.Presentation/Configuration/ApiConfiguration.cs @@ -1,11 +1,7 @@ using System; -using System.Collections.Generic; using System.Globalization; using System.Net.Http; -using System.Text; -using System.Threading; using System.Threading.Tasks; -using ApplicationTemplate; using ApplicationTemplate.Business; using ApplicationTemplate.DataAccess; using MallardMessageHandlers; diff --git a/src/app/ApplicationTemplate.Presentation/Configuration/ConfigurationConfiguration.cs b/src/app/ApplicationTemplate.Presentation/Configuration/ConfigurationConfiguration.cs index 9540814b..239ea298 100644 --- a/src/app/ApplicationTemplate.Presentation/Configuration/ConfigurationConfiguration.cs +++ b/src/app/ApplicationTemplate.Presentation/Configuration/ConfigurationConfiguration.cs @@ -56,6 +56,7 @@ public static IHostBuilder AddConfiguration(this IHostBuilder hostBuilder, strin /// /// The configuration builder. /// The folder containing the configuration override files. + /// The environment manager. private static IConfigurationBuilder AddReadOnlyConfiguration(this IConfigurationBuilder configurationBuilder, string folderPath, IEnvironmentManager environmentManager) { return configurationBuilder.AddInMemoryCollection(GetCodeConfiguration(folderPath)); @@ -91,6 +92,7 @@ private static IConfigurationBuilder AddBaseConfiguration(this IConfigurationBui /// The environment can be overriden by the user. /// /// The configuration builder. + /// The environment manager. private static IConfigurationBuilder AddEnvironmentConfiguration(this IConfigurationBuilder configurationBuilder, IEnvironmentManager environmentManager) { var currentEnvironment = environmentManager.Current; @@ -119,6 +121,7 @@ private static IConfigurationBuilder AddUserOverrideConfiguration(this IConfigur /// Registers the as a singleton. /// /// The host builder. + /// The environment manager. private static IHostBuilder AddConfiguration(this IHostBuilder hostBuilder, IEnvironmentManager environmentManager) { if (hostBuilder is null) @@ -190,7 +193,7 @@ public static AppSettingsFile[] GetAll() _appSettingsFiles = executingAssembly .GetManifestResourceNames() - .Where(fileName => fileName.ToUpperInvariant().Contains(AppSettingsFileName.ToUpperInvariant())) + .Where(fileName => fileName.ToUpperInvariant().Contains(AppSettingsFileName.ToUpperInvariant(), StringComparison.Ordinal)) .Select(fileName => new AppSettingsFile(fileName, executingAssembly)) .ToArray(); } diff --git a/src/app/ApplicationTemplate.Presentation/Configuration/ReviewConfiguration.cs b/src/app/ApplicationTemplate.Presentation/Configuration/ReviewConfiguration.cs index 68a072d9..aa537c2d 100644 --- a/src/app/ApplicationTemplate.Presentation/Configuration/ReviewConfiguration.cs +++ b/src/app/ApplicationTemplate.Presentation/Configuration/ReviewConfiguration.cs @@ -37,6 +37,7 @@ public static IServiceCollection AddReviewServices(this IServiceCollection servi .AddSingleton(); } + [System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1812:Avoid uninstantiated internal classes", Justification = "The constructor is invoked by DI.")] private sealed class ReviewServiceAdapter : IReviewService { private readonly IReviewService _reviewService; diff --git a/src/app/ApplicationTemplate.Presentation/Framework/Connectivity/MockedConnectivityProvider.cs b/src/app/ApplicationTemplate.Presentation/Framework/Connectivity/MockedConnectivityProvider.cs index 4daf8af9..3d727bf1 100644 --- a/src/app/ApplicationTemplate.Presentation/Framework/Connectivity/MockedConnectivityProvider.cs +++ b/src/app/ApplicationTemplate.Presentation/Framework/Connectivity/MockedConnectivityProvider.cs @@ -29,6 +29,6 @@ public NetworkAccess NetworkAccess ConnectivityChanged?.Invoke(this, new ConnectivityChangedEventArgs(value)); } } - + public event EventHandler ConnectivityChanged; } diff --git a/src/app/ApplicationTemplate.Presentation/Framework/DataLoader/NetworkReconnectionDataLoaderTrigger.cs b/src/app/ApplicationTemplate.Presentation/Framework/DataLoader/NetworkReconnectionDataLoaderTrigger.cs index 68ccaa7c..e6e66634 100644 --- a/src/app/ApplicationTemplate.Presentation/Framework/DataLoader/NetworkReconnectionDataLoaderTrigger.cs +++ b/src/app/ApplicationTemplate.Presentation/Framework/DataLoader/NetworkReconnectionDataLoaderTrigger.cs @@ -22,9 +22,11 @@ public NetworkReconnectionDataLoaderTrigger(IDataLoader dataLoader, IConnectivit _connectivity.ConnectivityChanged += OnConnectivityChanged; } + /// + /// We should only refresh when state is AND network access is . + /// private void OnConnectivityChanged(object sender, ConnectivityChangedEventArgs e) { - /// Should only refresh when state is AND network access is . if (_dataLoader.State.Error is NoNetworkException && e.NetworkAccess == NetworkAccess.Internet) { diff --git a/src/app/ApplicationTemplate.Presentation/Framework/Startup/StartupBase.cs b/src/app/ApplicationTemplate.Presentation/Framework/Startup/StartupBase.cs index f17e8e62..cd51d31d 100644 --- a/src/app/ApplicationTemplate.Presentation/Framework/Startup/StartupBase.cs +++ b/src/app/ApplicationTemplate.Presentation/Framework/Startup/StartupBase.cs @@ -10,7 +10,7 @@ namespace ApplicationTemplate; /// /// This class abstracts the startup of the actual app (UWP, iOS, Android and not test projects). /// This abstract class is responsible for building the host of the application as well as startup diagnostics. -/// The implementator class is responsible for the application-specific code that initializes the application's services. +/// The implementer class is responsible for the application-specific code that initializes the application's services. /// public abstract class StartupBase { @@ -61,6 +61,8 @@ public void PreInitialize() /// Initializes the application. /// /// Specifies the content root directory to be used by the host. + /// The folder path indicating where the override files are. + /// The delegate to call to configure logging. public void Initialize(string contentRootPath, string settingsFolderPath, LoggingConfigurator­­­ loggingConfigurator) { if (State.IsInitialized) diff --git a/src/app/ApplicationTemplate.Presentation/Framework/Version/VersionProviderExtensions.cs b/src/app/ApplicationTemplate.Presentation/Framework/Version/VersionProviderExtensions.cs index 6455646f..e9186769 100644 --- a/src/app/ApplicationTemplate.Presentation/Framework/Version/VersionProviderExtensions.cs +++ b/src/app/ApplicationTemplate.Presentation/Framework/Version/VersionProviderExtensions.cs @@ -8,7 +8,7 @@ public static class VersionProviderExtensions /// /// Gets the full version string (Major.Minor.Build (Revision)). /// - /// . + /// The version provider. /// The full version string (Major.Minor.Build (Revision)). public static string GetFullVersionString(this IVersionProvider versionProvider) { diff --git a/src/app/ApplicationTemplate.Presentation/Framework/ViewModels/ValueChangedOnBackgroundTaskDynamicPropertyFromDynamicProperty.cs b/src/app/ApplicationTemplate.Presentation/Framework/ViewModels/ValueChangedOnBackgroundTaskDynamicPropertyFromDynamicProperty.cs index e3cd0786..e708ad1f 100644 --- a/src/app/ApplicationTemplate.Presentation/Framework/ViewModels/ValueChangedOnBackgroundTaskDynamicPropertyFromDynamicProperty.cs +++ b/src/app/ApplicationTemplate.Presentation/Framework/ViewModels/ValueChangedOnBackgroundTaskDynamicPropertyFromDynamicProperty.cs @@ -118,7 +118,7 @@ public ValueChangedOnBackgroundTaskDynamicPropertyFromDynamicProperty(string nam public class ValueChangedOnBackgroundTaskDynamicPropertyFromDynamicProperty : ValueChangedOnBackgroundTaskDynamicPropertyFromDynamicProperty, IDynamicProperty { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The name of the this property. /// Source. diff --git a/src/app/ApplicationTemplate.Presentation/ViewModels/Diagnostics/Configuration/ConfigurationDebuggerViewModel.cs b/src/app/ApplicationTemplate.Presentation/ViewModels/Diagnostics/Configuration/ConfigurationDebuggerViewModel.cs index 40ad395d..f399b796 100644 --- a/src/app/ApplicationTemplate.Presentation/ViewModels/Diagnostics/Configuration/ConfigurationDebuggerViewModel.cs +++ b/src/app/ApplicationTemplate.Presentation/ViewModels/Diagnostics/Configuration/ConfigurationDebuggerViewModel.cs @@ -194,6 +194,7 @@ private static string ToJsonString(IConfiguration configuration) return JsonSerializer.Serialize(result, _jsonOptions); } + [System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1851:Possible multiple enumerations of 'IEnumerable' collection", Justification = "It's fine because it's an Any() before the real enumeration.")] private static object GetConfigurationValue(IConfigurationSection section) { var children = section.GetChildren(); @@ -240,7 +241,7 @@ static IEnumerable GetIgnoredPrefixes() private static List GetKeysFromOptionsTypes() { - var optionsTypes = GetAssemblies().SelectMany(a => a.GetTypes().Where(t => t.Name.EndsWith("Options") && !t.IsAbstract && t.IsClass)).ToArray(); + var optionsTypes = GetAssemblies().SelectMany(a => a.GetTypes().Where(t => t.Name.EndsWith("Options", StringComparison.Ordinal) && !t.IsAbstract && t.IsClass)).ToArray(); var keys = new List(); foreach (var optionsType in optionsTypes) @@ -313,7 +314,7 @@ static void RemoveTopLevelKeys(List keys) for (int j = i + 1; j < keys.Count; j++) { var next = keys[j]; - if (next.StartsWith(current)) + if (next.StartsWith(current, StringComparison.Ordinal)) { keys.RemoveAt(i); --i; diff --git a/src/app/ApplicationTemplate.Presentation/ViewModels/Diagnostics/DiagnosticsOverlayViewModel.cs b/src/app/ApplicationTemplate.Presentation/ViewModels/Diagnostics/DiagnosticsOverlayViewModel.cs index e9fcb3f7..2c27a8c5 100644 --- a/src/app/ApplicationTemplate.Presentation/ViewModels/Diagnostics/DiagnosticsOverlayViewModel.cs +++ b/src/app/ApplicationTemplate.Presentation/ViewModels/Diagnostics/DiagnosticsOverlayViewModel.cs @@ -1,5 +1,6 @@ using System; using System.Diagnostics; +using System.Globalization; using System.Reactive.Linq; using ByteSizeLib; using Chinook.DynamicMvvm; @@ -161,12 +162,12 @@ private IObservable ObserveOverlayState() .Select(_ => DiagnosticsCountersService.Counters); public string PrivateMemorySize => this.GetFromObservable( - source: _memoryProvider.ObservePrivateMemorySize().Select(x => ByteSize.FromBytes(x).ToString("0.#")), + source: _memoryProvider.ObservePrivateMemorySize().Select(x => ByteSize.FromBytes(x).ToString("0.#", CultureInfo.InvariantCulture)), initialValue: string.Empty ); public string ManagedMemorySize => this.GetFromObservable( - source: _memoryProvider.ObserveManagedMemorySize().Select(x => ByteSize.FromBytes(x).ToString("0.#")), + source: _memoryProvider.ObserveManagedMemorySize().Select(x => ByteSize.FromBytes(x).ToString("0.#", CultureInfo.InvariantCulture)), initialValue: string.Empty ); } diff --git a/src/app/ApplicationTemplate.Presentation/ViewModels/Diagnostics/SummaryDiagnosticsViewModel.cs b/src/app/ApplicationTemplate.Presentation/ViewModels/Diagnostics/SummaryDiagnosticsViewModel.cs index 011040e9..9b1fff9a 100644 --- a/src/app/ApplicationTemplate.Presentation/ViewModels/Diagnostics/SummaryDiagnosticsViewModel.cs +++ b/src/app/ApplicationTemplate.Presentation/ViewModels/Diagnostics/SummaryDiagnosticsViewModel.cs @@ -9,6 +9,7 @@ namespace ApplicationTemplate.Presentation; +[System.Diagnostics.CodeAnalysis.SuppressMessage("Globalization", "CA1305:Specify IFormatProvider", Justification = "Default behavior is acceptable for diagnostics.")] public partial class SummaryDiagnosticsViewModel : ViewModel { private readonly DateTimeOffset _now = DateTimeOffset.Now; diff --git a/src/app/ApplicationTemplate.Presentation/ViewModels/Posts/PostsPageViewModel.cs b/src/app/ApplicationTemplate.Presentation/ViewModels/Posts/PostsPageViewModel.cs index dd16211f..e6e2fe2c 100644 --- a/src/app/ApplicationTemplate.Presentation/ViewModels/Posts/PostsPageViewModel.cs +++ b/src/app/ApplicationTemplate.Presentation/ViewModels/Posts/PostsPageViewModel.cs @@ -13,6 +13,7 @@ public partial class PostsPageViewModel : ViewModel { private readonly Func _onGetPostsCalled; + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2213:Disposable fields should be disposed", Justification = "It will be disposed by the DataLoader when passed via WithTrigger.")] private readonly ManualDataLoaderTrigger _deletePostTrigger = new(); public PostsPageViewModel(Func onGetPostsCalled = null) diff --git a/src/app/ApplicationTemplate.Presentation/ViewModels/Settings/LicensesPageViewModel.cs b/src/app/ApplicationTemplate.Presentation/ViewModels/Settings/LicensesPageViewModel.cs index a8ce23f9..1d94738e 100644 --- a/src/app/ApplicationTemplate.Presentation/ViewModels/Settings/LicensesPageViewModel.cs +++ b/src/app/ApplicationTemplate.Presentation/ViewModels/Settings/LicensesPageViewModel.cs @@ -43,7 +43,7 @@ private async Task GetLicenses(CancellationToken ct) using (var streamReader = new StreamReader(resourceStream)) { - return await streamReader.ReadToEndAsync(); + return await streamReader.ReadToEndAsync(ct); } } } diff --git a/src/app/ApplicationTemplate.Shared.Views/Behaviors/FormattingTextBoxBehavior.Android.cs b/src/app/ApplicationTemplate.Shared.Views/Behaviors/FormattingTextBoxBehavior.Android.cs index a07c7161..cf8cffa2 100644 --- a/src/app/ApplicationTemplate.Shared.Views/Behaviors/FormattingTextBoxBehavior.Android.cs +++ b/src/app/ApplicationTemplate.Shared.Views/Behaviors/FormattingTextBoxBehavior.Android.cs @@ -91,7 +91,7 @@ private static void MaterializeAllTemplates(DependencyObject view) while (control != null); } - private class FormattingTextInputFilter : Java.Lang.Object, Android.Text.IInputFilter + private sealed class FormattingTextInputFilter : Java.Lang.Object, Android.Text.IInputFilter { private string _stringFormat; private TextBox _targetTextBox; diff --git a/src/app/ApplicationTemplate.Shared.Views/Behaviors/FormattingTextBoxBehavior.iOS.cs b/src/app/ApplicationTemplate.Shared.Views/Behaviors/FormattingTextBoxBehavior.iOS.cs index 98443897..119f826e 100644 --- a/src/app/ApplicationTemplate.Shared.Views/Behaviors/FormattingTextBoxBehavior.iOS.cs +++ b/src/app/ApplicationTemplate.Shared.Views/Behaviors/FormattingTextBoxBehavior.iOS.cs @@ -74,6 +74,7 @@ private static void UpdateDelegateImpl(TextBox textbox, bool? enabled = null, st } } + [System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "CA1711:Identifiers should not have incorrect suffix", Justification = "Delegate is fine in this case because it's not referring the delegate construct but rather the base class.")] public class UITextFieldDelegatingDelegate : UITextFieldDelegate { private SinglelineTextBoxView _nativeView; diff --git a/src/app/ApplicationTemplate.Shared.Views/Configuration/LoggingConfiguration.cs b/src/app/ApplicationTemplate.Shared.Views/Configuration/LoggingConfiguration.cs index 597678e9..ab3d9b32 100644 --- a/src/app/ApplicationTemplate.Shared.Views/Configuration/LoggingConfiguration.cs +++ b/src/app/ApplicationTemplate.Shared.Views/Configuration/LoggingConfiguration.cs @@ -58,6 +58,7 @@ public static void ConfigureLogging(HostBuilderContext hostBuilderContext, ILogg #endif } + [System.Diagnostics.CodeAnalysis.SuppressMessage("Globalization", "CA1305:Specify IFormatProvider", Justification = "False positive: These are message template. The brackets are expected.")] private static LoggerConfiguration AddConsoleLogging(LoggerConfiguration configuration) { return configuration @@ -78,6 +79,7 @@ private static LoggerConfiguration AddConsoleLogging(LoggerConfiguration configu //+:cnd:noEmit } + [System.Diagnostics.CodeAnalysis.SuppressMessage("Globalization", "CA1305:Specify IFormatProvider", Justification = "False positive: These are message template. The brackets are expected.")] private static LoggerConfiguration AddFileLogging(LoggerConfiguration configuration, string logFilePath) { //-:cnd:noEmit diff --git a/src/app/ApplicationTemplate.Shared.Views/Configuration/NavigationConfiguration.cs b/src/app/ApplicationTemplate.Shared.Views/Configuration/NavigationConfiguration.cs index 31566794..a6377485 100644 --- a/src/app/ApplicationTemplate.Shared.Views/Configuration/NavigationConfiguration.cs +++ b/src/app/ApplicationTemplate.Shared.Views/Configuration/NavigationConfiguration.cs @@ -26,7 +26,6 @@ public static IServiceCollection AddNavigation(this IServiceCollection services) private static IReadOnlyDictionary GetPageRegistrations() => new Dictionary() { // TODO: Add your ViewModel and Page associations here. - { typeof(WelcomePageViewModel), typeof(WelcomePage) }, { typeof(PostsPageViewModel), typeof(PostsPage) }, { typeof(EditPostPageViewModel), typeof(EditPostPage) }, diff --git a/src/app/ApplicationTemplate.Shared.Views/Controls/AppBarBackButton.cs b/src/app/ApplicationTemplate.Shared.Views/Controls/AppBarBackButton.cs index ec05b10e..18d2a40d 100644 --- a/src/app/ApplicationTemplate.Shared.Views/Controls/AppBarBackButton.cs +++ b/src/app/ApplicationTemplate.Shared.Views/Controls/AppBarBackButton.cs @@ -20,6 +20,7 @@ namespace ApplicationTemplate.Views.Controls; /// public sealed partial class AppBarBackButton : AppBarButton { + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2213:Disposable fields should be disposed", Justification = "It's disposed via OnUnloaded.")] private IDisposable _backButtonVisibilitySubscription; public AppBarBackButton() diff --git a/src/app/ApplicationTemplate.Shared.Views/Framework/DiagnosticsService.cs b/src/app/ApplicationTemplate.Shared.Views/Framework/DiagnosticsService.cs index 7245359e..b6397d61 100644 --- a/src/app/ApplicationTemplate.Shared.Views/Framework/DiagnosticsService.cs +++ b/src/app/ApplicationTemplate.Shared.Views/Framework/DiagnosticsService.cs @@ -82,7 +82,7 @@ public async Task TestExceptionFromMainThread(CancellationToken ct) #if __IOS__ //+:cnd:noEmit /// This will be handled by - UIKit.UIApplication.SharedApplication.InvokeOnMainThread(() => throw new Exception("This is a test of an exception in the MainThread. Please ignore.")); + UIKit.UIApplication.SharedApplication.InvokeOnMainThread(() => throw new InvalidOperationException("This is a test of an exception in the MainThread. Please ignore.")); //-:cnd:noEmit #elif __ANDROID__ //+:cnd:noEmit diff --git a/src/app/ApplicationTemplate.Shared.Views/Helpers/DispatcherQueueExtensions.cs b/src/app/ApplicationTemplate.Shared.Views/Helpers/DispatcherQueueExtensions.cs index 882189c8..1a8d1b15 100644 --- a/src/app/ApplicationTemplate.Shared.Views/Helpers/DispatcherQueueExtensions.cs +++ b/src/app/ApplicationTemplate.Shared.Views/Helpers/DispatcherQueueExtensions.cs @@ -63,6 +63,7 @@ internal static Task RunTaskAsync(this DispatcherQueue dispatcher, DispatcherQue /// The target to invoke the code on. /// The priority level for the function to invoke. /// The to invoke. + /// The return type of the task. /// A that completes when the invocation of is over. /// If the current thread has access to , will be invoked directly. internal static Task RunTaskAsync(this DispatcherQueue dispatcher, DispatcherQueuePriority priority, Func> asyncAction) diff --git a/src/app/ApplicationTemplate.Shared.Views/Helpers/ObservableExtensions.cs b/src/app/ApplicationTemplate.Shared.Views/Helpers/ObservableExtensions.cs index b5c59c25..cfe261f6 100644 --- a/src/app/ApplicationTemplate.Shared.Views/Helpers/ObservableExtensions.cs +++ b/src/app/ApplicationTemplate.Shared.Views/Helpers/ObservableExtensions.cs @@ -42,27 +42,27 @@ internal enum SubscribeToElementOptions : byte internal enum UiEventSubscriptionsOptions { /// - /// Default is ImmediateSubscribe. - /// - Default = ImmediateSubscribe, - - /// - /// Subscribe and Unsubscribe will be enforced on Dispacther scheduler. + /// Subscribe and Unsubscribe will be enforced on Dispatcher scheduler. /// /// Be sure to not miss an event between subscribe to observable and real event handler add. DispatcherOnly = 0, /// - /// Add event handler immediatly on Subscribe. + /// Add event handler immediately on Subscribe. /// /// This mean you must call subscribe on dispatcher. ImmediateSubscribe = 1, /// - /// Remove event handler immediatly on Dispose / Complete. + /// Remove event handler immediately on Dispose / Complete. /// /// This mean you must dispose subscription on dispatcher. ImmediateUnsubscribe = 2, + + /// + /// Default is ImmediateSubscribe. + /// + Default = ImmediateSubscribe, } internal static class ObservableExtensions diff --git a/stylecop.json b/stylecop.json index d22c9c43..87207bae 100644 --- a/stylecop.json +++ b/stylecop.json @@ -1,4 +1,4 @@ -{ +{ "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json", "settings": { "documentationRules": {