Prolint is a tool for automated source code review of Progress 4GL code. It reads one or more sourcefiles and examines it for bad programming practice
When you are interested Prolint, you are encouraged to subscribe to this group where you find the on-line tools to collaborate and discuss Prolint. There is a discussion forum, you can submit issues (for bugs and enhancement requests), you can modify the on-line documentation. So subscribe, and then don't forget to go to your subscription details to enable the e-mail notification!
10.1A permits to use shorthand code to get / set dynamic buffer-field values.
Something like:
hDynBuffer:BUFFER-FIELD("SomeField":U):BUFFER-VALUE
can now be written as:
hDynBuffer::BUFFER-VALUE
Prolint refuses to lint the file when it encounters this syntax and gives a "unexpected token: ::" error.
You can drop .p and .w file in this window, but you cannot drop .cls, although you can type their path and get them linted anyway.
(originally issued by dlauzon)
I get this notification ("wrong usage of ASSIGN..WHEN..statement") when I use something like:
---
ASSIGN
lLogical = FALSE WHEN lLogical = ?
...
.
---
Since the variable in the WHEN is on the same line, it's evident that it uses the value of the variable as it was before the ASSIGN, it's not confounding as if I had:
ASSIGN
lLogical = ?
...
lLogical = FALSE WHEN lLogical = ?
.
It's not different then with a non-ASSIGN.
e.g.
If I have:
cSomeVar = cSomeVar + "hello".
I know that the right cSomeVar is the value of cSomeVar prior to this li
Prolint could or should try to determine if the source code that you wrote, will have problems when you ever attempt to run it with a different dataserver like Oracle or MS-SQL.
But before we can teach Prolint what to look for, we first have to learn the "bad programming patterns" for Datasources.
The attached PDF files may help. Actually, the teach us best programming practices, so you have to read between the lines...
Create buffer and create query statements should always be followed by delete object statements, in order to prevent memory leaks. How about introducing this rule?
Suppose there is a statement:
FIND something WHERE something.color = "red" NO-ERROR etc
but there is no unique key on the selected fields (color). The FIND statement will return nothing. Of course this can be intentional, but I think it is often a mistake.
I have been searching for a full week :!: trying to figure out why a particular program was slowing down. In the end, the problem appeared to be a stupid little typo in an assign statement, that caused duplicate records to be created in a temp-table. Something like this:
Code:
FOR EACH .....
FIND FIRST tt_table WHERE tt_table.idnr = someNumber NO-ERROR.
IF NOT AVAILABLE tt_table THEN DO:
CREATE tt_table.
ASSIGN
tt_table.idnr = sameNumber
tt_table.text = someText.
END.
END.
Well it wasn't exactly like this but you get the idea.... sameNumber is 0 so a lot of identical records are created. This temp-table was supposed to hold only a small number of records, but instead it would grow to 100000 identical records because each FIND failed.
If an operation contains number 0.01 or 0.49 (or even 0.5) you should warn that these constants strongly suggest a broken approach to rounding (or bad implementation of ceil and floor functions).
Same is true for 0.001, 0.0001, 0.00001, 0.499, 0.4999 and so on.
Suggested by Tom Bascom.
The help file should tell how to write a proper round (ceil, floor) function or at least explain what goes wrong.
Greg Higgins submitted these functions :
function floor returns integer ( input decimal ) forward. function ceiling returns integer ( input decimal ) forward. function floor returns integer
Literal numbers should be replaced by constants or variables.
But 0 and 1 should be allowed because of the large number of false positives (do i=1 to 15: 1 is fine but 15 is not).
Suggested by Tom Bascom
buffers used for updates should be either
* explicitely strong scoped
* local to an internal procedure
Finding all updates seems hard to me so I would look for
* retrieve (find/for each,query) with lock
* create
* buffer-copy: can be used to create a record
as those are needed to update a buffer, I hope I didn't miss a technique.
If your Progress 4GL needs run with different dataservers (like Oracle, AS400, ODBC, SQL, ...) then maybe you need to consider some special coding practices. If we know what dataservers require, then maybe we can write the Prolint rules to help you check if your 4GL is portable.
* Character fields on AS/400 do not have variable length. If the Progress format is "x(20)" then the AS/400 field will have a fixed length of 20 characters. Your Progress session will crash when you try to write more than 20 characters to the field.
* cannot rely on the existence of VST, or system tables like _File and _Field
I have made this mistake a couple of times:
Code:
MESSAGE "Please enter a valid code"
IsValid = FALSE.
IF NOT IsValid THEN RETURN.
These were supposed to be two statements, but I keep forgetting to add a period to the first statement. The result is: customer gets to see a message "Please enter a valid code no" and the program continues as if the validation found no problem, resulting in a logic error.
I suppose the Prolint rule would have to check for indentation.
see if there is a transaction scoped to the procedure level (I mean, compilation unit level). Prolint must probably read the compile listing for this
check for strong scoping of buffers, or at least RELEASE buffer, especially in persistent procedures. Reason: dirty reads when some persistent procedure has an old copy of a record in memory.
Find absolute path names in the code. (Run, include, triggers, etc.)