mirror of
https://github.com/microsoft/BCQuality.git
synced 2026-10-05 14:46:55 +01:00
Improve partner onboarding and documentation navigation (#174)
Lead with a complete plugin quick start and add task-oriented usage, troubleshooting, customization, and contribution guides. Preserve the broader plugin framing, correct conflicting contract guidance, support Agents folder reviews, and align repository validation. Convert existing sample references to clickable links without changing knowledge rules. Co-authored-by: Jesper Schulz-Wedde <jesper.schulzwedde@microsoft.com> Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
parent
a21edfec46
commit
2b5550c346
276 changed files with 1287 additions and 756 deletions
|
|
@ -19,10 +19,10 @@ The reviewer signal is "this is a new top-level card or list page in an app whos
|
|||
|
||||
Set `AboutTitle` and `AboutText` on every new top-level card, list, and document page in an app that already uses them. Keep `AboutText` to two or three short sentences. Describe what the page does, not the navigation steps to use it — teaching tips explain WHAT, not HOW.
|
||||
|
||||
See sample: `abouttitle-abouttext-teaching-tips.good.al`.
|
||||
See sample: [`abouttitle-abouttext-teaching-tips.good.al`](abouttitle-abouttext-teaching-tips.good.al).
|
||||
|
||||
## Anti Pattern
|
||||
|
||||
A new top-level page in an app whose siblings have `AboutTitle`/`AboutText`, but with no teaching tips defined. Equally wrong is filling `AboutText` with step-by-step instructions ("Click New, then enter…") — the property is for orientation, not procedural help.
|
||||
|
||||
See sample: `abouttitle-abouttext-teaching-tips.bad.al`.
|
||||
See sample: [`abouttitle-abouttext-teaching-tips.bad.al`](abouttitle-abouttext-teaching-tips.bad.al).
|
||||
|
|
|
|||
|
|
@ -17,10 +17,10 @@ API pages — pages declared with `PageType = API` — surface as OData/JSON end
|
|||
|
||||
Pick camelCase identifiers up front: `APIPublisher = 'contoso'`, `APIGroup = 'app1'`, `EntityName = 'customer'`, field `Name = 'displayName'`. Keep them short — they end up in URL paths and JSON keys that every consumer types.
|
||||
|
||||
See sample: `api-page-camelcase-properties.good.al`.
|
||||
See sample: [`api-page-camelcase-properties.good.al`](api-page-camelcase-properties.good.al).
|
||||
|
||||
## Anti Pattern
|
||||
|
||||
`APIPublisher = 'Contoso-App'` (hyphen rejected, capitalization wrong for camelCase), `EntityName = 'sales_order'` (underscore rejected), or fields exposed with `Name = 'Display Name'` (space rejected). The compiler usually catches these, but the failure mode is opaque and the rename cost on a deployed API is high.
|
||||
|
||||
See sample: `api-page-camelcase-properties.bad.al`.
|
||||
See sample: [`api-page-camelcase-properties.bad.al`](api-page-camelcase-properties.bad.al).
|
||||
|
|
|
|||
|
|
@ -17,10 +17,10 @@ On a normal page, `DelayedInsert = false` is the default: the record is inserted
|
|||
|
||||
Declare `DelayedInsert = true` on every page with `PageType = API`. The setting plays well with `Modify(true)` and `Insert(true)` calls inside `OnInsert` and avoids the half-populated record states that otherwise reach validation logic.
|
||||
|
||||
See sample: `api-page-delayedinsert-true.good.al`.
|
||||
See sample: [`api-page-delayedinsert-true.good.al`](api-page-delayedinsert-true.good.al).
|
||||
|
||||
## Anti Pattern
|
||||
|
||||
Omitting `DelayedInsert` (which defaults to `false`) on an API page. Validation triggers fire on a partially populated record, mandatory-field errors come back to the caller for fields the JSON payload was about to supply, and the API surface produces failures that have no analogue in the UI page model.
|
||||
|
||||
See sample: `api-page-delayedinsert-true.bad.al`.
|
||||
See sample: [`api-page-delayedinsert-true.bad.al`](api-page-delayedinsert-true.bad.al).
|
||||
|
|
|
|||
|
|
@ -17,10 +17,10 @@ application-area: [all]
|
|||
|
||||
Pick the singular noun for `EntityName` and its grammatical plural for `EntitySetName`, both in camelCase. For compound nouns, only the trailing noun is pluralized: `EntityName = 'salesOrder'`, `EntitySetName = 'salesOrders'`. For nouns whose plural is irregular, use the natural English form — `EntitySetName = 'people'` for `EntityName = 'person'`.
|
||||
|
||||
See sample: `api-page-entity-naming-singular-plural.good.al`.
|
||||
See sample: [`api-page-entity-naming-singular-plural.good.al`](api-page-entity-naming-singular-plural.good.al).
|
||||
|
||||
## Anti Pattern
|
||||
|
||||
`EntityName = 'customers'`, `EntitySetName = 'customer'` — singular and plural swapped. Equally wrong is reusing the same form for both — `EntityName = 'customer'`, `EntitySetName = 'customer'` — which breaks OData metadata parsers and client codegen.
|
||||
|
||||
See sample: `api-page-entity-naming-singular-plural.bad.al`.
|
||||
See sample: [`api-page-entity-naming-singular-plural.bad.al`](api-page-entity-naming-singular-plural.bad.al).
|
||||
|
|
|
|||
|
|
@ -17,10 +17,10 @@ The `APIVersion` property on an API page is part of the public URL path: `/api/<
|
|||
|
||||
Start a new public endpoint at `'v1.0'`. Bump the minor when adding fields or non-breaking changes; bump the major when changing field types, removing fields, or any breaking change. Use `'beta'` for endpoints that are still iterating and SHOULD NOT be consumed by external integrations.
|
||||
|
||||
See sample: `api-page-version-format.good.al`.
|
||||
See sample: [`api-page-version-format.good.al`](api-page-version-format.good.al).
|
||||
|
||||
## Anti Pattern
|
||||
|
||||
`APIVersion = 'v2'` (missing minor), `APIVersion = '2.0'` (missing `v` prefix), `APIVersion = 'v2.0.0'` (extra segment). All three either fail to compile or produce a URL that consumers cannot reach.
|
||||
|
||||
See sample: `api-page-version-format.bad.al`.
|
||||
See sample: [`api-page-version-format.bad.al`](api-page-version-format.bad.al).
|
||||
|
|
|
|||
|
|
@ -19,10 +19,10 @@ Set the property to an area the app actually enables. `All` makes the control vi
|
|||
|
||||
Every field control and action carries `ApplicationArea = All;` (or a declared area of the app). The value is set once per control and keeps the control visible in the Web client.
|
||||
|
||||
See sample: `applicationarea-required-on-page-controls.good.al`.
|
||||
See sample: [`applicationarea-required-on-page-controls.good.al`](applicationarea-required-on-page-controls.good.al).
|
||||
|
||||
## Anti Pattern
|
||||
|
||||
A field control with no `ApplicationArea`. AS0062 flags it, and the control is invisible in the Web client for any profile that does not already enable a matching area.
|
||||
|
||||
See sample: `applicationarea-required-on-page-controls.bad.al`.
|
||||
See sample: [`applicationarea-required-on-page-controls.bad.al`](applicationarea-required-on-page-controls.bad.al).
|
||||
|
|
|
|||
|
|
@ -17,10 +17,10 @@ When a compound block follows `then`, `else`, or `do`, the `begin` keyword must
|
|||
|
||||
`if Condition then begin … end;`, `else begin … end;`, `for i := 1 to N do begin … end;`. The block body is indented one level below the `if`/`for` line, and `end;` sits at the same indentation as the line that opened the block.
|
||||
|
||||
See sample: `begin-on-same-line-as-then-else-do.good.al`.
|
||||
See sample: [`begin-on-same-line-as-then-else-do.good.al`](begin-on-same-line-as-then-else-do.good.al).
|
||||
|
||||
## Anti Pattern
|
||||
|
||||
A line that ends with `then` (or `else`, or `do`) and is followed by a line whose only content is `begin`. The compiler accepts it but CodeCop AA0005 flags it; the visual cost is a wasted line per block and a layout that looks alien to readers used to current AL style.
|
||||
|
||||
See sample: `begin-on-same-line-as-then-else-do.bad.al`.
|
||||
See sample: [`begin-on-same-line-as-then-else-do.bad.al`](begin-on-same-line-as-then-else-do.bad.al).
|
||||
|
|
|
|||
|
|
@ -17,10 +17,10 @@ CodeCop AA0018 requires that the block-introducing keywords `if`, `repeat`, `unt
|
|||
|
||||
Each `if`, `else if`, `repeat`, `for`, `while`, and `case` starts a line. Each `end;` (the closing of a `begin … end` block or a `case`) starts a line. Branch bodies are on their own line, indented.
|
||||
|
||||
See sample: `block-keywords-start-new-line.good.al`.
|
||||
See sample: [`block-keywords-start-new-line.good.al`](block-keywords-start-new-line.good.al).
|
||||
|
||||
## Anti Pattern
|
||||
|
||||
`if IsContactName then ValidateContactName() else if IsSalespersonCode then ValidateSalespersonCode();` collapses an `if/else if` chain onto a single line; AA0018 flags both the `else` and the second `if`. The same applies to `for i := 1 to 10 do begin DoX(i); DoY(i); end;` — `end` is not at the start of its line.
|
||||
|
||||
See sample: `block-keywords-start-new-line.bad.al`.
|
||||
See sample: [`block-keywords-start-new-line.bad.al`](block-keywords-start-new-line.bad.al).
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ Define the shared caption on the table field and let bound page fields inherit i
|
|||
|
||||
Before reporting a missing caption, inspect the binding and source field, including dependency symbols when needed. If the source definition is unavailable, do not treat an omitted page property as proof that the caption is missing. Caption and tooltip requirements are separate: do not add a `ToolTip` just because a caption is being reviewed; see [tooltip inheritance guidance](tooltip-required-on-page-fields.md).
|
||||
|
||||
See sample: `caption-required-on-page-fields.good.al`. Caption inheritance applies across BC versions; the sample uses BC24/runtime 13.0 or later to also define tooltips on its table fields.
|
||||
See sample: [`caption-required-on-page-fields.good.al`](caption-required-on-page-fields.good.al). Caption inheritance applies across BC versions; the sample uses BC24/runtime 13.0 or later to also define tooltips on its table fields.
|
||||
|
||||
## Anti Pattern
|
||||
|
||||
|
|
@ -31,7 +31,7 @@ A user-facing field that needs a label but has no non-empty explicit or inherite
|
|||
|
||||
The opposite review defect is flagging a bound field solely because it omits a page-level `Caption`, or inserting a copy of the table field's caption to satisfy AA0225/AA0226. That adds redundant text and prevents subsequent table-caption changes from flowing through to the page.
|
||||
|
||||
See sample: `caption-required-on-page-fields.bad.al`.
|
||||
See sample: [`caption-required-on-page-fields.bad.al`](caption-required-on-page-fields.bad.al).
|
||||
|
||||
## References
|
||||
|
||||
|
|
|
|||
|
|
@ -17,10 +17,10 @@ In an AL `case` statement, the action for each label is written on the line that
|
|||
|
||||
Each case label sits on its own line, terminated by `:`. The action below it is indented; multi-statement actions open with `begin` on the label line and close with `end;` on its own line.
|
||||
|
||||
See sample: `case-action-on-line-after-possibility.good.al`.
|
||||
See sample: [`case-action-on-line-after-possibility.good.al`](case-action-on-line-after-possibility.good.al).
|
||||
|
||||
## Anti Pattern
|
||||
|
||||
`'A': Letter2 := '10';` (single-line label and action), and `'C': begin Letter2 := '12'; DoSomething(); end;` (everything on one line including the block body). Both defeat per-line diff review and crowd the control flow.
|
||||
|
||||
See sample: `case-action-on-line-after-possibility.bad.al`.
|
||||
See sample: [`case-action-on-line-after-possibility.bad.al`](case-action-on-line-after-possibility.bad.al).
|
||||
|
|
|
|||
|
|
@ -17,10 +17,10 @@ application-area: [all]
|
|||
|
||||
Declare a `Label` with the `Err` suffix and the appropriate `Comment` for placeholders, then call `Error(YourErr, arg1, arg2)`. The same rule applies to `Message`, `Confirm`, and other UI primitives: format string in, parameters as separate arguments, no `StrSubstNo` wrapper at the call site, no string concatenation. An `Error('')` (empty message) is acceptable when the calling code expects another layer to emit the actual diagnostic.
|
||||
|
||||
See sample: `error-passes-parameters-directly-not-strsubstno.good.al`.
|
||||
See sample: [`error-passes-parameters-directly-not-strsubstno.good.al`](error-passes-parameters-directly-not-strsubstno.good.al).
|
||||
|
||||
## Anti Pattern
|
||||
|
||||
`Error(StrSubstNo(CustomerNotFoundErr, CustomerNo))` and `Error(CustomerNotFoundErr + ': ' + CustomerNo)` both defeat the translation and analysis machinery. Reviewers should treat `StrSubstNo` appearing as an argument to `Error`, `Message`, `Confirm`, or `StrMenu` as an unconditional signal to rewrite.
|
||||
|
||||
See sample: `error-passes-parameters-directly-not-strsubstno.bad.al`.
|
||||
See sample: [`error-passes-parameters-directly-not-strsubstno.bad.al`](error-passes-parameters-directly-not-strsubstno.bad.al).
|
||||
|
|
|
|||
|
|
@ -17,10 +17,10 @@ application-area: [all]
|
|||
|
||||
Reach for `FieldCaption("Location Code")` and `TableCaption()` whenever the value flows into a UI primitive. The same rule applies to format parameters: `Error(SomeErr, FieldCaption("Status"), TableCaption(), "Status")` rather than `Error(SomeErr, FieldName("Status"), TableName(), "Status")`. The captions follow the user's language; the names do not.
|
||||
|
||||
See sample: `fieldcaption-not-fieldname-in-user-messages.good.al`.
|
||||
See sample: [`fieldcaption-not-fieldname-in-user-messages.good.al`](fieldcaption-not-fieldname-in-user-messages.good.al).
|
||||
|
||||
## Anti Pattern
|
||||
|
||||
`Message('Updated %1', TableName())` or `Confirm(UpdateLocationQst, true, FieldName("Location Code"))`. The user sees the English internal name in every locale, and any future rename of the caption fails to reach the message.
|
||||
|
||||
See sample: `fieldcaption-not-fieldname-in-user-messages.bad.al`.
|
||||
See sample: [`fieldcaption-not-fieldname-in-user-messages.bad.al`](fieldcaption-not-fieldname-in-user-messages.bad.al).
|
||||
|
|
|
|||
|
|
@ -17,10 +17,10 @@ AL allows a parameterless procedure to be called without parentheses — `Custom
|
|||
|
||||
Always write `()` on a procedure call, even when it takes no arguments: `Customer.Init();`, `TempBuffer.DeleteAll();`, `if Customer.FindFirst() then …`. The same applies inside expressions and as a condition.
|
||||
|
||||
See sample: `function-call-parentheses-required.good.al`.
|
||||
See sample: [`function-call-parentheses-required.good.al`](function-call-parentheses-required.good.al).
|
||||
|
||||
## Anti Pattern
|
||||
|
||||
`Customer.Init;`, `TempBuffer.DeleteAll;`, `if Customer.FindFirst then …`. Every one of those is an AA0008 violation. Reviewers should treat a parameterless procedure name appearing without parentheses as a defect, even though the compiler accepts it.
|
||||
|
||||
See sample: `function-call-parentheses-required.bad.al`.
|
||||
See sample: [`function-call-parentheses-required.bad.al`](function-call-parentheses-required.bad.al).
|
||||
|
|
|
|||
|
|
@ -17,10 +17,10 @@ application-area: [all]
|
|||
|
||||
Write the Comment in the form `'%1 = Customer No., %2 = Sales Header No.'` — one entry per placeholder, matched by ordinal, named in the vocabulary of the BC domain. When the label is reused across multiple call sites, the Comment names the canonical meaning all call sites must conform to.
|
||||
|
||||
See sample: `label-comment-explains-placeholders.good.al`.
|
||||
See sample: [`label-comment-explains-placeholders.good.al`](label-comment-explains-placeholders.good.al).
|
||||
|
||||
## Anti Pattern
|
||||
|
||||
A label with two or more placeholders and no Comment, leaving the translator to guess. Equally bad is a Comment that only restates the placeholders (`'%1 and %2 are values'`) without naming what they are. Both fail in translation: the localized string ends up grammatically or semantically wrong, and the bug surfaces only in a non-English tenant.
|
||||
|
||||
See sample: `label-comment-explains-placeholders.bad.al`.
|
||||
See sample: [`label-comment-explains-placeholders.bad.al`](label-comment-explains-placeholders.bad.al).
|
||||
|
|
|
|||
|
|
@ -17,10 +17,10 @@ A `Label` is by default surfaced to translators and rewritten per locale. That i
|
|||
|
||||
Pair `Locked = true` with the `Tok` suffix for short tokens (`GetMethodTok: Label 'GET', Locked = true;`) and with the `Txt` suffix for telemetry strings that contain format placeholders but should not be localized. The `Locked` parameter and the `Tok` / `Txt` suffix together make the intent unambiguous.
|
||||
|
||||
See sample: `label-locked-for-non-translatable.good.al`.
|
||||
See sample: [`label-locked-for-non-translatable.good.al`](label-locked-for-non-translatable.good.al).
|
||||
|
||||
## Anti Pattern
|
||||
|
||||
`HttpsUrl: Label 'https://example.com';` or `ContentTypeTok: Label 'application/json';` declared without `Locked = true`. The translator localizes them, the integration fails in production for the affected tenant, and the failure is invisible in the developer's English-locale tests.
|
||||
|
||||
See sample: `label-locked-for-non-translatable.bad.al`.
|
||||
See sample: [`label-locked-for-non-translatable.bad.al`](label-locked-for-non-translatable.bad.al).
|
||||
|
|
|
|||
|
|
@ -17,10 +17,10 @@ CodeCop AA0074 flags `Label` and `TextConst` identifiers that do not end with an
|
|||
|
||||
Pick the suffix that matches the call where the label is consumed: `UpdateCompleteMsg` for `Message(...)`, `CustomerNotFoundErr` for `Error(...)`, `DeleteRecordQst` for `Confirm(...)`, `CustomerNameLbl` for tooltips and captions, `GetMethodTok` for locked tokens, `TelemetryDataTxt` for telemetry payloads. Suffix choices between `Tok`, `Lbl`, `Txt`, and `Msg` are judgment calls when the suffix is valid for the usage — what matters is that the suffix is on the approved list and matches the actual call.
|
||||
|
||||
See sample: `label-suffix-approved-list.good.al`.
|
||||
See sample: [`label-suffix-approved-list.good.al`](label-suffix-approved-list.good.al).
|
||||
|
||||
## Anti Pattern
|
||||
|
||||
A `Label` declared with no suffix (`CannotDeleteLine: Label '…';`), a generic name (`Text000: Label '…';`), or a suffix that contradicts the usage (`WrongSuffixTok: Label 'Customer %1 not found.'` then passed to `Error()`). All three trip AA0074 or its reviewers and obscure the call-site contract.
|
||||
|
||||
See sample: `label-suffix-approved-list.bad.al`.
|
||||
See sample: [`label-suffix-approved-list.bad.al`](label-suffix-approved-list.bad.al).
|
||||
|
|
|
|||
|
|
@ -19,10 +19,10 @@ Test codeunits that retain legacy uppercase forms (`OPENEDIT`, `ASSERTERROR`, `V
|
|||
|
||||
Write keywords lowercase: `if Condition then begin … end;`, `repeat … until Found;`, `for i := 1 to N do …`. The standard AL formatter normalizes casing automatically.
|
||||
|
||||
See sample: `lowercase-reserved-keywords.good.al`.
|
||||
See sample: [`lowercase-reserved-keywords.good.al`](lowercase-reserved-keywords.good.al).
|
||||
|
||||
## Anti Pattern
|
||||
|
||||
`IF Condition THEN BEGIN DoSomething(); END;`, `REPEAT GetNext(); UNTIL Found;`. Uppercase keywords trip AA0241 and signal C/AL-era code that has not been modernized.
|
||||
|
||||
See sample: `lowercase-reserved-keywords.bad.al`.
|
||||
See sample: [`lowercase-reserved-keywords.bad.al`](lowercase-reserved-keywords.bad.al).
|
||||
|
|
|
|||
|
|
@ -17,10 +17,10 @@ application-area: [all]
|
|||
|
||||
When invoking an object whose named alias is available in the same app (or in a dependency the current app already references), use the named form: `Page.RunModal(Page::"Posted Sales Shipment Lines", SalesShptLine)`, `Report.Run(Report::"Sales - Invoice", true)`. The same applies to `Codeunit.Run`, `XmlPort.Run`, `Query.Open`, and any platform method that takes an object reference. The named form makes diffs reviewable — a rename is visible — and makes log output and stack traces interpretable.
|
||||
|
||||
See sample: `named-invocations-not-object-ids.good.al`.
|
||||
See sample: [`named-invocations-not-object-ids.good.al`](named-invocations-not-object-ids.good.al).
|
||||
|
||||
## Anti Pattern
|
||||
|
||||
`Page.RunModal(525, …)` or `Report.Run(206, true)`. The numeric form is unreadable, fragile across renumbering, and breaks every search that looks for callers of a named object.
|
||||
|
||||
See sample: `named-invocations-not-object-ids.bad.al`.
|
||||
See sample: [`named-invocations-not-object-ids.bad.al`](named-invocations-not-object-ids.bad.al).
|
||||
|
|
|
|||
|
|
@ -17,10 +17,10 @@ CodeCop AA0013 flags `begin … end` blocks that contain exactly one statement.
|
|||
|
||||
A single statement following `then`, `else`, `do`, or a case label is written on its own line, indented one level, with no `begin … end`. Use `begin … end` only when there are two or more statements to group.
|
||||
|
||||
See sample: `no-begin-end-around-single-statement.good.al`.
|
||||
See sample: [`no-begin-end-around-single-statement.good.al`](no-begin-end-around-single-statement.good.al).
|
||||
|
||||
## Anti Pattern
|
||||
|
||||
`if Cond then begin OneCall(); end;` — single statement wrapped in a block. AA0013 flags it. The reviewer signal is "a `begin` followed by exactly one statement before its `end`."
|
||||
|
||||
See sample: `no-begin-end-around-single-statement.bad.al`.
|
||||
See sample: [`no-begin-end-around-single-statement.bad.al`](no-begin-end-around-single-statement.bad.al).
|
||||
|
|
|
|||
|
|
@ -17,10 +17,10 @@ When the `then` branch of an `if` ends in a terminating statement — `exit`, `b
|
|||
|
||||
Drop the `else` when the `then` branch unconditionally exits the procedure or the enclosing loop. The body that would have been inside `else` becomes the unindented continuation.
|
||||
|
||||
See sample: `no-else-after-terminating-statement.good.al`.
|
||||
See sample: [`no-else-after-terminating-statement.good.al`](no-else-after-terminating-statement.good.al).
|
||||
|
||||
## Anti Pattern
|
||||
|
||||
An `if … then Error(…) else Error(…)` pair where both branches terminate. The `else` is structural noise — the reader cannot tell at a glance whether it exists to handle an actual continuation or simply mirrors the `then`. The fix is to drop `else` and let the second `Error` fall through naturally.
|
||||
|
||||
See sample: `no-else-after-terminating-statement.bad.al`.
|
||||
See sample: [`no-else-after-terminating-statement.bad.al`](no-else-after-terminating-statement.bad.al).
|
||||
|
|
|
|||
|
|
@ -17,10 +17,10 @@ CodeCop AA0002 forbids whitespace between a procedure/method name and its `(`. `
|
|||
|
||||
`Customer.Get(CustomerNo)`, `Customer.SetFilter("No.", '%1', '*A*')`, `Message(GreetingMsg, UserName)`. The standard AL formatter enforces this automatically.
|
||||
|
||||
See sample: `no-space-before-method-parenthesis.good.al`.
|
||||
See sample: [`no-space-before-method-parenthesis.good.al`](no-space-before-method-parenthesis.good.al).
|
||||
|
||||
## Anti Pattern
|
||||
|
||||
`Customer.Get ( CustomerNo )`, `Message ( GreetingMsg, UserName )`. Both trip AA0002 and read as if the call had an extra unnamed parameter — a small but persistent friction every reader pays.
|
||||
|
||||
See sample: `no-space-before-method-parenthesis.bad.al`.
|
||||
See sample: [`no-space-before-method-parenthesis.bad.al`](no-space-before-method-parenthesis.bad.al).
|
||||
|
|
|
|||
|
|
@ -17,10 +17,10 @@ CodeCop AA0221 requires an `OptionCaption` on every option-type field that is no
|
|||
|
||||
`OptionMembers = Open,Released,Pending;` and `OptionCaption = 'Open,Released,Pending';` — same count, same order. When adding a new member, update both lines in the same commit.
|
||||
|
||||
See sample: `optioncaption-required-and-matches-membercount.good.al`.
|
||||
See sample: [`optioncaption-required-and-matches-membercount.good.al`](optioncaption-required-and-matches-membercount.good.al).
|
||||
|
||||
## Anti Pattern
|
||||
|
||||
`OptionMembers = Open,Released,Pending;` with no `OptionCaption` at all (the user sees the raw English members and translation is impossible), or `OptionMembers = Low,Medium,High,Critical;` paired with `OptionCaption = 'Low,Medium,High';` — count mismatch, `Critical` displays as blank or carries the wrong caption depending on platform version.
|
||||
|
||||
See sample: `optioncaption-required-and-matches-membercount.bad.al`.
|
||||
See sample: [`optioncaption-required-and-matches-membercount.bad.al`](optioncaption-required-and-matches-membercount.bad.al).
|
||||
|
|
|
|||
|
|
@ -17,10 +17,10 @@ CodeCop AA0003 requires exactly one space between the `not` operator and the exp
|
|||
|
||||
`if not Condition then`, `if not Customer.IsEmpty() then`, `exit(not Result)`. One space, lowercase keyword, no parentheses around the bare boolean.
|
||||
|
||||
See sample: `single-space-after-not-operator.good.al`.
|
||||
See sample: [`single-space-after-not-operator.good.al`](single-space-after-not-operator.good.al).
|
||||
|
||||
## Anti Pattern
|
||||
|
||||
`if NOT condition then`, `if not condition then`, `if !condition then` (which is not even AL — `!` is not a negation operator in AL). All three either trip AA0003 / AA0241 or fail to compile.
|
||||
|
||||
See sample: `single-space-after-not-operator.bad.al`.
|
||||
See sample: [`single-space-after-not-operator.bad.al`](single-space-after-not-operator.bad.al).
|
||||
|
|
|
|||
|
|
@ -17,10 +17,10 @@ CodeCop AA0001 requires exactly one space on each side of every binary operator:
|
|||
|
||||
Write `x := 1 + 2`, `Price := Amount * Quantity`, `if a = b then`, `if a and b then`. The standard AL formatter inserts these spaces automatically; running `Alt+Shift+F` (Format Document) in the AL extension is the simplest way to bring an entire file into compliance.
|
||||
|
||||
See sample: `single-space-around-binary-operators.good.al`.
|
||||
See sample: [`single-space-around-binary-operators.good.al`](single-space-around-binary-operators.good.al).
|
||||
|
||||
## Anti Pattern
|
||||
|
||||
`x:=1+2;`, `Price:=Amount*Quantity;`, `if a=b then`, `if a and b then`. All trip AA0001.
|
||||
|
||||
See sample: `single-space-around-binary-operators.bad.al`.
|
||||
See sample: [`single-space-around-binary-operators.bad.al`](single-space-around-binary-operators.bad.al).
|
||||
|
|
|
|||
|
|
@ -17,10 +17,10 @@ A `Record` variable declared with the `temporary` modifier behaves nothing like
|
|||
|
||||
Every local or global variable of type `Record X temporary` must start with `Temp`. Ordinary procedure parameters follow the same convention. Event publisher parameters are owned by the events-domain rule `prefix-temporary-record-event-parameters-with-temp.md`; the style leaf must not emit a second finding for the same event parameter.
|
||||
|
||||
See sample: `temporary-variable-temp-prefix.good.al`.
|
||||
See sample: [`temporary-variable-temp-prefix.good.al`](temporary-variable-temp-prefix.good.al).
|
||||
|
||||
## Anti Pattern
|
||||
|
||||
`WIPBuffer: Record "Job WIP Buffer" temporary;` as a local, global, or ordinary procedure parameter reads at the call site as if it were a database operation. Exclude event publisher parameters here so the events leaf remains their single owner.
|
||||
|
||||
See sample: `temporary-variable-temp-prefix.bad.al`.
|
||||
See sample: [`temporary-variable-temp-prefix.bad.al`](temporary-variable-temp-prefix.bad.al).
|
||||
|
|
|
|||
|
|
@ -17,10 +17,10 @@ CodeCop AA0248 recommends prefixing self-references inside a codeunit with `this
|
|||
|
||||
Inside a codeunit, prefix calls to procedures and accesses to global variables on the same codeunit with `this.`, and pass `this` when an external codeunit needs a reference to the running instance.
|
||||
|
||||
See sample: `this-keyword-in-codeunits.good.al`.
|
||||
See sample: [`this-keyword-in-codeunits.good.al`](this-keyword-in-codeunits.good.al).
|
||||
|
||||
## Anti Pattern
|
||||
|
||||
Calling a codeunit-local procedure as a bare identifier (`ValidateCustomer(Customer)`) when other readings are possible. The ambiguity costs reading time on every encounter and grows with codeunit size.
|
||||
|
||||
See sample: `this-keyword-in-codeunits.bad.al`.
|
||||
See sample: [`this-keyword-in-codeunits.bad.al`](this-keyword-in-codeunits.bad.al).
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ Make the text answer a question the caption does not: what the value is used for
|
|||
|
||||
Before raising a `medium`-severity finding, check the target runtime, the control's binding, and the source field's tooltip, including dependency symbols when needed. Report a field with neither an explicit nor an inherited tooltip independently of whether AA0218 is active. If the source definition or target runtime is unavailable, do not assume a missing page property means missing tooltip text.
|
||||
|
||||
See sample: `tooltip-required-on-page-fields.good.al` (BC24/runtime 13.0 or later).
|
||||
See sample: [`tooltip-required-on-page-fields.good.al`](tooltip-required-on-page-fields.good.al) (BC24/runtime 13.0 or later).
|
||||
|
||||
## Anti Pattern
|
||||
|
||||
|
|
@ -35,7 +35,7 @@ Flagging a bound field that already inherits its tooltip, or adding the same too
|
|||
|
||||
Treating a non-empty tooltip that merely repeats the caption as useful help is a separate quality issue, not a missing-tooltip finding. Point out the concrete information users need rather than demanding longer wording or a page-level override for its own sake.
|
||||
|
||||
See sample: `tooltip-required-on-page-fields.bad.al`.
|
||||
See sample: [`tooltip-required-on-page-fields.bad.al`](tooltip-required-on-page-fields.bad.al).
|
||||
|
||||
## References
|
||||
|
||||
|
|
|
|||
|
|
@ -17,10 +17,10 @@ CodeCop AA0021 requires that variable declarations inside a `var` block follow a
|
|||
|
||||
Declare all `Record` variables first, then other complex types, then primitives. A consistent order makes diffs review-friendly and matches the convention enforced by the AL formatter and CodeCop.
|
||||
|
||||
See sample: `variable-declaration-order-by-type.good.al`.
|
||||
See sample: [`variable-declaration-order-by-type.good.al`](variable-declaration-order-by-type.good.al).
|
||||
|
||||
## Anti Pattern
|
||||
|
||||
A `var` block where records and primitives are interleaved — `CustomerNo: Code[20];` between two `Record` variables, or `Amount: Decimal;` declared above the `Customer: Record Customer;` it is computed from. AA0021 flags it and the block is harder to scan; readers expect composite types at the top.
|
||||
|
||||
See sample: `variable-declaration-order-by-type.bad.al`.
|
||||
See sample: [`variable-declaration-order-by-type.bad.al`](variable-declaration-order-by-type.bad.al).
|
||||
|
|
|
|||
|
|
@ -17,10 +17,10 @@ Three CodeCop rules — AA0198, AA0202, AA0204 — together forbid a local varia
|
|||
|
||||
Differentiate every local declaration from globals, fields, procedures, and actions on the same object. `Customer` global plus `CustomerName` local; method `GetAmount` plus local `SalesAmount`. The standard pattern is to attach a noun suffix to the local (`CustomerName`, `CustomerRec`, `CustomerNo`) rather than to the global.
|
||||
|
||||
See sample: `variable-name-must-not-shadow.good.al`.
|
||||
See sample: [`variable-name-must-not-shadow.good.al`](variable-name-must-not-shadow.good.al).
|
||||
|
||||
## Anti Pattern
|
||||
|
||||
A procedure that declares a local `Customer: Text` inside a codeunit that already has a global `Customer: Record Customer`. The local wins and the global becomes unreachable inside the procedure. AA0198/AA0202/AA0204 flag this category of conflict whether the colliding entity is a global, a field, a method, or an action.
|
||||
|
||||
See sample: `variable-name-must-not-shadow.bad.al`.
|
||||
See sample: [`variable-name-must-not-shadow.bad.al`](variable-name-must-not-shadow.bad.al).
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue