diff --git a/content/docs/analyzers/PlatformCop/PC0040.md b/content/docs/analyzers/PlatformCop/PC0040.md new file mode 100644 index 0000000..0802782 --- /dev/null +++ b/content/docs/analyzers/PlatformCop/PC0040.md @@ -0,0 +1,60 @@ ++++ +title = 'TryFunction database write is not rolled back' +linkTitle = 'PC0040' + +[params] + id = 'PC0040' + severity = 'Warning' + category = 'Usage' + codeAction = false + ignoreObsolete = true ++++ + +Database changes made inside a `[TryFunction]` are not rolled back when an error is caught. On-premises, writes can also cause a runtime error when `DisableWriteInsideTryFunctions` is enabled. + +PC0040 reports direct writes made by record built-in methods. A successful write can remain committed even if a later error causes the TryFunction to return `false`. + +### Example + +{{< highlight al "hl_lines=4-5" >}} +[TryFunction] +local procedure TryInsertCustomer(var Customer: Record Customer) +begin + Customer.Insert(); // The insert is not rolled back if a later error is caught [PC0040] + Error('Something failed after the insert.'); +end; +{{< /highlight >}} + +Keep the TryFunction for validation, and perform the write after it succeeds: + +{{< highlight al "hl_lines=3-4" >}} +procedure CreateCustomer(var Customer: Record Customer) +begin + if TryValidateCustomer(Customer) then + Customer.Insert(); +end; + +[TryFunction] +local procedure TryValidateCustomer(var Customer: Record Customer) +begin + Customer.TestField("No."); + Customer.TestField(Name); +end; +{{< /highlight >}} + +### When the diagnostic is reported + +PC0040 is reported for calls to `Insert`, `Modify`, `ModifyAll`, `Delete`, `DeleteAll`, `Rename`, `Truncate`, `AddLink`, `CopyLinks`, `DeleteLink` or `DeleteLinks` inside a TryFunction when the receiver resolves to a non-temporary record and table. Calls with or without parentheses are recognized. + +### When the diagnostic is not reported + +- Calls outside TryFunctions. +- Writes to temporary records, tables declared with `TableType = Temporary`, or temporary page source tables. +- Same-named methods on non-record types, such as `List.Insert`, `TextBuilder.Insert` and `HttpClient.Delete`. +- `RecordRef` writes, because whether a `RecordRef` is temporary is only known at runtime. +- Calls to custom procedures. Enable [PC0041](../pc0041/) to review calls that may write without following method bodies. + +### See also + +- [Handling errors using try methods](https://learn.microsoft.com/en-us/dynamics365/business-central/dev-itpro/developer/devenv-handling-errors-using-try-methods) on Microsoft Learn +- [PC0041](../pc0041/) - Call may write to the database inside a TryFunction diff --git a/content/docs/analyzers/PlatformCop/PC0041.md b/content/docs/analyzers/PlatformCop/PC0041.md new file mode 100644 index 0000000..5e6aab5 --- /dev/null +++ b/content/docs/analyzers/PlatformCop/PC0041.md @@ -0,0 +1,62 @@ ++++ +title = 'Call may write to the database inside a TryFunction' +linkTitle = 'PC0041' + +[params] + id = 'PC0041' + severity = 'Hidden' + category = 'Usage' + codeAction = false + ignoreObsolete = true ++++ + +Database changes made inside a `[TryFunction]` are not rolled back when an error is caught. PC0041 helps find calls whose effects are not known to be safe. + +This diagnostic is disabled by default because it reports every custom procedure call and selected built-in methods without analyzing their implementations. Enable it when you want to review potential writes across your codebase; use [PC0040](../pc0040/) for direct record writes. + +### Example + +{{< highlight al "hl_lines=4" >}} +[TryFunction] +local procedure TryPrepareCustomer(var Customer: Record Customer) +begin + PrepareCustomer(Customer); // This procedure may write to the database [PC0041] +end; +{{< /highlight >}} + +If you know a call is safe and does not write to the database, suppress the diagnostic with a reason: + +{{< highlight al "hl_lines=4-6" >}} +[TryFunction] +local procedure TryPrepareCustomer(var Customer: Record Customer) +begin +#pragma warning disable PC0041 // PrepareCustomer only validates values in memory. + PrepareCustomer(Customer); +#pragma warning restore PC0041 +end; +{{< /highlight >}} + +### Enable the rule + +Add PC0041 to your project's `.ruleset.json` file. `Info` shows potential writes as informational messages: + +{{< highlight json >}} +{ + "name": "My Project Ruleset", + "rules": [ + { + "id": "PC0041", + "action": "Info" + } + ] +} +{{< /highlight >}} + +### When the diagnostic is reported + +PC0041 is reported inside a TryFunction for every custom procedure call and for built-in `Commit`, `Execute`, `Print`, `Run`, `RunModal`, `RunRequestPage`, `SaveAs`, `SaveAsExcel`, `SaveAsHtml`, `SaveAsPdf`, `SaveAsWord`, `SaveAsXml` and `Validate` calls. It does not determine whether a custom procedure actually writes to the database. + +### See also + +- [Handling errors using try methods](https://learn.microsoft.com/en-us/dynamics365/business-central/dev-itpro/developer/devenv-handling-errors-using-try-methods) on Microsoft Learn +- [PC0040](../pc0040/) - TryFunction database write is not rolled back diff --git a/content/docs/analyzers/PlatformCop/_index.md b/content/docs/analyzers/PlatformCop/_index.md index 60d0373..6f7e1dc 100644 --- a/content/docs/analyzers/PlatformCop/_index.md +++ b/content/docs/analyzers/PlatformCop/_index.md @@ -47,3 +47,5 @@ PlatformCop inspects AL code for constructs the platform rejects, ignores, or ex | [PC0036](pc0036/) | SetRecord() does not support temporary records | Warning | ✓ | | | [PC0037](pc0037/) | Use Validate() instead of direct field assignment | Warning | ✓ | ✓ | | [PC0038](pc0038/) | Not all code paths return a value | Info | ✓ | | +| [PC0040](pc0040/) | TryFunction database write is not rolled back | Warning | ✓ | | +| [PC0041](pc0041/) | Call may write to the database inside a TryFunction | Hidden | | |