diff --git a/src/Npgsql.GeoJSON/Npgsql.GeoJSON.csproj b/src/Npgsql.GeoJSON/Npgsql.GeoJSON.csproj index a802ca5653..072feabea3 100644 --- a/src/Npgsql.GeoJSON/Npgsql.GeoJSON.csproj +++ b/src/Npgsql.GeoJSON/Npgsql.GeoJSON.csproj @@ -5,6 +5,7 @@ npgsql;postgresql;postgres;postgis;geojson;spatial;ado;ado.net;database;sql net6.0 net8.0 + $(NoWarn);NPG9001 diff --git a/src/Npgsql.Json.NET/Npgsql.Json.NET.csproj b/src/Npgsql.Json.NET/Npgsql.Json.NET.csproj index 49707eb02f..67109a48da 100644 --- a/src/Npgsql.Json.NET/Npgsql.Json.NET.csproj +++ b/src/Npgsql.Json.NET/Npgsql.Json.NET.csproj @@ -6,6 +6,7 @@ net6.0 net8.0 enable + $(NoWarn);NPG9001 diff --git a/src/Npgsql.NetTopologySuite/Npgsql.NetTopologySuite.csproj b/src/Npgsql.NetTopologySuite/Npgsql.NetTopologySuite.csproj index fd2342614b..214f4bd72e 100644 --- a/src/Npgsql.NetTopologySuite/Npgsql.NetTopologySuite.csproj +++ b/src/Npgsql.NetTopologySuite/Npgsql.NetTopologySuite.csproj @@ -7,6 +7,7 @@ net6.0 net8.0 $(NoWarn);NU5104 + $(NoWarn);NPG9001 diff --git a/src/Npgsql.NodaTime/Npgsql.NodaTime.csproj b/src/Npgsql.NodaTime/Npgsql.NodaTime.csproj index 4ac9e068fa..3e4d826188 100644 --- a/src/Npgsql.NodaTime/Npgsql.NodaTime.csproj +++ b/src/Npgsql.NodaTime/Npgsql.NodaTime.csproj @@ -6,6 +6,7 @@ README.md net6.0 net8.0 + $(NoWarn);NPG9001 diff --git a/src/Npgsql/Internal/BufferRequirements.cs b/src/Npgsql/Internal/BufferRequirements.cs index cd32c0cbd1..14ffabc52b 100644 --- a/src/Npgsql/Internal/BufferRequirements.cs +++ b/src/Npgsql/Internal/BufferRequirements.cs @@ -1,7 +1,9 @@ using System; +using System.Diagnostics.CodeAnalysis; namespace Npgsql.Internal; +[Experimental(NpgsqlDiagnostics.ConvertersExperimental)] public readonly struct BufferRequirements : IEquatable { readonly Size _read; diff --git a/src/Npgsql/Internal/DataFormat.cs b/src/Npgsql/Internal/DataFormat.cs index c9950ea417..c52b418b7d 100644 --- a/src/Npgsql/Internal/DataFormat.cs +++ b/src/Npgsql/Internal/DataFormat.cs @@ -1,8 +1,10 @@ using System; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; namespace Npgsql.Internal; +[Experimental(NpgsqlDiagnostics.ConvertersExperimental)] public enum DataFormat : byte { Binary, diff --git a/src/Npgsql/Internal/DynamicTypeInfoResolver.cs b/src/Npgsql/Internal/DynamicTypeInfoResolver.cs index 637c337321..421de703f5 100644 --- a/src/Npgsql/Internal/DynamicTypeInfoResolver.cs +++ b/src/Npgsql/Internal/DynamicTypeInfoResolver.cs @@ -6,6 +6,7 @@ namespace Npgsql.Internal; +[Experimental(NpgsqlDiagnostics.ConvertersExperimental)] [RequiresDynamicCode("A dynamic type info resolver may need to construct a generic converter for a statically unknown type.")] public abstract class DynamicTypeInfoResolver : IPgTypeInfoResolver { diff --git a/src/Npgsql/Internal/HackyEnumTypeMapping.cs b/src/Npgsql/Internal/HackyEnumTypeMapping.cs index 1aa4b27554..8b3d5255cc 100644 --- a/src/Npgsql/Internal/HackyEnumTypeMapping.cs +++ b/src/Npgsql/Internal/HackyEnumTypeMapping.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; using Npgsql.Internal; @@ -8,10 +9,10 @@ namespace Npgsql.Internal; - /// /// Hacky temporary measure used by EFCore.PG to extract user-configured enum mappings. Accessed via reflection only. /// +[Experimental(NpgsqlDiagnostics.ConvertersExperimental)] public sealed class HackyEnumTypeMapping { public HackyEnumTypeMapping(Type enumClrType, string pgTypeName, INpgsqlNameTranslator nameTranslator) diff --git a/src/Npgsql/Internal/INpgsqlDatabaseInfoFactory.cs b/src/Npgsql/Internal/INpgsqlDatabaseInfoFactory.cs index ccdb7a8477..ea3f0ad525 100644 --- a/src/Npgsql/Internal/INpgsqlDatabaseInfoFactory.cs +++ b/src/Npgsql/Internal/INpgsqlDatabaseInfoFactory.cs @@ -1,4 +1,5 @@ -using System.Threading.Tasks; +using System.Diagnostics.CodeAnalysis; +using System.Threading.Tasks; using Npgsql.Util; namespace Npgsql.Internal; @@ -8,6 +9,7 @@ namespace Npgsql.Internal; /// and the types it contains. When first connecting to a database, Npgsql will attempt to load information /// about it via this factory. /// +[Experimental(NpgsqlDiagnostics.ConvertersExperimental)] public interface INpgsqlDatabaseInfoFactory { /// @@ -19,4 +21,4 @@ public interface INpgsqlDatabaseInfoFactory /// database isn't of the correct type and isn't handled by this factory. /// Task Load(NpgsqlConnector conn, NpgsqlTimeout timeout, bool async); -} \ No newline at end of file +} diff --git a/src/Npgsql/Internal/IPgTypeInfoResolver.cs b/src/Npgsql/Internal/IPgTypeInfoResolver.cs index 62955446eb..b7b3ddc9ec 100644 --- a/src/Npgsql/Internal/IPgTypeInfoResolver.cs +++ b/src/Npgsql/Internal/IPgTypeInfoResolver.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics.CodeAnalysis; using Npgsql.Internal.Postgres; namespace Npgsql.Internal; @@ -6,6 +7,7 @@ namespace Npgsql.Internal; /// /// An Npgsql resolver for type info. Used by Npgsql to read and write values to PostgreSQL. /// +[Experimental(NpgsqlDiagnostics.ConvertersExperimental)] public interface IPgTypeInfoResolver { /// diff --git a/src/Npgsql/Internal/NpgsqlConnector.cs b/src/Npgsql/Internal/NpgsqlConnector.cs index c3726180a1..adfa7cb5d8 100644 --- a/src/Npgsql/Internal/NpgsqlConnector.cs +++ b/src/Npgsql/Internal/NpgsqlConnector.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Data; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Net; using System.Net.Security; @@ -29,6 +30,7 @@ namespace Npgsql.Internal; /// Represents a connection to a PostgreSQL backend. Unlike NpgsqlConnection objects, which are /// exposed to users, connectors are internal to Npgsql and are recycled by the connection pool. /// +[Experimental(NpgsqlDiagnostics.ConvertersExperimental)] public sealed partial class NpgsqlConnector { #region Fields and Properties diff --git a/src/Npgsql/Internal/NpgsqlDatabaseInfo.cs b/src/Npgsql/Internal/NpgsqlDatabaseInfo.cs index fed3f8c165..7fd3fe95e9 100644 --- a/src/Npgsql/Internal/NpgsqlDatabaseInfo.cs +++ b/src/Npgsql/Internal/NpgsqlDatabaseInfo.cs @@ -12,6 +12,7 @@ namespace Npgsql.Internal; /// Base class for implementations which provide information about PostgreSQL and PostgreSQL-like databases /// (e.g. type definitions, capabilities...). /// +[Experimental(NpgsqlDiagnostics.DatabaseInfoExperimental)] public abstract class NpgsqlDatabaseInfo { #region Fields diff --git a/src/Npgsql/Internal/NpgsqlReadBuffer.cs b/src/Npgsql/Internal/NpgsqlReadBuffer.cs index 6cfa68e16a..139594e25a 100644 --- a/src/Npgsql/Internal/NpgsqlReadBuffer.cs +++ b/src/Npgsql/Internal/NpgsqlReadBuffer.cs @@ -2,6 +2,7 @@ using System.Buffers; using System.Buffers.Binary; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Net.Sockets; using System.Runtime.CompilerServices; @@ -17,6 +18,7 @@ namespace Npgsql.Internal; /// A buffer used by Npgsql to read data from the socket efficiently. /// Provides methods which decode different values types and tracks the current position. /// +[Experimental(NpgsqlDiagnostics.ConvertersExperimental)] sealed partial class NpgsqlReadBuffer : IDisposable { #region Fields and Properties diff --git a/src/Npgsql/Internal/PgBufferedConverter.cs b/src/Npgsql/Internal/PgBufferedConverter.cs index 2bed7ffa3c..d7b673fb7c 100644 --- a/src/Npgsql/Internal/PgBufferedConverter.cs +++ b/src/Npgsql/Internal/PgBufferedConverter.cs @@ -5,6 +5,7 @@ namespace Npgsql.Internal; +[Experimental(NpgsqlDiagnostics.ConvertersExperimental)] public abstract class PgBufferedConverter : PgConverter { protected PgBufferedConverter(bool customDbNullPredicate = false) : base(customDbNullPredicate) { } diff --git a/src/Npgsql/Internal/PgConverter.cs b/src/Npgsql/Internal/PgConverter.cs index 462030f2b7..323c572e0a 100644 --- a/src/Npgsql/Internal/PgConverter.cs +++ b/src/Npgsql/Internal/PgConverter.cs @@ -8,6 +8,7 @@ namespace Npgsql.Internal; +[Experimental(NpgsqlDiagnostics.ConvertersExperimental)] public abstract class PgConverter { internal DbNullPredicate DbNullPredicateKind { get; } diff --git a/src/Npgsql/Internal/PgConverterResolver.cs b/src/Npgsql/Internal/PgConverterResolver.cs index baee09d58e..5fbe699017 100644 --- a/src/Npgsql/Internal/PgConverterResolver.cs +++ b/src/Npgsql/Internal/PgConverterResolver.cs @@ -1,8 +1,10 @@ using System; +using System.Diagnostics.CodeAnalysis; using Npgsql.Internal.Postgres; namespace Npgsql.Internal; +[Experimental(NpgsqlDiagnostics.ConvertersExperimental)] public abstract class PgConverterResolver { private protected PgConverterResolver() { } diff --git a/src/Npgsql/Internal/PgReader.cs b/src/Npgsql/Internal/PgReader.cs index d3bd7d3260..90f5b53e14 100644 --- a/src/Npgsql/Internal/PgReader.cs +++ b/src/Npgsql/Internal/PgReader.cs @@ -10,6 +10,7 @@ namespace Npgsql.Internal; +[Experimental(NpgsqlDiagnostics.ConvertersExperimental)] public class PgReader { // We don't want to add a ton of memory pressure for large strings. diff --git a/src/Npgsql/Internal/PgSerializerOptions.cs b/src/Npgsql/Internal/PgSerializerOptions.cs index 193e193826..b79b5757ec 100644 --- a/src/Npgsql/Internal/PgSerializerOptions.cs +++ b/src/Npgsql/Internal/PgSerializerOptions.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Runtime.CompilerServices; using System.Text; @@ -8,6 +9,7 @@ namespace Npgsql.Internal; +[Experimental(NpgsqlDiagnostics.ConvertersExperimental)] public sealed class PgSerializerOptions { /// diff --git a/src/Npgsql/Internal/PgStreamingConverter.cs b/src/Npgsql/Internal/PgStreamingConverter.cs index 3d69889b3b..f0a32156d3 100644 --- a/src/Npgsql/Internal/PgStreamingConverter.cs +++ b/src/Npgsql/Internal/PgStreamingConverter.cs @@ -1,11 +1,13 @@ using System; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; namespace Npgsql.Internal; +[Experimental(NpgsqlDiagnostics.ConvertersExperimental)] public abstract class PgStreamingConverter : PgConverter { protected PgStreamingConverter(bool customDbNullPredicate = false) : base(customDbNullPredicate) { } diff --git a/src/Npgsql/Internal/PgTypeInfo.cs b/src/Npgsql/Internal/PgTypeInfo.cs index a949ee8db5..d83c5dfa36 100644 --- a/src/Npgsql/Internal/PgTypeInfo.cs +++ b/src/Npgsql/Internal/PgTypeInfo.cs @@ -4,6 +4,7 @@ namespace Npgsql.Internal; +[Experimental(NpgsqlDiagnostics.ConvertersExperimental)] public class PgTypeInfo { readonly bool _canBinaryConvert; diff --git a/src/Npgsql/Internal/PgTypeInfoResolverFactory.cs b/src/Npgsql/Internal/PgTypeInfoResolverFactory.cs index f30059c7ec..9392e2c840 100644 --- a/src/Npgsql/Internal/PgTypeInfoResolverFactory.cs +++ b/src/Npgsql/Internal/PgTypeInfoResolverFactory.cs @@ -1,5 +1,8 @@ +using System.Diagnostics.CodeAnalysis; + namespace Npgsql.Internal; +[Experimental(NpgsqlDiagnostics.ConvertersExperimental)] public abstract class PgTypeInfoResolverFactory { public abstract IPgTypeInfoResolver CreateResolver(); diff --git a/src/Npgsql/Internal/PgWriter.cs b/src/Npgsql/Internal/PgWriter.cs index 3c5064386c..6fe6ed0e4c 100644 --- a/src/Npgsql/Internal/PgWriter.cs +++ b/src/Npgsql/Internal/PgWriter.cs @@ -2,6 +2,7 @@ using System.Buffers; using System.Buffers.Binary; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -92,6 +93,7 @@ public ValueTask FlushAsync(CancellationToken cancellationToken = default) => new(_buffer.Flush(async: true, cancellationToken)); } +[Experimental(NpgsqlDiagnostics.ConvertersExperimental)] public sealed class PgWriter { readonly IBufferWriter _writer; @@ -557,6 +559,7 @@ public override long Seek(long offset, SeekOrigin origin) } // No-op for now. +[Experimental(NpgsqlDiagnostics.ConvertersExperimental)] public struct NestedWriteScope : IDisposable { public void Dispose() diff --git a/src/Npgsql/Internal/Postgres/DataTypeName.cs b/src/Npgsql/Internal/Postgres/DataTypeName.cs index d20e479f85..c5b223f866 100644 --- a/src/Npgsql/Internal/Postgres/DataTypeName.cs +++ b/src/Npgsql/Internal/Postgres/DataTypeName.cs @@ -1,11 +1,13 @@ using System; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; namespace Npgsql.Internal.Postgres; /// /// Represents the fully-qualified name of a PostgreSQL type. /// +[Experimental(NpgsqlDiagnostics.ConvertersExperimental)] [DebuggerDisplay("{DisplayName,nq}")] public readonly struct DataTypeName : IEquatable { diff --git a/src/Npgsql/Internal/Postgres/Field.cs b/src/Npgsql/Internal/Postgres/Field.cs index f6a261c103..cb2879f998 100644 --- a/src/Npgsql/Internal/Postgres/Field.cs +++ b/src/Npgsql/Internal/Postgres/Field.cs @@ -1,6 +1,9 @@ +using System.Diagnostics.CodeAnalysis; + namespace Npgsql.Internal.Postgres; /// Base field type shared between tables and composites. +[Experimental(NpgsqlDiagnostics.ConvertersExperimental)] public readonly struct Field { public Field(string name, PgTypeId pgTypeId, int typeModifier) diff --git a/src/Npgsql/Internal/Postgres/Oid.cs b/src/Npgsql/Internal/Postgres/Oid.cs index e6fcad6f4a..55ede288fe 100644 --- a/src/Npgsql/Internal/Postgres/Oid.cs +++ b/src/Npgsql/Internal/Postgres/Oid.cs @@ -1,7 +1,9 @@ using System; +using System.Diagnostics.CodeAnalysis; namespace Npgsql.Internal.Postgres; +[Experimental(NpgsqlDiagnostics.ConvertersExperimental)] public readonly struct Oid: IEquatable { public Oid(uint value) => Value = value; diff --git a/src/Npgsql/Internal/Postgres/PgTypeId.cs b/src/Npgsql/Internal/Postgres/PgTypeId.cs index c5a40d22ca..ee5ffb9d41 100644 --- a/src/Npgsql/Internal/Postgres/PgTypeId.cs +++ b/src/Npgsql/Internal/Postgres/PgTypeId.cs @@ -6,6 +6,7 @@ namespace Npgsql.Internal.Postgres; /// /// A discriminated union of and . /// +[Experimental(NpgsqlDiagnostics.ConvertersExperimental)] public readonly struct PgTypeId: IEquatable { readonly DataTypeName _dataTypeName; diff --git a/src/Npgsql/Internal/Size.cs b/src/Npgsql/Internal/Size.cs index 7f5e52a1f1..7cbdd9bb20 100644 --- a/src/Npgsql/Internal/Size.cs +++ b/src/Npgsql/Internal/Size.cs @@ -1,8 +1,10 @@ using System; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; namespace Npgsql.Internal; +[Experimental(NpgsqlDiagnostics.ConvertersExperimental)] public enum SizeKind { Unknown = 0, @@ -10,6 +12,7 @@ public enum SizeKind UpperBound } +[Experimental(NpgsqlDiagnostics.ConvertersExperimental)] [DebuggerDisplay("{DebuggerDisplay,nq}")] public readonly struct Size : IEquatable { diff --git a/src/Npgsql/Internal/TypeInfoMapping.cs b/src/Npgsql/Internal/TypeInfoMapping.cs index 00b9ba18ee..753c2bcac3 100644 --- a/src/Npgsql/Internal/TypeInfoMapping.cs +++ b/src/Npgsql/Internal/TypeInfoMapping.cs @@ -19,8 +19,10 @@ namespace Npgsql.Internal; /// /// Signals whether a resolver based TypeInfo can keep its PgTypeId undecided or whether it should follow mapping.DataTypeName. /// +[Experimental(NpgsqlDiagnostics.ConvertersExperimental)] public delegate PgTypeInfo TypeInfoFactory(PgSerializerOptions options, TypeInfoMapping mapping, bool resolvedDataTypeName); +[Experimental(NpgsqlDiagnostics.ConvertersExperimental)] public enum MatchRequirement { /// Match when the clr type and datatype name both match. @@ -33,6 +35,7 @@ public enum MatchRequirement } /// A factory for well-known PgConverters. +[Experimental(NpgsqlDiagnostics.ConvertersExperimental)] public static class PgConverterFactory { public static PgConverter CreateArrayMultirangeConverter(PgConverter rangeConverter, PgSerializerOptions options) where T : notnull @@ -55,6 +58,7 @@ public static PgConverter CreatePolymorphicArrayConverter(Func throw new InvalidOperationException($"Boxing converters are not supported, manually construct a mapping over a casting converter{(resolver ? " resolver" : "")} instead."); } +[Experimental(NpgsqlDiagnostics.ConvertersExperimental)] public static class TypeInfoMappingHelpers { internal static bool TryResolveFullyQualifiedName(PgSerializerOptions options, string dataTypeName, out DataTypeName fqDataTypeName) diff --git a/src/Npgsql/Internal/ValueMetadata.cs b/src/Npgsql/Internal/ValueMetadata.cs index ff041a3060..b71028c4a1 100644 --- a/src/Npgsql/Internal/ValueMetadata.cs +++ b/src/Npgsql/Internal/ValueMetadata.cs @@ -1,5 +1,8 @@ +using System.Diagnostics.CodeAnalysis; + namespace Npgsql.Internal; +[Experimental(NpgsqlDiagnostics.ConvertersExperimental)] public readonly struct ValueMetadata { public required DataFormat Format { get; init; } diff --git a/src/Npgsql/Npgsql.csproj b/src/Npgsql/Npgsql.csproj index ecae24940a..77b65d47b2 100644 --- a/src/Npgsql/Npgsql.csproj +++ b/src/Npgsql/Npgsql.csproj @@ -8,6 +8,8 @@ net6.0;net8.0 net8.0 $(NoWarn);CA2017 + $(NoWarn);NPG9001 + $(NoWarn);NPG9002 diff --git a/src/Npgsql/NpgsqlDiagnostics.cs b/src/Npgsql/NpgsqlDiagnostics.cs new file mode 100644 index 0000000000..2037fec667 --- /dev/null +++ b/src/Npgsql/NpgsqlDiagnostics.cs @@ -0,0 +1,7 @@ +namespace Npgsql; + +static class NpgsqlDiagnostics +{ + public const string ConvertersExperimental = "NPG9001"; + public const string DatabaseInfoExperimental = "NPG9002"; +} diff --git a/src/Npgsql/Shims/ExperimentalAttribute.cs b/src/Npgsql/Shims/ExperimentalAttribute.cs new file mode 100644 index 0000000000..36ff9ee11d --- /dev/null +++ b/src/Npgsql/Shims/ExperimentalAttribute.cs @@ -0,0 +1,21 @@ +#if !NET8_0_OR_GREATER +namespace System.Diagnostics.CodeAnalysis; + +/// Indicates that an API is experimental and it may change in the future. +[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Module | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Interface | AttributeTargets.Delegate, Inherited = false)] +public sealed class ExperimentalAttribute : Attribute +{ + /// Initializes a new instance of the class, specifying the ID that the compiler will use when reporting a use of the API the attribute applies to. + /// The ID that the compiler will use when reporting a use of the API the attribute applies to. + public ExperimentalAttribute(string diagnosticId) => this.DiagnosticId = diagnosticId; + + /// Gets the ID that the compiler will use when reporting a use of the API the attribute applies to. + /// The unique diagnostic ID. + public string DiagnosticId { get; } + + /// Gets or sets the URL for corresponding documentation. + /// The API accepts a format string instead of an actual URL, creating a generic URL that includes the diagnostic ID. + /// The format string that represents a URL to corresponding documentation. + public string? UrlFormat { get; set; } +} +#endif diff --git a/test/Npgsql.Benchmarks/Npgsql.Benchmarks.csproj b/test/Npgsql.Benchmarks/Npgsql.Benchmarks.csproj index bc51b25561..922d4cbdce 100644 --- a/test/Npgsql.Benchmarks/Npgsql.Benchmarks.csproj +++ b/test/Npgsql.Benchmarks/Npgsql.Benchmarks.csproj @@ -4,6 +4,7 @@ portable Npgsql.Benchmarks Exe + $(NoWarn);NPG9001 diff --git a/test/Npgsql.Tests/Npgsql.Tests.csproj b/test/Npgsql.Tests/Npgsql.Tests.csproj index 980b51d8aa..6b7baca8ad 100644 --- a/test/Npgsql.Tests/Npgsql.Tests.csproj +++ b/test/Npgsql.Tests/Npgsql.Tests.csproj @@ -12,5 +12,7 @@ true + $(NoWarn);NPG9001 + $(NoWarn);NPG9002