@netoalmanca/advpl-sensei 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/agents/changelog-generator.md +63 -0
- package/agents/code-generator.md +215 -0
- package/agents/code-reviewer.md +145 -0
- package/agents/debugger.md +83 -0
- package/agents/doc-generator.md +67 -0
- package/agents/docs-reference.md +86 -0
- package/agents/migrator.md +84 -0
- package/agents/process-consultant.md +97 -0
- package/agents/refactorer.md +75 -0
- package/agents/sx-configurator.md +67 -0
- package/commands/changelog.md +66 -0
- package/commands/diagnose.md +67 -0
- package/commands/docs.md +81 -0
- package/commands/document.md +67 -0
- package/commands/explain.md +60 -0
- package/commands/generate.md +111 -0
- package/commands/migrate.md +81 -0
- package/commands/process.md +111 -0
- package/commands/refactor.md +65 -0
- package/commands/review.md +60 -0
- package/commands/sxgen.md +98 -0
- package/commands/test.md +103 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +143 -0
- package/dist/index.js.map +1 -0
- package/package.json +30 -0
- package/skills/advpl-code-generation/SKILL.md +163 -0
- package/skills/advpl-code-generation/patterns-fwformbrowse.md +485 -0
- package/skills/advpl-code-generation/patterns-jobs.md +519 -0
- package/skills/advpl-code-generation/patterns-mvc.md +765 -0
- package/skills/advpl-code-generation/patterns-pontos-entrada.md +708 -0
- package/skills/advpl-code-generation/patterns-rest.md +974 -0
- package/skills/advpl-code-generation/patterns-soap.md +639 -0
- package/skills/advpl-code-generation/patterns-treport.md +481 -0
- package/skills/advpl-code-generation/patterns-workflow.md +779 -0
- package/skills/advpl-code-generation/templates-classes.md +1096 -0
- package/skills/advpl-code-review/SKILL.md +72 -0
- package/skills/advpl-code-review/rules-best-practices.md +444 -0
- package/skills/advpl-code-review/rules-modernization.md +290 -0
- package/skills/advpl-code-review/rules-performance.md +333 -0
- package/skills/advpl-code-review/rules-security.md +302 -0
- package/skills/advpl-debugging/SKILL.md +265 -0
- package/skills/advpl-debugging/common-errors.md +1124 -0
- package/skills/advpl-debugging/performance-tips.md +768 -0
- package/skills/advpl-refactoring/SKILL.md +139 -0
- package/skills/advpl-to-tlpp-migration/SKILL.md +293 -0
- package/skills/advpl-to-tlpp-migration/migration-checklist.md +122 -0
- package/skills/advpl-to-tlpp-migration/migration-rules.md +265 -0
- package/skills/changelog-patterns/SKILL.md +99 -0
- package/skills/code-explanation/SKILL.md +66 -0
- package/skills/documentation-patterns/SKILL.md +172 -0
- package/skills/embedded-sql/SKILL.md +379 -0
- package/skills/probat-testing/SKILL.md +226 -0
- package/skills/probat-testing/patterns-unit-tests.md +614 -0
- package/skills/protheus-business/SKILL.md +92 -0
- package/skills/protheus-business/modulo-compras.md +780 -0
- package/skills/protheus-business/modulo-contabilidade.md +874 -0
- package/skills/protheus-business/modulo-estoque.md +876 -0
- package/skills/protheus-business/modulo-faturamento.md +800 -0
- package/skills/protheus-business/modulo-financeiro.md +1015 -0
- package/skills/protheus-business/modulo-fiscal.md +749 -0
- package/skills/protheus-business/modulo-manutencao.md +848 -0
- package/skills/protheus-business/modulo-pcp.md +743 -0
- package/skills/protheus-reference/SKILL.md +119 -0
- package/skills/protheus-reference/native-functions.md +7029 -0
- package/skills/protheus-reference/rest-api-reference.md +1758 -0
- package/skills/protheus-reference/restricted-functions.md +265 -0
- package/skills/protheus-reference/sx-dictionary.md +854 -0
- package/skills/sx-configuration/SKILL.md +184 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: advpl-code-review
|
|
3
|
+
description: Use when reviewing ADVPL/TLPP code for best practices, performance, security, and modernization opportunities
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# ADVPL/TLPP Code Review
|
|
7
|
+
|
|
8
|
+
## Overview
|
|
9
|
+
|
|
10
|
+
Systematic code review methodology for ADVPL/TLPP on TOTVS Protheus. This skill provides structured rules to identify issues related to best practices, performance bottlenecks, security vulnerabilities, and modernization opportunities in existing codebases.
|
|
11
|
+
|
|
12
|
+
## When to Use
|
|
13
|
+
|
|
14
|
+
- Reviewing ADVPL/TLPP source code before merge or deploy
|
|
15
|
+
- Auditing existing code for quality and compliance
|
|
16
|
+
- Identifying performance bottlenecks in slow routines
|
|
17
|
+
- Checking for security vulnerabilities (SQL injection, credential exposure)
|
|
18
|
+
- Evaluating code for migration readiness from .prw to .tlpp
|
|
19
|
+
- Onboarding reviews to enforce team coding standards
|
|
20
|
+
|
|
21
|
+
## Review Categories
|
|
22
|
+
|
|
23
|
+
| Category | File | Focus | Severity Range |
|
|
24
|
+
|----------|------|-------|----------------|
|
|
25
|
+
| Best Practices | `rules-best-practices.md` | RecLock/MsUnlock pairing, variable scope, area management, error handling, documentation | CRITICAL - INFO |
|
|
26
|
+
| Performance | `rules-performance.md` | Embedded SQL optimization, loop efficiency, string operations, index usage | CRITICAL - INFO |
|
|
27
|
+
| Security | `rules-security.md` | SQL injection, input validation, credential exposure, sensitive data logging | CRITICAL - WARNING |
|
|
28
|
+
| Modernization | `rules-modernization.md` | TLPP migration, namespace usage, OOP patterns, modern UI frameworks | INFO |
|
|
29
|
+
|
|
30
|
+
## Output Format
|
|
31
|
+
|
|
32
|
+
Each finding must include:
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
[RULE-ID] SEVERITY: Brief description
|
|
36
|
+
File: filename.prw (line XX)
|
|
37
|
+
Issue: What was found
|
|
38
|
+
Fix: How to correct it
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Severity Levels
|
|
42
|
+
|
|
43
|
+
| Level | Meaning | Action Required |
|
|
44
|
+
|-------|---------|-----------------|
|
|
45
|
+
| **CRITICAL** | Data corruption, security breach, or system failure risk | Must fix before deploy |
|
|
46
|
+
| **WARNING** | Performance degradation, maintenance burden, or potential bugs | Should fix in current sprint |
|
|
47
|
+
| **INFO** | Improvement opportunity, style suggestion, or modernization hint | Fix when touching the code |
|
|
48
|
+
|
|
49
|
+
## Review Process
|
|
50
|
+
|
|
51
|
+
1. **Scan includes and headers** - Check for obsolete includes (`Protheus.ch` vs `TOTVS.CH`), missing documentation headers
|
|
52
|
+
2. **Analyze variable declarations** - Verify scope (`Local` preferred), naming conventions (Hungarian notation)
|
|
53
|
+
3. **Check database operations** - Validate `RecLock`/`MsUnlock` pairing, `GetArea`/`RestArea` usage, error handling around DB ops
|
|
54
|
+
4. **Evaluate queries** - Review Embedded SQL for `SELECT *`, proper macro usage (`%exp:`, `%table:`, `%notDel%`), index alignment
|
|
55
|
+
5. **Inspect security surface** - Look for SQL injection vectors, hardcoded credentials, sensitive data in logs, unvalidated REST input
|
|
56
|
+
6. **Assess modernization** - Identify candidates for TLPP migration, OOP refactoring, modern UI patterns
|
|
57
|
+
|
|
58
|
+
## Rule ID Format
|
|
59
|
+
|
|
60
|
+
| Prefix | Category | Example |
|
|
61
|
+
|--------|----------|---------|
|
|
62
|
+
| `BP` | Best Practices | `[BP-001]` RecLock without MsUnlock, `[BP-008]` Reserved system variables |
|
|
63
|
+
| `PERF` | Performance | `[PERF-001]` SELECT * in Embedded SQL |
|
|
64
|
+
| `SEC` | Security | `[SEC-001]` SQL injection, `[SEC-005]` Restricted TOTVS functions |
|
|
65
|
+
| `MOD` | Modernization | `[MOD-001]` .prw class candidate for .tlpp |
|
|
66
|
+
|
|
67
|
+
## References
|
|
68
|
+
|
|
69
|
+
- `rules-best-practices.md` - Best practice rules with detection patterns and code examples
|
|
70
|
+
- `rules-performance.md` - Performance rules with detection patterns and code examples
|
|
71
|
+
- `rules-security.md` - Security rules with detection patterns and code examples
|
|
72
|
+
- `rules-modernization.md` - Modernization rules with detection patterns and code examples
|
|
@@ -0,0 +1,444 @@
|
|
|
1
|
+
# Best Practices Rules
|
|
2
|
+
|
|
3
|
+
Rules for detecting violations of ADVPL/TLPP best practices. Each rule includes a detection pattern, violation example, correct example, and explanation.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## [BP-001] RecLock without corresponding MsUnlock
|
|
8
|
+
|
|
9
|
+
**Severity:** CRITICAL
|
|
10
|
+
|
|
11
|
+
**Description:** Every `RecLock` call must have a corresponding `MsUnlock` call. Forgetting to unlock a record keeps it locked, blocking other users and threads from accessing it, potentially causing deadlocks and data access failures.
|
|
12
|
+
|
|
13
|
+
**What to look for:** A `RecLock` call that is not followed by `MsUnlock` within the same logical block. Check that all code paths (including error branches) release the lock.
|
|
14
|
+
|
|
15
|
+
**Violation:**
|
|
16
|
+
|
|
17
|
+
```advpl
|
|
18
|
+
User Function UpdateClient()
|
|
19
|
+
Local cCodCli := "000001"
|
|
20
|
+
|
|
21
|
+
DbSelectArea("SA1")
|
|
22
|
+
DbSetOrder(1)
|
|
23
|
+
|
|
24
|
+
If DbSeek(xFilial("SA1") + cCodCli)
|
|
25
|
+
RecLock("SA1", .F.)
|
|
26
|
+
SA1->A1_NOME := "New Name"
|
|
27
|
+
// Missing MsUnlock - record stays locked!
|
|
28
|
+
EndIf
|
|
29
|
+
|
|
30
|
+
Return
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
**Correct:**
|
|
34
|
+
|
|
35
|
+
```advpl
|
|
36
|
+
User Function UpdateClient()
|
|
37
|
+
Local cCodCli := "000001"
|
|
38
|
+
|
|
39
|
+
DbSelectArea("SA1")
|
|
40
|
+
DbSetOrder(1)
|
|
41
|
+
|
|
42
|
+
If DbSeek(xFilial("SA1") + cCodCli)
|
|
43
|
+
If RecLock("SA1", .F.)
|
|
44
|
+
SA1->A1_NOME := "New Name"
|
|
45
|
+
MsUnlock()
|
|
46
|
+
EndIf
|
|
47
|
+
EndIf
|
|
48
|
+
|
|
49
|
+
Return
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
**Why it matters:** An unreleased lock blocks all other threads attempting to write to the same record. In high-concurrency environments this leads to lock timeouts, deadlocks, and user complaints about frozen screens.
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## [BP-002] Local variables declared outside the function header
|
|
57
|
+
|
|
58
|
+
**Severity:** ERROR
|
|
59
|
+
|
|
60
|
+
**Description:** In ADVPL/TLPP, ALL `Local` variable declarations MUST appear at the top of the function, immediately after the function signature and before any executable code. Declaring `Local` inside `If`, `While`, `For` blocks, or after any executable statement is a violation that can cause compilation errors or unpredictable behavior.
|
|
61
|
+
|
|
62
|
+
**What to look for:** `Local` keyword appearing after any executable statement (DbSelectArea, If, While, RecLock, assignments, function calls, etc.).
|
|
63
|
+
|
|
64
|
+
**Violation:**
|
|
65
|
+
|
|
66
|
+
```advpl
|
|
67
|
+
User Function ProcessData()
|
|
68
|
+
Local aArea := GetArea()
|
|
69
|
+
Local cAlias := "SA1"
|
|
70
|
+
|
|
71
|
+
DbSelectArea(cAlias)
|
|
72
|
+
DbSetOrder(1)
|
|
73
|
+
|
|
74
|
+
If DbSeek(xFilial("SA1") + "000001")
|
|
75
|
+
Local cNome := AllTrim(SA1->A1_NOME) // WRONG: Local inside If block
|
|
76
|
+
Local nSaldo := SA1->A1_SALDO // WRONG: Local after executable code
|
|
77
|
+
EndIf
|
|
78
|
+
|
|
79
|
+
RestArea(aArea)
|
|
80
|
+
Return cNome
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
**Correct:**
|
|
84
|
+
|
|
85
|
+
```advpl
|
|
86
|
+
User Function ProcessData()
|
|
87
|
+
Local aArea := GetArea()
|
|
88
|
+
Local cAlias := "SA1"
|
|
89
|
+
Local cNome := ""
|
|
90
|
+
Local nSaldo := 0
|
|
91
|
+
|
|
92
|
+
DbSelectArea(cAlias)
|
|
93
|
+
DbSetOrder(1)
|
|
94
|
+
|
|
95
|
+
If DbSeek(xFilial("SA1") + "000001")
|
|
96
|
+
cNome := AllTrim(SA1->A1_NOME)
|
|
97
|
+
nSaldo := SA1->A1_SALDO
|
|
98
|
+
EndIf
|
|
99
|
+
|
|
100
|
+
RestArea(aArea)
|
|
101
|
+
Return cNome
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
**Why it matters:** ADVPL/TLPP requires all variable declarations at the function header. Declaring variables in the middle of executable code violates the language specification and can lead to compilation errors in strict mode or undefined behavior. Variables that are conditionally needed should be declared at the top with a default value (Nil, "", 0, .F., {}) and assigned later.
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## [BP-002b] Variables declared as Private or Public instead of Local
|
|
109
|
+
|
|
110
|
+
**Severity:** WARNING
|
|
111
|
+
|
|
112
|
+
**Description:** Variables should be declared as `Local` unless there is a specific need for broader scope. `Private` and `Public` variables pollute the call stack and can be accidentally overwritten by called functions.
|
|
113
|
+
|
|
114
|
+
**What to look for:** `Private` or `Public` variable declarations where `Local` would suffice. Legitimate uses of `Private` include passing variables to MVC models or legacy framework functions that expect them.
|
|
115
|
+
|
|
116
|
+
**Violation:**
|
|
117
|
+
|
|
118
|
+
```advpl
|
|
119
|
+
User Function CalcTotal()
|
|
120
|
+
Private nTotal := 0
|
|
121
|
+
Private cCodCli := "000001"
|
|
122
|
+
Private nIdx := 0
|
|
123
|
+
|
|
124
|
+
For nIdx := 1 To 10
|
|
125
|
+
nTotal += GetValue(nIdx)
|
|
126
|
+
Next nIdx
|
|
127
|
+
|
|
128
|
+
Return nTotal
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
**Correct:**
|
|
132
|
+
|
|
133
|
+
```advpl
|
|
134
|
+
User Function CalcTotal()
|
|
135
|
+
Local nTotal := 0
|
|
136
|
+
Local cCodCli := "000001"
|
|
137
|
+
Local nIdx := 0
|
|
138
|
+
|
|
139
|
+
For nIdx := 1 To 10
|
|
140
|
+
nTotal += GetValue(nIdx)
|
|
141
|
+
Next nIdx
|
|
142
|
+
|
|
143
|
+
Return nTotal
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
**Why it matters:** `Private` variables are visible to all functions called from the declaring function. A called function may unintentionally read or overwrite a `Private` variable, causing hard-to-trace bugs. `Local` variables are scoped to the declaring function only, making code predictable and safer.
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
## [BP-003] Missing GetArea/RestArea in functions that use DbSelectArea/DbSetOrder/DbSeek
|
|
151
|
+
|
|
152
|
+
**Severity:** WARNING
|
|
153
|
+
|
|
154
|
+
**Description:** Functions that change the current WorkArea (via `DbSelectArea`, `DbSetOrder`, `DbSeek`, etc.) must save and restore the caller's WorkArea state using `GetArea()` and `RestArea()`. Otherwise, the calling function's WorkArea context is silently altered.
|
|
155
|
+
|
|
156
|
+
**What to look for:** Any function that calls `DbSelectArea`, `DbSetOrder`, `DbSeek`, or `DbGoTo` without first calling `GetArea()` on the affected alias and calling `RestArea()` before returning.
|
|
157
|
+
|
|
158
|
+
**Violation:**
|
|
159
|
+
|
|
160
|
+
```advpl
|
|
161
|
+
User Function GetClientName(cCodCli)
|
|
162
|
+
Local cNome := ""
|
|
163
|
+
|
|
164
|
+
DbSelectArea("SA1")
|
|
165
|
+
DbSetOrder(1)
|
|
166
|
+
|
|
167
|
+
If DbSeek(xFilial("SA1") + cCodCli)
|
|
168
|
+
cNome := SA1->A1_NOME
|
|
169
|
+
EndIf
|
|
170
|
+
|
|
171
|
+
Return cNome
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
**Correct:**
|
|
175
|
+
|
|
176
|
+
```advpl
|
|
177
|
+
User Function GetClientName(cCodCli)
|
|
178
|
+
Local cNome := ""
|
|
179
|
+
Local aAreaSA1 := SA1->(GetArea())
|
|
180
|
+
|
|
181
|
+
DbSelectArea("SA1")
|
|
182
|
+
DbSetOrder(1)
|
|
183
|
+
|
|
184
|
+
If DbSeek(xFilial("SA1") + cCodCli)
|
|
185
|
+
cNome := SA1->A1_NOME
|
|
186
|
+
EndIf
|
|
187
|
+
|
|
188
|
+
RestArea(aAreaSA1)
|
|
189
|
+
|
|
190
|
+
Return cNome
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
**Why it matters:** Without saving and restoring the area, the calling function loses its WorkArea position. This causes incorrect reads, skipped records, and mysterious bugs that are extremely difficult to diagnose because they depend on call order.
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
## [BP-004] #Include "Protheus.ch" instead of #Include "TOTVS.CH"
|
|
198
|
+
|
|
199
|
+
**Severity:** WARNING
|
|
200
|
+
|
|
201
|
+
**Description:** The include `Protheus.ch` is obsolete. All `.prw` files should use `#Include "TOTVS.CH"` which is the current standard and includes all necessary definitions.
|
|
202
|
+
|
|
203
|
+
**What to look for:** Any line containing `#Include "Protheus.ch"` (case-insensitive).
|
|
204
|
+
|
|
205
|
+
**Violation:**
|
|
206
|
+
|
|
207
|
+
```advpl
|
|
208
|
+
#Include "Protheus.ch"
|
|
209
|
+
|
|
210
|
+
User Function MyFunc()
|
|
211
|
+
// ...
|
|
212
|
+
Return
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
**Correct:**
|
|
216
|
+
|
|
217
|
+
```advpl
|
|
218
|
+
#Include "TOTVS.CH"
|
|
219
|
+
|
|
220
|
+
User Function MyFunc()
|
|
221
|
+
// ...
|
|
222
|
+
Return
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
**Why it matters:** `Protheus.ch` is a legacy include that may not contain all current definitions and may be removed in future Protheus versions. `TOTVS.CH` is the officially supported include that aggregates all necessary header definitions.
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
## [BP-005] Missing Begin Sequence/Recover/End Sequence around critical operations
|
|
230
|
+
|
|
231
|
+
**Severity:** WARNING
|
|
232
|
+
|
|
233
|
+
**Description:** Critical operations such as `RecLock`, database writes, external API calls, and file operations should be wrapped in `Begin Sequence`/`Recover`/`End Sequence` blocks to handle errors gracefully instead of crashing.
|
|
234
|
+
|
|
235
|
+
**What to look for:** `RecLock`, `MsExecAuto`, `HttpGet`, `HttpPost`, `FT_FUse`, or web service calls that are not inside a `Begin Sequence` block.
|
|
236
|
+
|
|
237
|
+
**Violation:**
|
|
238
|
+
|
|
239
|
+
```advpl
|
|
240
|
+
User Function ProcessOrder(cOrder)
|
|
241
|
+
Local lRet := .T.
|
|
242
|
+
|
|
243
|
+
DbSelectArea("SC5")
|
|
244
|
+
DbSetOrder(1)
|
|
245
|
+
DbSeek(xFilial("SC5") + cOrder)
|
|
246
|
+
|
|
247
|
+
RecLock("SC5", .F.)
|
|
248
|
+
SC5->C5_LIBEROK := "S"
|
|
249
|
+
MsUnlock()
|
|
250
|
+
|
|
251
|
+
// If RecLock fails or an error occurs, function crashes
|
|
252
|
+
// with an unhandled exception
|
|
253
|
+
|
|
254
|
+
Return lRet
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
**Correct:**
|
|
258
|
+
|
|
259
|
+
```advpl
|
|
260
|
+
User Function ProcessOrder(cOrder)
|
|
261
|
+
Local lRet := .T.
|
|
262
|
+
Local oError
|
|
263
|
+
|
|
264
|
+
DbSelectArea("SC5")
|
|
265
|
+
DbSetOrder(1)
|
|
266
|
+
DbSeek(xFilial("SC5") + cOrder)
|
|
267
|
+
|
|
268
|
+
Begin Sequence
|
|
269
|
+
|
|
270
|
+
If RecLock("SC5", .F.)
|
|
271
|
+
SC5->C5_LIBEROK := "S"
|
|
272
|
+
MsUnlock()
|
|
273
|
+
Else
|
|
274
|
+
lRet := .F.
|
|
275
|
+
ConOut("[ProcessOrder] Could not lock SC5 for order: " + cOrder)
|
|
276
|
+
EndIf
|
|
277
|
+
|
|
278
|
+
Recover Using oError
|
|
279
|
+
lRet := .F.
|
|
280
|
+
ConOut("[ProcessOrder] Error: " + oError:Description)
|
|
281
|
+
|
|
282
|
+
End Sequence
|
|
283
|
+
|
|
284
|
+
Return lRet
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
**Why it matters:** Without error handling, any failure in database or external operations causes the entire thread to crash, potentially leaving records locked and data in an inconsistent state. Structured error handling allows graceful recovery and proper cleanup.
|
|
288
|
+
|
|
289
|
+
---
|
|
290
|
+
|
|
291
|
+
## [BP-006] Variable names not following Hungarian notation
|
|
292
|
+
|
|
293
|
+
**Severity:** INFO
|
|
294
|
+
|
|
295
|
+
**Description:** ADVPL convention uses Hungarian notation with a type prefix followed by PascalCase: `c` for character, `n` for numeric, `l` for logical, `d` for date, `a` for array, `o` for object, `b` for code block, `x` for variant/indefinite.
|
|
296
|
+
|
|
297
|
+
**What to look for:** Variable declarations where the name does not start with a recognized type prefix (`c`, `n`, `l`, `d`, `a`, `o`, `b`, `x`) or does not follow PascalCase after the prefix.
|
|
298
|
+
|
|
299
|
+
**Violation:**
|
|
300
|
+
|
|
301
|
+
```advpl
|
|
302
|
+
User Function Calculate()
|
|
303
|
+
Local total := 0
|
|
304
|
+
Local clientName := ""
|
|
305
|
+
Local flag := .T.
|
|
306
|
+
Local items := {}
|
|
307
|
+
Local dt := Date()
|
|
308
|
+
|
|
309
|
+
// Variable names do not indicate their type
|
|
310
|
+
|
|
311
|
+
Return
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
**Correct:**
|
|
315
|
+
|
|
316
|
+
```advpl
|
|
317
|
+
User Function Calculate()
|
|
318
|
+
Local nTotal := 0
|
|
319
|
+
Local cClientName := ""
|
|
320
|
+
Local lFlag := .T.
|
|
321
|
+
Local aItems := {}
|
|
322
|
+
Local dDate := Date()
|
|
323
|
+
|
|
324
|
+
// Each variable name starts with its type prefix
|
|
325
|
+
|
|
326
|
+
Return
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
**Why it matters:** Hungarian notation makes code self-documenting. When reading `nTotal`, any developer immediately knows it is a numeric value. This reduces bugs caused by type mismatches and speeds up code reviews.
|
|
330
|
+
|
|
331
|
+
---
|
|
332
|
+
|
|
333
|
+
## [BP-007] Functions missing Protheus.doc documentation header
|
|
334
|
+
|
|
335
|
+
**Severity:** INFO
|
|
336
|
+
|
|
337
|
+
**Description:** Every `User Function`, `Static Function`, and `Main Function` should have a `Protheus.doc` documentation header describing the function's purpose, author, date, parameters, and return value.
|
|
338
|
+
|
|
339
|
+
**What to look for:** Function definitions without a preceding `Protheus.doc` comment block.
|
|
340
|
+
|
|
341
|
+
**Violation:**
|
|
342
|
+
|
|
343
|
+
```advpl
|
|
344
|
+
User Function CalcDiscount(nPrice, nPercent)
|
|
345
|
+
Local nDiscount := nPrice * (nPercent / 100)
|
|
346
|
+
Return nDiscount
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
**Correct:**
|
|
350
|
+
|
|
351
|
+
```advpl
|
|
352
|
+
/*/{Protheus.doc} CalcDiscount
|
|
353
|
+
Calculates discount value based on price and percentage.
|
|
354
|
+
|
|
355
|
+
@type User Function
|
|
356
|
+
@author Developer Name
|
|
357
|
+
@since 01/01/2025
|
|
358
|
+
@version 1.0
|
|
359
|
+
|
|
360
|
+
@param nPrice, Numeric, Original price
|
|
361
|
+
@param nPercent, Numeric, Discount percentage (0-100)
|
|
362
|
+
|
|
363
|
+
@return Numeric, Calculated discount value
|
|
364
|
+
|
|
365
|
+
@example
|
|
366
|
+
Local nDisc := CalcDiscount(100.00, 10) // Returns 10.00
|
|
367
|
+
/*/
|
|
368
|
+
User Function CalcDiscount(nPrice, nPercent)
|
|
369
|
+
Local nDiscount := nPrice * (nPercent / 100)
|
|
370
|
+
Return nDiscount
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
**Why it matters:** Documentation headers enable IDE tooltips, automatic documentation generation, and help other developers understand the function's contract without reading the implementation. They are especially important in large teams and long-lived codebases.
|
|
374
|
+
|
|
375
|
+
---
|
|
376
|
+
|
|
377
|
+
## [BP-008] Using reserved system variables as Local/Static variable names
|
|
378
|
+
|
|
379
|
+
**Severity:** CRITICAL
|
|
380
|
+
|
|
381
|
+
**Description:** The Protheus framework maintains several `Private` variables to track the current company, branch, and environment context. Declaring a `Local` variable with the same name shadows the system variable, causing loss of context, incorrect data filtering, and hard-to-reproduce bugs in multi-branch/multi-company environments.
|
|
382
|
+
|
|
383
|
+
**Reserved variable names (NEVER use as Local/Static):**
|
|
384
|
+
|
|
385
|
+
| Variable | Purpose |
|
|
386
|
+
|----------|---------|
|
|
387
|
+
| `cFilial` | Branch context — used internally by xFilial() and other framework functions |
|
|
388
|
+
| `cFilAnt` | Current branch code — default value for xFilial() second parameter |
|
|
389
|
+
| `cEmpAnt` | Current company/group code — used by FWSizeFilial() and environment functions |
|
|
390
|
+
| `nModulo` | Current module number |
|
|
391
|
+
| `cUsuario` | Current user login |
|
|
392
|
+
|
|
393
|
+
**Violation:**
|
|
394
|
+
|
|
395
|
+
```advpl
|
|
396
|
+
User Function MyJob()
|
|
397
|
+
Local cEmpresa := "99"
|
|
398
|
+
Local cFilial := "01" // WRONG: shadows system Private variable
|
|
399
|
+
|
|
400
|
+
RpcSetEnv(cEmpresa, cFilial)
|
|
401
|
+
// After this call, the system's cFilial Private is overwritten
|
|
402
|
+
// xFilial() and other functions may return incorrect values
|
|
403
|
+
Return
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
**Correct:**
|
|
407
|
+
|
|
408
|
+
```advpl
|
|
409
|
+
User Function MyJob()
|
|
410
|
+
Local cCodEmp := "99"
|
|
411
|
+
Local cCodFil := "01" // Safe: different name from system variable
|
|
412
|
+
|
|
413
|
+
RpcSetEnv(cCodEmp, cCodFil)
|
|
414
|
+
// System's cFilAnt/cEmpAnt are set correctly by RpcSetEnv
|
|
415
|
+
// xFilial() works as expected
|
|
416
|
+
Return
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
**Recommended alternatives:**
|
|
420
|
+
|
|
421
|
+
| Instead of | Use |
|
|
422
|
+
|------------|-----|
|
|
423
|
+
| `cFilial` | `cCodFil` |
|
|
424
|
+
| `cEmpresa` | `cCodEmp` |
|
|
425
|
+
| `cFilAnt` | (never reassign — read-only system variable) |
|
|
426
|
+
| `cEmpAnt` | (never reassign — read-only system variable) |
|
|
427
|
+
|
|
428
|
+
**Use FW* functions to read company/branch values:**
|
|
429
|
+
|
|
430
|
+
| Function | Purpose |
|
|
431
|
+
|----------|---------|
|
|
432
|
+
| `FWCodFil()` | Returns current branch code (M0_CODFIL) |
|
|
433
|
+
| `FWCodEmp()` | Returns current company code |
|
|
434
|
+
| `FWFilial(cAlias)` | Returns branch for the given alias (respects sharing mode) |
|
|
435
|
+
| `FWCompany(cAlias)` | Returns company for the given alias |
|
|
436
|
+
| `FWGrpCompany()` | Returns current company group |
|
|
437
|
+
| `FWUnitBusiness(cAlias)` | Returns current business unit |
|
|
438
|
+
| `FWAllFilial()` | Returns array of all branches for current group/company |
|
|
439
|
+
| `FWAllCompany()` | Returns array of all companies for current group |
|
|
440
|
+
| `FWAllGrpCompany()` | Returns array of all company groups |
|
|
441
|
+
| `FWSizeFilial()` | Returns branch field size |
|
|
442
|
+
| `xFilial(cAlias)` | Returns branch for index composition (respects sharing mode) |
|
|
443
|
+
|
|
444
|
+
**Why it matters:** Shadowing system Private variables is one of the most dangerous bugs in Protheus because it is silent — the code compiles and appears to work in single-branch testing, but fails in multi-branch production environments where branch context is critical for data isolation.
|