Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions content/docs/analyzers/PlatformCop/PC0038.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,40 @@ end;

Passing it by value (without `var`) does not count.

For a named `Record` return, assigning a field or property, or setting filters and keys, also counts as assigned. The named form avoids the extra record assignment that `exit(LocalRecord)` performs ([AL complex types](https://learn.microsoft.com/dynamics365/business-central/dev-itpro/developer/devenv-al-complextypes)); that assignment also drops filters, the current key and other view state ([The complexity of complex return types](https://waldo.be/2021/07/06/the-complexity-of-complex-return-types/)).

{{< highlight al "hl_lines=3 4 9" >}}
procedure NewCustomer(CustomerNo: Code[20]) Customer: Record Customer
begin
Customer."No." := CustomerNo;
Customer.Name := 'New customer';
end;

procedure OpenSalesLines(DocumentNo: Code[20]) SalesLine: Record "Sales Line"
begin
SalesLine.SetRange("Document No.", DocumentNo);
end;
{{< /highlight >}}

`Customer.Init()` and `Clear(Customer)` count as well, even as the only statement. Both apply the table's `InitValue` defaults, which an untouched record variable already holds. They differ only when something was set before: `Init()` keeps the primary key, filters and the current key, `Clear()` resets them.

#### What a returned record carries to the caller

A returned record behaves like a value. Filters and the current key set on a named return survive only while the caller uses the result directly in an expression. Every record assignment, whether `exit(LocalRecord)` inside the procedure or `SalesLine := OpenSalesLines(No)` in the caller, copies the field values only:

| Caller code | Filters and key |
|---|---|
| `OpenSalesLines(No).GetFilters()` with a named return | kept |
| `SalesLine := OpenSalesLines(No);` | lost |
| any use when the procedure ends with `exit(LocalRecord)` | lost |

`SalesLine.CopyFilters(OpenSalesLines(No))` and `SalesLine.Copy(OpenSalesLines(No))` do not compile, because both methods take a `var` record argument. The same assignment rule applies to a `temporary` record return: the caller's variable receives the current row, not the rows inserted into the temporary table.

To hand filters or the contents of a temporary table to a caller, pass the record as a `var` parameter. For filters alone, returning `GetView()` as text and applying it with `SetView()` also carries the key and sort order.

### See also

- [LC0097](../../lintercop/lc0097/) - Avoid mixing exit() and named return variable assignments
- [Working with AL methods - Return values](https://learn.microsoft.com/dynamics365/business-central/dev-itpro/developer/devenv-al-methods#declaring-methods) on Microsoft Learn
- [AL control statements - Exit statement](https://learn.microsoft.com/dynamics365/business-central/dev-itpro/developer/devenv-al-control-statements#exit-statement) on Microsoft Learn
- [AL complex types](https://learn.microsoft.com/dynamics365/business-central/dev-itpro/developer/devenv-al-complextypes) on Microsoft Learn
Loading