Tài liệu

Mã chẩn đoán

Thời gian đọc 15 phút Gói: Nalix.Analyzers
Trên trang này

Trang này liệt kê tất cả 75 mô tả chẩn đoán đang hoạt động do Nalix.Analyzers (67 quy tắc trong analyzers/Nalix.Analyzers/Diagnostics/DiagnosticDescriptors.cs) và Nalix.Analyzers.Generators (8 quy tắc trong analyzers/Nalix.Analyzers.Generators/Internal/GeneratorDiagnosticDescriptors.cs) phát ra.

1. Điều phối controller và đăng ký gói tin (NALIX001–NALIX018, NALIX047–NALIX058)

IDTiêu đềMức độDanh mụcCode FixMô tả
NALIX001Controller gói tin chứa PacketOpcode bị trùngCảnh báoUsageKhôngĐiều phối controller dựa vào opcode duy nhất trong mỗi controller.
NALIX002Handler của controller gói tin nên khai báo PacketOpcodeCảnh báoUsageCóCác method controller khớp mẫu handler của Nalix nên khai báo [PacketOpcode(...)].
NALIX003Handler của controller gói tin có chữ ký không được hỗ trợCảnh báoUsageKhôngTham số 0 phải là IPacketContext<TPacket>. Mọi tham số bổ sung (1..N) phải là kiểu tham chiếu được đánh dấu [FromScope] (xem NALIX067).
NALIX004PacketContext<T> không khớp kiểu gói tin của dispatchCảnh báoUsageKhôngPacketContext<T> nên dùng kiểu gói tin có thể gán cho TPacket của dispatcher.
NALIX005Kiểu gói tin của handler controller đã đăng ký không khớp kiểu của dispatcherCảnh báoUsageKhôngCác controller đăng ký trên PacketDispatchOptions<TPacket> phải xử lý kiểu gói tin tương thích.
NALIX008Controller handler đã đăng ký thiếu PacketHandlerCảnh báoUsageCóCác kiểu đăng ký qua WithHandler hoặc MapHandlers phải khai báo [PacketHandler].
NALIX009Kiểu gói tin đã đăng ký thiếu Deserialize tĩnhCảnh báoUsageCóViệc gắn kết với packet registry cần hỗ trợ IPacketDeserializer<T> / Deserialize(ReadOnlySpan<byte>) tĩnh.
NALIX010PacketBase<TSelf> nên dùng lớp gói tin chứa nóCảnh báoUsageCóPacketBase<TSelf> phải truyền lớp gói tin chứa làm TSelf (CRTP).
NALIX011IPacketDeserializer<T> nên dùng lớp gói tin chứa nóCảnh báoUsageCóHợp đồng deserializer của gói tin phải nhắm tới lớp gói tin chứa nó.
NALIX012Gói tin PacketBase nên khai báo Deserialize tĩnhCảnh báoUsageCóCác kiểu gói tin PacketBase<TSelf> nên khai báo public static new TSelf Deserialize(ReadOnlySpan<byte>) có thể truy cập.
NALIX017Chữ ký Deserialize của gói tin không hợp lệCảnh báoUsageKhôngDeserialize phải là public static new {Packet} Deserialize(ReadOnlySpan<byte>).
NALIX018Kiểu gói tin đã đăng ký phải là cụ thểCảnh báoUsageKhôngCác mục packet registry phải là kiểu gói tin cụ thể, không abstract, không generic.
NALIX047Số vòng lặp dispatch nằm ngoài khoảng hỗ trợCảnh báoUsageCóWithDispatchLoopCount yêu cầu giá trị trong khoảng 1..64 (hoặc null để tự động co giãn).
NALIX048Kiểu trả về của handler controller gói tin không được hỗ trợCảnh báoUsageKhôngCác kiểu trả về được hỗ trợ là void, Task, ValueTask, TResult, Task<TResult>, ValueTask<TResult> và IAsyncEnumerable<TElement>.
NALIX050PacketOpcode được khai báo trên kiểu không phải controllerThông tinUsageKhôngCác handler [PacketOpcode] phải được khai báo bên trong lớp controller [PacketHandler].
NALIX052Overload Deserialize của gói tin nên có ReadOnlySpan<byte>Cảnh báoUsageCóViệc phát hiện gói tin yêu cầu một overload Deserialize(ReadOnlySpan<byte>).
NALIX054Tên PacketHandler bị trùngThông tinUsageKhôngTên [PacketHandler("...")] trùng nhau làm giảm độ rõ ràng của định tuyến và chẩn đoán.
NALIX055Ép kiểu thừa trên PacketContext<T>.PacketThông tinUsageCócontext.Packet đã có kiểu mạnh là T.
NALIX056Đăng ký middleware dùng nullCảnh báoUsageCóCác method đăng ký middleware yêu cầu instance middleware khác null.
NALIX058Method handler gói tin không nên là genericCảnh báoUsageCóMethod handler gói tin generic không thể gắn với một wire opcode cụ thể.

2. Middleware và metadata định tuyến (NALIX006, NALIX025–NALIX038)

IDTiêu đềMức độDanh mụcCode FixMô tả
NALIX006Kiểu middleware đã đăng ký không khớp kiểu gói tin của dispatcherCảnh báoUsageKhôngIPacketMiddleware<TPacket> phải khớp kiểu gói tin của dispatcher.
NALIX025Packet metadata provider xóa OpcodeCảnh báoRoutingKhôngCác implementation IPacketMetadataProvider tùy chỉnh không được đặt builder.Opcode = null.
NALIX026Packet metadata provider ghi đè Opcode mà không có điều kiện bảo vệThông tinRoutingKhôngMetadata provider nên bảo vệ phép gán bằng builder.Opcode ??= ... thay vì ghi đè vô điều kiện.
NALIX030Middleware gói tin nên khai báo MiddlewareOrderThông tinMiddlewareCó[MiddlewareOrder(n)] tường minh giúp thứ tự thực thi middleware có tính xác định.
NALIX032Middleware Inbound bỏ qua AlwaysExecuteThông tinMiddlewareCóMiddlewareStage.Inbound bỏ qua AlwaysExecute = true (AlwaysExecute chỉ áp dụng cho Outbound và Both).
NALIX033Middleware đã đăng ký dùng chung MiddlewareOrder với middleware khác trong cùng chuỗiThông tinMiddlewareKhôngCác giá trị [MiddlewareOrder] trùng nhau trong một chuỗi pipeline làm giảm tính dự đoán được của thứ tự.
NALIX035PacketOpcode nằm trong dải dành riêngCảnh báoUsageKhôngCác opcode 0x0000..0x00FF (0..255) được dành cho khung hệ thống/giao thức của Nalix. Dùng >= 0x0100 cho gói tin ứng dụng.
NALIX036PacketOpcode đã được dùng trong một controller khácCảnh báoUsageKhôngMọi handler đăng ký trên cùng một dispatcher phải có opcode duy nhất trên toàn cục.
NALIX038OpCode trong tài liệu không khớp với attributeThông tinDocumentationKhôngCác comment XML nhắc tới một opcode nên khớp với giá trị của [PacketOpcode].

3. Bố cục tuần tự hóa (NALIX013–NALIX022, NALIX034, NALIX046, NALIX051)

IDTiêu đềMức độDanh mụcCode FixMô tả
NALIX013Thành viên tuần tự hóa tường minh nên khai báo SerializeOrderCảnh báoSerializationCóCác kiểu có [SerializePackable(SerializeLayout.Explicit)] nên chú thích thành viên được tuần tự hóa bằng [SerializeOrder(n)] hoặc [SerializeIgnore].
NALIX014Giá trị SerializeOrder bị trùngCảnh báoSerializationCóCác giá trị [SerializeOrder] phải duy nhất trên mọi thành viên được tuần tự hóa trong một chuỗi kiểu.
NALIX015SerializeIgnore xung đột với SerializeOrderCảnh báoSerializationCóMột thành viên không thể vừa có [SerializeIgnore] vừa có [SerializeOrder].
NALIX016SerializeDynamicSize không cần thiết trên thành viên kích thước cố địnhThông tinSerializationKhông[SerializeDynamicSize] chỉ có ý nghĩa với thành viên kích thước thay đổi (string, mảng, tập hợp).
NALIX021SerializeOrder không nên là số âmCảnh báoSerializationKhôngChỉ số [SerializeOrder] phải >= 0.
NALIX022SerializeHeader(0) là dành riêng trên kiểu PacketBaseCảnh báoSerializationKhôngVới các lớp con PacketBase<TSelf>, thứ tự 0 (PacketHeaderOffset.Header) đã bị PacketBase.Header chiếm. Bắt đầu các field của người dùng từ thứ tự 0 qua [SerializeOrder(0)] hoặc dùng offset header khác 0.
NALIX034SerializeHeader xung đột với SerializeOrderCảnh báoSerializationKhôngKhông kết hợp [SerializeHeader] và [SerializeOrder] trên cùng một thành viên.
NALIX046Khoảng cách SerializeOrder quá lớnThông tinSerializationKhông[SerializeOrder] là metadata thứ tự field tuần tự, không phải offset byte thô. Khoảng cách > 16 sẽ kích hoạt gợi ý này.
NALIX051Kiểu IFixedSizeSerializable chứa thành viên tuần tự hóa độngCảnh báoSerializationKhôngCác kiểu implement IFixedSizeSerializable không được chứa chuỗi, mảng hoặc tập hợp có độ dài thay đổi.

4. Vòng đời, cấu hình, SDK và hosting (NALIX020, NALIX023–NALIX028, NALIX037, NALIX039–NALIX045, NALIX057)

IDTiêu đềMức độDanh mụcCode FixMô tả
NALIX020ResetForPool của gói tin nên gọi base.ResetForPoolCảnh báoLifecycleCóCác override ResetForPool() tùy chỉnh trên PacketBase<TSelf> phải gọi base.ResetForPool() để header và priority cơ sở được đặt lại.
NALIX023Kiểu property cấu hình không được hỗ trợCảnh báoConfigurationCóConfigurationLoader hỗ trợ các property kiểu nguyên thủy, decimal, string, Guid, TimeSpan, DateTime và enum.
NALIX024Property cấu hình không bind đượcThông tinConfigurationCóCác property ConfigurationLoader public không có setter public không thể bind từ file .ini và nên khai báo [ConfiguredIgnore].
NALIX027RequestOptions.RetryCount không nên là số âmCảnh báoSDKKhôngRequestOptions.RetryCount phải >= 0.
NALIX028RequestOptions.TimeoutMs không nên là số âmCảnh báoSDKKhôngRequestOptions.TimeoutMs phải >= 0 (0 tắt timeout cho mỗi lần thử).
NALIX037Có thể cấp phát bộ nhớ trong hot pathThông tinPerformanceKhôngCảnh báo khi có new byte[...], .ToArray() hoặc cấp phát do LINQ bên trong handler gói tin, middleware và method serializer.
NALIX039Có thể rò rỉ IBufferLeaseCảnh báoUsageKhôngMọi IBufferLease đã thuê phải được dispose bằng using, được trả về, hoặc được chuyển giao trên mọi nhánh điều khiển.
NALIX040NetworkApplicationBuilder nên cấu hình BufferPoolManagerThông tinPerformanceKhôngGợi ý gọi .UseBufferPoolManager(...) khi xây dựng pipeline host tùy chỉnh.
NALIX041NetworkApplicationBuilder nên cấu hình ConnectionHubThông tinUsageKhôngGợi ý gọi .UseConnectionHub(...) khi xây dựng pipeline host tùy chỉnh.
NALIX042Kiểu handler của NetworkApplicationBuilder không thể khởi tạoCảnh báoUsageKhôngCác kiểu controller truyền vào MapHandlers phải là lớp cụ thể, có thể khởi tạo.
NALIX043Kiểu metadata provider của NetworkApplicationBuilder không thể khởi tạoCảnh báoUsageKhôngCác kiểu metadata provider phải là lớp cụ thể, có thể khởi tạo.
NALIX044NetworkApplicationBuilder nên cấu hình một binding TCPThông tinUsageKhôngCảnh báo khi .Build() được gọi mà không có binding listener transport nào.
NALIX045NetworkApplicationBuilder nên cấu hình TCP trước UDPThông tinUsageKhôngCác listener UDP đã xác thực dựa vào phiên được thiết lập qua TCP (MapTcp nên đi cùng MapUdp).
NALIX057RequestOptions dùng timeout vô hạn với retryThông tinSDKCóĐặt TimeoutMs = 0 với RetryCount > 0 làm cho các lần retry không bao giờ xảy ra vì lần thử đầu tiên chờ vô hạn.

5. Chẩn đoán của Source Generator (NALIX059–NALIX068)

Được phát ra bởi Nalix.Analyzers.Generators (analyzers/Nalix.Analyzers.Generators/Internal/GeneratorDiagnosticDescriptors.cs):

IDTiêu đềMức độDanh mụcGeneratorMô tả
NALIX059Thiếu method Create() tĩnhLỗiNalix.Codec.SerializationSerializeFormatterGeneratorKiểu tham chiếu được đánh dấu [GenerateFormatter] phải khai báo method Create() tĩnh trả về một instance để pooling/deserialization (được PacketBase<TSelf> kế thừa tự động).
NALIX060Lớp gói tin phải là partialLỗiNalix.Codec.SerializationPacketSchemaGeneratorCác lớp kế thừa PacketBase<TSelf> phải khai báo partial để PacketSchemaGenerator có thể sinh Length, ResetForPool() và Size.
NALIX063Không tìm thấy constructor có thể truy cậpLỗiNalix.Framework.InjectionInstanceGeneratorLớp [Injectable] không có constructor public hoặc internal nào mà source generator có thể gọi.
NALIX064Constructor không rõ ràng trong lớp injectableLỗiNalix.Framework.InjectionInstanceGeneratorLớp [Injectable] có nhiều constructor public/internal với cùng số tham số không phải cấu hình (args.Length) và các kiểu tham số không rời nhau.
NALIX065Lớp con SingletonBase thiếu constructor không tham số có thể truy cậpCảnh báoNalix.Framework.InjectionInstanceGeneratorLớp con SingletonBase<T> không có constructor không tham số public hoặc internal (chỉ protected thì bộ khởi tạo được sinh ra không truy cập được).
NALIX066Kiểu trả về không được hỗ trợ của method [RpcService]LỗiNalix.SDK.TransportRpcClientGeneratorMethod của interface [RpcService] phải trả về ValueTask, ValueTask<T>, RpcCall<T> hoặc RpcStream<T>.
NALIX067Method handler bị loại khỏi bảng điều phốiLỗiNalix.Runtime.DispatchingPacketHandlerGeneratorTham số 0 của handler [PacketOpcode] là IPacketContext<TPacket>; mọi tham số bổ sung (1..N) phải là kiểu tham chiếu được đánh dấu [FromScope].
NALIX068StaticOpCode của gói tin bị trùngLỗiNalix.Codec.DataFramesPacketRegistryGeneratorHai hoặc nhiều lớp gói tin trong compilation cùng khai báo một StaticOpCode.

6. Chẩn đoán bảo mật, pooling, RPC, injection và vòng đời (NALIX071–NALIX107)

IDTiêu đềMức độDanh mụcPhạm viMô tả
NALIX071Dùng abstraction mật mã của Nalix thay vì System.Security.CryptographyCảnh báoSecurityChỉ CoreDùng trực tiếp mật mã của BCL trong các assembly Nalix Core sẽ bỏ qua việc rà soát bảo mật (OsCsprng và CryptographicOperations.FixedTimeEquals được đưa vào danh sách cho phép).
NALIX072Dùng định dạng endpoint không cấp phát bộ nhớ trong hot pathThông tinPerformanceChỉ CoreIPAddress.ToString(), INetworkEndpoint.Address và SocketEndpoint.Address cấp phát chuỗi. Ưu tiên TryFormatAddress trên hot path.
NALIX073catch(Exception) nên lọc qua ExceptionClassifier.IsNonFatal()Cảnh báoCorrectnessChỉ CoreMệnh đề catch trống hoặc catch (Exception) trong các assembly Core phải lọc bằng when (...) hoặc ném lại, để các ngoại lệ fatal của runtime không bị nuốt.
NALIX074Tránh định dạng chuỗi sớm trong ghi log chẩn đoánThông tinPerformanceChỉ CoreChuỗi nội suy, string.Format hoặc phép nối bên trong new DiagnosticLog(...) vẫn cấp phát bộ nhớ ngay cả khi chẩn đoán tắt. Hãy bảo vệ bằng DiagnosticsEvents.Source.IsEnabled(...).
NALIX075PacketScope<T> phải được disposeLỗiPoolingMọi assemblyPacketScope<T> bao bọc một gói tin đã pool và phải được khai báo bằng using (using PacketScope<T> scope = ...) hoặc được dispose tường minh.
NALIX076Packet context thoát khỏi phạm vi handlerLỗiCorrectnessMọi assemblyIPacketContext<T> và context.Packet được pool và trả về ngay khi handler kết thúc. Lưu chúng vào field, property, mảng/tập hợp, event, hoặc delegate của Task.Run / ThreadPool sẽ gây hỏng dữ liệu do dùng sau khi đã trả về.
NALIX078Tránh reflection không giới hạn trong mã Nalix Core nhạy cảm với AOTCảnh báoAOTChỉ CoreĐánh dấu Assembly.GetTypes(), Expression.Compile(), MakeGenericType(), MethodInfo.Invoke() và Type.GetType(string) trong các assembly Core.
NALIX100Method RpcService có kiểu trả về không hợp lệLỗiUsageMọi assemblyMethod trong interface [RpcService] phải trả về ValueTask, ValueTask<T>, RpcCall<T> hoặc RpcStream<T> (Task và Task<T> không được hỗ trợ).
NALIX101Method RpcService có tham số không hợp lệLỗiUsageMọi assemblyTham số 0 của method [RpcService] phải implement IPacket; các tham số sau đó chỉ có thể là RequestOptions, Func<TResponse, bool>?, bool? hoặc CancellationToken.
NALIX102Interface RpcService chứa thành viên không phải methodLỗiUsageMọi assemblyInterface [RpcService] chỉ được khai báo method (không được có property, event hay indexer).
NALIX103Interface RpcService thiếu attributeThông tinUsageMọi assemblyInterface chứa method [PacketOpcode] phải được đánh dấu [RpcService] để RpcClientGenerator phát hiện được.
NALIX104Field [Inject] không được là readonlyLỗiUsageMọi assemblyField có chú thích [Inject] được điền giá trị bên trong method InitializeDependencies() được sinh ra và không thể là readonly.
NALIX105Đã tắt analyzer Nalix: không resolve được symbol cần thiếtCảnh báoUsageMọi assemblyĐược phát ra khi bắt đầu compilation nếu không resolve được một symbol Nalix Core bắt buộc từ các assembly được tham chiếu.
NALIX106StaticOpCode của gói tin đã được gói tin khác dùngLỗiUsageMọi assemblyHai kiểu gói tin trong compilation khai báo cùng một giá trị StaticOpCode.
NALIX107ResetForPool của gói tin không đặt lại thành viên có thể thay đổiCảnh báoLifecycleMọi assemblyOverride ResetForPool() thủ công trên lớp gói tin bỏ sót việc đặt lại một field hoặc property có thể thay đổi mà không được đánh dấu [SkipClean].

Ví dụ NALIX076 (PacketContextEscapeAnalyzer)

IPacketContext<TPacket> và context.Packet được thuê từ ObjectPoolManager và tự động trả về khi ValueTask của handler hoàn thành.

Mẫu an toàn

[PacketOpcode(0x1001)]
public async ValueTask HandleEchoAsync(IPacketContext<EchoPacket> context)
{
    // Sao chép các field kiểu giá trị hoặc bất biến vào biến cục bộ nếu cần dùng sau
    string message = context.Packet.Message;
    uint seq = context.Packet.SequenceId;

    // Await trực tiếp một helper hoặc method Sender trong vòng đời handler là an toàn
    await context.Sender.ReplyAsync(new EchoPacket
    {
        SequenceId = seq,
        Message = message
    }).ConfigureAwait(false);
}

Mẫu không an toàn bị NALIX076 bắt

private static IPacketContext<EchoPacket>? s_leakedContext;

[PacketOpcode(0x1001)]
public void HandleUnsafe(IPacketContext<EchoPacket> context)
{
    // ERROR NALIX076: Lưu context hoặc packet vào field/property
    s_leakedContext = context;

    // ERROR NALIX076: Nắm giữ context hoặc bí danh packet cục bộ trong Task.Run
    EchoPacket alias = context.Packet;
    _ = Task.Run(() => ProcessInBackground(alias));

    // ERROR NALIX076: Nắm giữ context trong event handler
    context.Connection.ConnectionClosed += (_, _) => Log(context.Packet);
}