Rules

This is the list of rules, or actually the list of help-pages for each rule.
Not every rule in this list is by default available in your Prolint setup: the "Contributed rules" are not. Contributed rules can be downloaded separately to your prolint/contribs/rules directory from subversion path http://websvn.oehive.org/listing.php?repname=prolint&path=/trunk/contrib...

Click Here when you want to add a new Rule helppage.


abbrevkwd

Abbreviated [keyword] for [keyword]

Rule "abbrevkwd" gives this warning when it finds an abbreviated keyword in the source code.

Examples:

- Abbreviated MOVE-AFTER for MOVE-AFTER-TAB-ITEM

- Abbreviated SUBST for SUBSTITUTE.

the risc:

Confusion: is SUBST() actually the SUBSTRING() function or the SUBSTITUTE() function? Also, the source may compile differently in a next Progress version if the abbreviation becomes ambiguous.

how to solve this:

Don't abbreviate, just type the extra keystrokes.

How to suppress these warnings:

You can put the directive {&_proparse_ prolint-nowarn(abbrevkwd)} directly before
the statement that contains abbreviated keywords. See also: suppress warnings.

implementation:
This rule is implemented in Turbolint.


abbrevtable

Abbreviation of table &1 used (&2)"

Rule "abbrevtable" gives this warning when it finds an abbreviated table name.

FOR EACH custome NO-LOCK:  /* abbreviated "customer" to "custome" !! */
    DISPLAY custome.customer-name.  
END.

the risc:

The abbreviated table name may become ambiguous after you add a new table to the database. In the example, imagine someone adds table "custome" to the schema...

known issues:

This rule does not run in Progress version 8. If you run Prolint in Progress 8, this rule will simply do nothing.

The rule only looks at actual table names, not field names. For example:

FOR EACH custome NO-LOCK:  /* this will be noticed */
    DISPLAY custome.customer-name.  /* but this will not be noticed!! */
END.

how to solve this:

Do not use abbreviated table names.

How to suppress these warnings:

You can put the directive {&_proparse_ prolint-nowarn(abbrevtable)} directly before
the statement. See also: suppress warnings.


alertmessage

MESSAGE not an Alert Box

Rule "alertmessage" gives this warning when it finds a MESSAGE statement without the VIEW-AS ALERT-BOX option.

the risc:

GUI looks bad when SESSION:APPL-ALERT-BOXES happens to be FALSE.

How to suppress these warnings:

You can put directive {&_proparse_ prolint-nowarn(alertmessage)} directly before the MESSAGE statement.
See also: suppress warnings.


allfinds

FIND|CAN-FIND FIRST|PREV|LAST|NEXT buffer

Rule "allfinds" gives this warning for every FIND or CAN-FIND that has a FIRST|PREV|NEXT|LAST qualifier. This is to warn for compatibility with Oracle dataservers: Oracle has some issues with find first, PSC recommends they get converted to
something like "for first" or "open query".


andorparens

use parentheses when mixing AND and OR

Prolint rule "andorparens" raises this warning when it finds a mix of OR and AND without parentheses.

A recent discussion at one of the PSDN forums showed that not everyone agrees what the correct execution precedence is of the logical operators, but still are confident enough to not use parentheses. As a result the program without parentheses may behave different than expected, which may lead to bugs. Using parentheses at least helps to make clear what the programmer's intentions are.

Example:

  EACH customer WHERE customer.salesrep=filterSalesrep OR filterSalesrep="*" AND customer.creditlimit>1000

can be interpreted in two different ways (if there is doubt about the correct precendece rules).
So to avoid confusion please rewrite to

  EACH customer WHERE (customer.salesrep=filterSalesrep OR filterSalesrep="*") AND customer.creditlimit>1000

or

  EACH customer WHERE customer.salesrep=filterSalesrep OR (filterSalesrep="*" AND customer.creditlimit>1000)

backslash

backslash in string "&1" (not Unix-compatible)

Rule "backslash" gives this warning when it finds a string that contains a backslash, but ignores backslashes if they are preceeded by a tilde.

the risc:

String will behave strange on Unix, because backslash is an escape-character on Unix.

how to solve this:

Escape the backslash with a tilde, or replace the backslash by a forwardslash.

backslash in filename "&1" (not Unix-compatible)

Rule "backslash" gives this warning when it finds a filename that contains a backslash.

the risc:

Filename will not be valid on Unix.

how to solve this:

Replace the backslash by a forwardslash.


blocklabel

LEAVE/NEXT should specify a blocklabel

Rule "blocklabel" gives this warning when it finds a LEAVE or NEXT statement without a blocklabel. For example:

  DO WHILE TRUE :
     FOR EACH customer :
        FOR EACH order OF customer :
            IF order.shipped THEN DO:
               DISPLAY order.shippingdate.
               NEXT.
            END.
            ELSE
               RUN something.
        END.
     END.
  END.

In this example, Prolint will give the warning for NEXT on the 6th line.

the risc:

It is not really clear on which iteration you want to perform the NEXT statement. Of course the compiler will make a decision and
actually we can predict what that decision is going to be, but still, there is no way of knowing which
block the programmer had in mind and we can also not read what the intended functionality is.

how to solve this:

Add a blocklabel, like:

audit_salesrep:
  FOR EACH SalesRep :
     FOR EACH customer OF SalesRep :
        FOR EACH Order OF Customer :
            IF order.shipped THEN DO:
               DISPLAY order.shippingdate.
               NEXT audit_salesrep.
            END.
            ELSE
               RUN something.
        END.
     END.
  END.

How to suppress these warnings:

You can put the directive {&_proparse_ prolint-nowarn(blocklabel)} directly before
the NEXT/LEAVE statement. See also: suppress warnings.

implementation:
This rule is implemented in Turbolint.


bufdbfunc

no buffer defined for table in function

rule "bufdbfunc" requires that DEFINE BUFFER statements exist for every database buffer that appears in the code for a user-defined function.

Example:

FUNCTION SomethingStupid RETURNS LOGICAL :
    IF customer.cust-name = "jurjen" THEN 
       customer.cust-name = "john".
    RETURN FALSE.
END FUNCTION.

should have its own local buffer, like for example

FUNCTION SomethingStupid RETURNS LOGICAL :
    DEFINE BUFFER customer FOR customer.
    IF customer.cust-name = "jurjen" THEN 
       customer.cust-name = "john".
    RETURN FALSE.
END FUNCTION.

That way, you prevent side-effects from changing a buffer that may also be used by other internal proedures or functions. You also prevent locking issues, which are likely to happen when the procedure runs persistent.


bufdbmeth

no buffer defined for table in method

rule "bufdbmeth" requires that DEFINE BUFFER statements exist for every database buffer that appears in the code for a method (in a class).

Example:

METHOD PUBLIC VOID SomethingStupid :
    IF customer.cust-name = "jurjen" THEN 
       customer.cust-name = "john".
END METHOD.

should have its own local buffer, like for example

METHOD PUBLIC VOID SomethingStupid :
    DEFINE BUFFER customer FOR customer.
    IF customer.cust-name = "jurjen" THEN 
       customer.cust-name = "john".
END METHOD.

That way, you prevent side-effects from changing a buffer that may also be used by other methods. You also prevent locking issues, which are likely to happen because class files stay in memory for some time (much like persistent procedures).

Note:
The rule does not only look at methods, but also at constructors, destructor, property_getters and property_setters.


bufdbproc

no buffer defined for table in internal procedure

rule "bufdbproc" requires that DEFINE BUFFER statements exist for every database buffer that appears in the code for an internal procedure.

Example:

PROCEDURE SomethingStupid :
    IF customer.cust-name = "jurjen" THEN 
       customer.cust-name = "john".
END PROCEDURE.

should have its own local buffer, like for example

PROCEDURE SomethingStupid :
    DEFINE BUFFER customer FOR customer.
    IF customer.cust-name = "jurjen" THEN 
       customer.cust-name = "john".
END PROCEDURE.

That way, you prevent side-effects from changing a buffer that may also be used by other ip's or functions. You also prevent locking issues, which are likely to happen when the procedure runs persistent.


colon-t

attrib :T will trim [string]

Rule "colon-t" gives this warning when it finds a string literal that has the :T string attribute,
while the string begins or ends with whitespace. The string will be trimmed at run time.

This is most often caused by adding :T attributes to statements like:

  status = "there are " + string(inventory) + " items in inventory.".

After adding :T attributes, the source looks like

  status = "there are ":T + string(inventory) + " items in inventory.":T.

But the result looks pretty funny: there are5items in inventory.

how to solve this:

Try status = SUBSTITUTE ("there are &1 items in inventory.":T, string(inventory)).

How to suppress these warnings:

You can put directive {&_proparse_ prolint-nowarn(colon-t)} directly before the statement that contains string literals.
See also: suppress warnings.

implementation:
This rule is implemented in Turbolint.


compiler

Compile error

Prolint tried to compile the sourcefile but the compiler failed. Prolint only works on compilable sources.

Prolint requires a subdirectory "temp" in the current directory where it will attempt to write files "prolint.lst" and "prolint.xrf".
Please make sure this directory exists.

If the "temp" directory exists, please compile the sourcefile to see if there are any compiler messages.


contains

CONTAINS operation used to search [buffer] (word index)

Rule "contains" gives this warning when it finds a statement that contains the CONTAINS keyword. This is to warn for compatibility with Oracle dataservers: Oracle has no word indexes.


create

CREATE [tablename] table statement used in this program

This rule is for Oracle dataservers.

Rule "create" gives this warning when it finds a CREATE statement for creating a new record. The Oracle dataserver does not write the record until the end of the transaction, you have to "post" the record using VALIDATE or RELEASE if you need to read the record before the transaction ends.

For example:

  Def buffer c for customer.
  Create customer.
  Assign custnum = 100.
  /* validate customer.*/
  Find first c where custnum = 100.  /* This will fail unless the validate is uncommented*/

How to suppress these warnings:

You can put directive {&_proparse_ prolint-nowarn(create)} directly before the CREATE statement.
See also: suppress warnings.


dbtrigger

DISABLE TRIGGERS used - investigate

Rule "dbtrigger" gives this warning when it finds a DISABLE TRIGGERS statement.

the risc:

Schema triggers are defined to guarantee data consistency and are part of the data model.
An application should (normally) have no reasons to disable schema triggers, although some exceptions
are probably valid. Anyway, the use of DISABLE TRIGGERS is considered a red flag that needs to be reviewed.

How to suppress these warnings:

You can put directive {&_proparse_ prolint-nowarn(dbtrigger)} directly before
the DISABLE statement.
See also: suppress warnings.


defaultframe

undefined frame

The rule "defaultframe" gives this message when it finds instances of DISPLAY and UPDATE statements that are not using a previously defined frame.

Why use this rule?
This can be useful when you are trying to identify user interface components in your code base. It can also be helpful for applying standards.

Solution
Frames should be defined with either a DEFINE FRAME or FORMAT ... WITH statement.

In the following very simple example, the first display statement does not reference a frame and will trigger the rule.

/* myframe.p */
define frame myframe
customer.name
customer.custnum
with 1 col.

form Customer.address with frame otherframe.

find first customer no-lock no-error.
if available customer then display customer except comments.
find next customer no-lock no-error.
if available customer then display customer.name customer.custnum with frame myframe.
/* end procedure */


defaultname

[name] is not a meaningful widget name

Rule "defaultname" gives this warning when it finds a widget which still has its default name, assigned by AppBuilder.


When you are using the AppBuilder and drop a new widget on a frame,
this new widget will have a default name like BUTTON-1 or FILL-IN-2. Those names are supposed to be replaced by a more
meaningful name.

the risc:

Source is difficult to understand when variables don't have meaningful names.

how to solve this:

Using AppBuilder, simply type a new name in the property sheet.

How to suppress these warnings:

You would have to put the directive {&_proparse_ prolint-nowarn(defaultname)} directly before
the DEFINE VARIABLE statement, but that is impossible because AppBuilder won't let you type in that code section. So actually you can hardly suppress this warning.
See also: suppress warnings.


do1

"DO:" contains only one statement

Rule "do1" gives this warning when it finds a DO: ... END block that contains only one statement.
The statement will work without the DO/END block around it.

Actually, the rule only looks at DO/END blocks in the THEN or ELSE branches of an IF statement:

IF condition THEN DO:
   /* this block is inspected */
END.
ELSE DO:
   /* this block is also inspected */
END.
DO:
  /* this block is not inspected because it is
     not in a THEN or ELSE branch. */
END.

Furthermore, the rule does NOT raise a warning when the DO...END block contains an IF statement. That is because an IF statement inside an IF statement is usually hard to read but adding a DO...END block improves readability. So, you get a bonus for improving readability :-)

IF condition THEN DO:
   /* only 1 statement in the DO..END block 
      but no warning, because it's an IF statement */
   IF a=b THEN 
      a = a + 8.
   ELSE
      a = b * b.
END.

Likewise, the rule does not warn if the only statement is a CASE, FOR, DO or REPEAT statement. This list is set in prolint.properties.p (see property "rules.do1.StatementSkipList") so you can customize it.

the risc:

The DO/END-block wastes about 60 bytes in R-code and affects run-time performance.

how to solve this:

Just remove the DO: and END.


dotcomment

PERIOD comments a statement

Rule "dotcomment" gives this warning when it finds a
statement that begins with a period, for example:

.MESSAGE "hello"
   VIEW-AS ALERT-BOX.

Such a statement behaves like a comment (that is terminated by the next period).
So the MESSAGE in this example will never execute.

the risc:

The period is easily overlooked, and not too many people
know that it starts a comment. Prolint will raise a warning because it regards the dot (or period) as a
typo or as something that was accidently inserted,
perhaps by preprocessors.

how to solve this:

Remove the period, or remove the entire statement, or
use the usual /* ... */ syntax if you really want to
comment out the statement.

How to suppress these warnings:

You can put the directive {&_proparse_ prolint-nowarn(dotcomment)} directly before
the statement. See also: suppress warnings.


emptyblock

empty code block

Rule "emptyblock" searches for code blocks that do not have any code in them. For example, there may be a a repeat block where the import statement has been commented out.


REPEAT: 
/* 
  CREATE customer. 
  IMPORT customer. 
*/ 
END. 

Technically speaking, this rule belongs to an existing rule that checks for code that has no effect - but in this case, the warning message is more detailed, providing info on the type of a block that is empty.

Notes:

  • The program locates 'Code_block' node and checks that node's first
    child. If child node is not found, then there was nothing in the
    block.
  • The rule is really to flag 'empty' blocks, not meaningless blocks (
    i.e. blocks that don't do anything, like just define local variables).
  • There may be a false positive when utilizing a super/persistent procedure, and a function has an input or output parameter that sets or returns a private variable or a table:
    DEFINE TEMP-TABLE ttData NO-UNDO 
        FIELD dataLine AS CHAR. 
    
    FUNCTION fnGetData RETURNS LOGICAL (OUTPUT TABLE ttData): 
    /* In this case the function is not really empty */ 
    END FUNCTION. 
    
    FUNCTION fnSetData RETURNS LOGICAL (INPUT TABLE ttData): 
    /* Code block is not useless either */ 
    END FUNCTION. 
    
  • The rule should be enhanced to check for the input/output/input-
    output function parameters at some point and suppress a warning if the
    parameter refers to a global object (temp-tables are always global).
    Not sure what to do about buffer parameters ...
  • It also needs to be enhanced to read the contents of non-empty
    blocks that have only local variables defined and no other code at
    all.

endtype

Type of END statement not qualified in PROCEDURE, FUNCTION, or CASE

Rule "endtype" gives this warning when it finds END statements for procedure, function, or case statements that do not have the end type specified. For example:

PROCEDURE pTest: 
   .... 
END. /* Rewrite to END PROCEDURE. */
CASE x:
    WHEN '1' THEN MESSAGE x.
END. /* Rewrite to END CASE. */

the risc:

None. This is really more of a question of company standards or coding style, where we want to be explicit in identification of END type.


errortext

RETURN ERROR should have a string argument

Rule "errortext" gives this warning when it finds a "RETURN ERROR" statement without a string argument. For example:

/* prolint raises a warning: */
IF NOT AVAILABLE customer THEN 
   RETURN ERROR.
/* this is fine with prolint: */
IF NOT AVAILABLE customer THEN 
   RETURN ERROR "no data available".

How to suppress these warnings:

You can put the directive {&_proparse_ prolint-nowarn(errortext)} directly before
the RETURN ERROR statement. See also: suppress warnings.


findstate

FIND statement [name] defined without qualifier [ FIRST | LAST | NEXT | PREV | CURRENT ]


Rule "findstate" gives this warning when it finds a "FIND" statement without [ FIRST | LAST | NEXT | PREV | CURRENT ] while the WHERE clause does not appear to select a unique index, and if there is no 'IF AMBIGUOUS' statement immediately following.


This rule does not look at temp-tables: those are inspected by rule "findstate-tt".

The risc:

The WHERE clause does not contain enough fields to bracket all fields in a unique index. If the FIND statement returns more than one record an error will occur when an attempt to access the record is made. Also, if not already searching on an Unique index, without the qualifier the PROGRESS engine will look to see if there is another record that matches the criteria increasing the search time.


Known issue:

The IF AMBIGUOUS statement may not directly follow the find statement but it could be in the code before the record is accessed.


How to solve this:

Use more fields in the WHERE clause or check the data dictionary to find out which fields are in a unique index. Make a habit of adding IF AMBIGUOUS immediately after the find statement.


How to suppress these warnings:


findstate-tt

FIND statement [name] defined without qualifier [ FIRST | LAST | NEXT | PREV | CURRENT ]


Rule "findstate-tt" gives this warning when it finds a "FIND" statement without [ FIRST | LAST | NEXT | PREV | CURRENT ] and has no 'IF AMBIGUOUS' statement immediately following.


This rule only looks at temp-tables. Database tables are inspected by rule "findstate"

The risc:

If the FIND statement returns more than one record an error will occur when an attempt to access the record is made. Also, if not already searching on an Unique index, without the qualifier the PROGRESS engine will look to see if there is another record that matches the criteria increasing the search time. However, even in the case of searching on an Unique index, it is better coding practice to add the qualifier as it indicates to other developers what your intent is.


Known issue:

The IF AMBIGUOUS statement may not directly follow the find statement but it could be in the code before the record is accessed.


How to solve this:

Make a habit of adding IF AMBIGUOUS immediately after the find statement.


How to suppress these warnings:


fnusage

Function name() [is prototyped but] not called in current program

Rule "fnusage" gives this warning when it finds a function or function-prototype that is never called. The function can be removed from the
source (but before you do, check if the function is not called
dynamically because dynamic calls are invisible to Prolint).


fortranoper

Rule fortranoper searches for the use of Fortran-style operators like:
EQ, GT, LT, GE, LE, NE (for =, >, <, >=, <=, <>).

I know some people may like this or are used to this, but others don't.


forwardparams

parameters in function don't match with forward declaration

Rule "forwardparams" gives this warning when it finds that a function implementation has different parameters than its forward declaration.
For example:

function myfunction returns logical(input something as character) forward.

function myfunction returns logical:
    if something>"" then 
       return true.
    else 
       return false.
end function.

Why
It is partly a matter of style and taste. It is also a possible source for confusion which may lead to errors: when you read the implementation (where no parameters are defined) it is not clear that 'something' is a parameter instead of a wide-scoped variable.

This is a contributed rule, not by default available in the Prolint setup. You can download it separately to your prolint/contribs/rules directory from http://websvn.oehive.org/listing.php?repname=prolint&path=/trunk/contrib...


groupassign

Possibly group ASSIGN with line ...

Rule "groupassign" gives this warning when it finds several ASSIGN
statements that could have been grouped together into one single
ASSIGN statement.

Notes:

Possible issues with this rule that may need to be fixed or not:

  • You can't have call a UDF an assign after writing to a key field.
  • Sometimes a large assign statement is split on purpose, because there is a compiler limit for the number of characters in a single statement.
  • It could be argued that intentional ASSIGN splits should all use the ASSIGN keyword. Then a variation of the rule could just report on cases where the ASSIGN keyword isn't used.
  • sometimes you want separate ASSIGN statements for its NO-ERROR option.
  • AppBuilder generated ASSIGN statements are not always grouped and would lead to false positives.
  • Possible assign grouping within BUFFER-COPY is not checked:
    BUFFER-COPY customer TO ttCustomer. 
    ASSIGN ttCustomer.customer-name = 'Old Name'. /* May be grouped with Buffer-Copy statement */ 
    

i18nlength

LENGTH, OVERLAY, or SUBSTRING called without TYPE parameter

Rule "i18nlength" gives this warning when it finds a call to
LENGTH, OVERLAY, or SUBSTRING which did not provide the
"type" parameter: "CHARACTER," "RAW," or "COLUMN".

(the SUBSTRING function can also take a value of "FIXED")

the risc:

The source is not ready to support Unicode.

how to solve this:

Provide the "type" parameter.

How to suppress these warnings:

You can put directive {&_proparse_ prolint-nowarn(i18nlength)}
directly before the statement that contains the offending function.
See also: suppress warnings.


idiskeyword

name of [variable|parameter|temp-table|field] &1 is a keyword

Rule "idiskeyword" gives this warning when you define a variable (or parameter, or anything else) and give it a name which is also a valid Progress keyword.
Some examples:

   /* x is a keyword */
   define variable x as integer no-undo.
   /* object, code and width are keywords */
   define temp-table object
      field code as character 
      field width as decimal.

the risc:

Well, I just don't like it. It is confusing to code editors with syntax highlighting.

how to solve this:

rename the object.

How to suppress these warnings:

You would have to put the directive {&_proparse_ prolint-nowarn(idiskeyword)} directly before
the DEFINE statement.
See also: suppress warnings.


ifindent details

ifindent1: IF statement with indenting that could indicate a bug

ifindent2: IF statement with questionable indenting or couldn't check

These two rules share the same code base, but the warnings are separated into
major warnings (from ifindent1) and
minor warnings (from ifindent2).
This allows the more important warnings to have a higher severity.

If you want to run both rules "ifindent1" and "ifindent2", you might as well run rule ifindent instead to double the performance.

The rules output a variety of warnings for different situations. The major,
or 100-level, warnings will generally indicate either possible bugs or bad
indenting, and the minor, or 200-level, warnings generally do not indicate
bugs, but rather are IF statements which for various reasons cannot be checked
for potential bugs.

how these rules work

These rules have a somewhat complicated implementation. What they do is assess
your code in the same manner that the compiler will with regard to the grouping
of IF and ELSE blocks. What the compiler does may not be what you intended,
but at runtime, that is what will happen. Your indentation suggests how you
intended for your code to work, so we compare how the compiler will look at
it versus what your indentation implies, and report the differences.

This rule tests that all children nodes of an IF node have a greater than
or equal indent to the IF node, and also tests the next statement after
the IF to make sure it has an indent equal to the IF's indent. These two
checks ensure that the statements controlled by the IF block (as determined
by the compiler) are indented from it, and the statements outside and
following the IF block (as determined by the compiler) are not controlled
by the IF block. Note that this approach can occasionally result in more
than one warning being given for a particular line.

An example of the kinds of bugs found by this rule:

 
IF ok = TRUE THEN               
  ASSIGN c1 = "correct". 
  IF other = TRUE THEN RETURN. 
ELSE                    /* Should be for "ok = TRUE" but actually is for "other = TRUE" */
  ASSIGN c1 = "oops". 
IF other THEN           
  ASSIGN v1 = "Hello".

In this example, Prolint will give three warnings:

  • "#101: Node IF has greater indent than IF on line 1. Expected to be 0, is 2."

    The IF at line 3 is not controlled by the IF ok = TRUE
    on line 1, but the code indenting implies that it is.

  • "#102: More indent expected for node ELSE. Node's indent should be at least 2, is 0."

    This refers to the ELSE on line 4. It occurs because the IF other
    on line 3 is the IF that the ELSE on line 4 is paired
    to, and given the IF other's current
    indent of 2, the rule says that the ELSE on line 4 for it should also
    have an indent of 2. This is where the code bug is - adding the missing
    DO... END. statements to the IF ok = TRUE on line 1 will result in
    the ELSE being correctly paired to the IF on line 1.

  • "#202: Node IF has less indent than IF on line 3. Expected to be 2, is 0."

    This warning follows on from the current situation - the IF on
    line 7 is not controlled by either of the previous IFs, so its' indent
    should be equal to the previous statement. Unfortunately, this previous
    statement is (incorrectly) the IF on line 3. This warning
    will be resolved simply by fixing the real problem, the missing DO.

the risc:

It is not really clear that the ELSE on line 4 is paired to the IF
on line 3, which causes a bug by changing the conditional execution of the code.

how to solve this:

If the programmer fixes the blocking by adding in the missing DO...END, all
of these messages will go away without any changes to the indenting. Alternately,
the programmer could try fixing the first indent to the recommended amount,
then rerunning the rule to see if this has removed the rest of the warnings.

The corrected code:

IF ok = TRUE THEN DO:              
  ASSIGN c1 = "correct". 
  IF other = TRUE THEN RETURN. 
END.
ELSE                    /* Is now for "ok = TRUE" as intended */
  ASSIGN c1 = "oops". 
IF other THEN           
  ASSIGN v1 = "Hello".

After this correction, all warnings are prevented.

indent suggestions

These rules' suggested indents aren't enforcing a particular indenting
standard, but are simply the suggestions which would result in the indenting
being consistent with the blocking the compiler will use. It is assumed that
the program's indenting reflects the following model:

  • a block that follows an IF... THEN block will have the same indent as
    the IF. (as it is not controlled by the IF - it is a sibling of the IF and
    so should have the same indent level).

  • a statement contained within an IF block will be indented as much or
    more than the IF, as it is controlled by the IF.

    warning messages given

    • 101: important: indicates possible code bug. Next statement node has greater indent than the IF, falsely implying that it is controlled by the IF.
    • 102: important: indicates possible code bug. A subnode of the IF has less indent than the IF, implying that it is NOT controlled by the IF.
    • 201: informational. The next statement after the IF isn't in the same file as the IF,
      so we can't meaningfully compare indents.

    • 202: informational. The next statement node after the IF has less indent than the IF.
    • 203: informational. A subnode of the IF is in a different file, so no meaningful
      indent comparison can be made on this node. Only one of these warnings are
      output per IF statement, as an indicator.

    If it's hard to see why the rules are giving a certain warning, try changing
    the code's indent as it suggests which may cause the problem/possible
    bug to become obvious.

    Alternately, you can run the program through COMPILE...PREPROCESS
    then have a look at the code where the warning comes up - the problem
    may then become apparent.

    known problems


    Tabs:


    Prolint treats Tab characters as only one single character, although they should contribute to a larger indent. We can fix that if you want.

    false positives

    Preprocessing:

    As with all rules which use Proparse, this rule operates against code
    which is effectively preprocessed. Code which contains preprocessor
    directives will frequently cause false positives, due to the
    programmer's indenting in from the &IF etc. for readability. In the
    resulting preprocessed code, these just look like their indents are
    wrong. The number of false positives is reduced by omitting indent
    checks on AppBuilder-generated code, but some will still remain.
    Preprocessors can also cause false positives when a preprocessor
    is defined to contain more than one Progress statement. If the rule
    tries to calculate the indent of the second statement in the preprocessor,
    it will have some huge indent like 497, and this may cause warnings.

    For example:

    &scop PART1 IF TRUE THEN FIND FIRST order NO-LOCK NO-ERROR.
    &scop PART2 IF AVAILABLE order THEN MESSAGE "avail order" VIEW-AS ALERT-BOX.
    &scop ALLPARTS {&PART1} {&PART2} 
    IF TRUE THEN DO: 
      MESSAGE "TRUE" VIEW-AS ALERT-BOX. 
    END.
    ELSE DO:
      {&ALLPARTS} 
    END.
    

    The IF statement contained in &PART2 has an indent of about 50,
    because after the preprocessing has been done, this IF is on the
    same line as the IF from &PART1.

    Bad Indenting:


    These are probably when the developer didn't notice the indenting wasn't quite right.

    Tabs:


    (As above) Prolint treats Tab characters as only one single character, although they appear much wider to the user. We can fix that if you want.

    Include Files:


    Occasional false positives occur on nodes either at the beginning of,
    or immediately after, include files. This occurs because of the way
    the white space is handled when entering or leaving includes.

    more examples


    An example of a warning from the rules that doesn't quite tell you the correct problem,
    just that there is one here:

     
        RUN myProg(var1,var2,OUTPUT retvar).
            IF retvar <> 0 THEN MESSAGE("Failed.") 
                           VIEW-AS ALERT-BOX.
        RUN yourProg (var3, OUTPUT retvar).
    

    The warning given for this code is that the indent is expected to be 8 but is 4,
    for node RUN on line 4. This is because we're only checking
    indents on IF nodes, their subnodes, and their next statement node after.
    We aren't checking that the IF has a sensible indent in the first place,
    but we're able to see that the second RUN (IF's
    next statement following) doesn't have a sensible indent when compared to
    the IF.

    what's not checked by the rule

    IF functions. This rule only checks IF statements.

    Indents of nodes in different files:


    It's pretty meaningless to try to check any indents of subnodes which are
    in a different file. If they're subnodes of the IF, they're in some
    include and could have any indenting scheme, and since they may
    not be stand-alone statements, there's nothing to compare indents
    to. Also, some programmers will write portions of a statement in one
    include and other portions in another include or the main procedure.

    Therefore, indents are not checked across files - if an IF is in
    one file and some of its' children statements in the block are
    in another file (an include), these children are ignored.

    In this example, we can check the indent of the ASSIGN node, but not of any of the nodes inside the {someinclude.i}.

         IF ... THEN DO:
           ASSIGN var = "blue".
           {someinclude.i}
         END. 
    

    How to suppress these warnings:

    You can put the directive {&_proparse_ prolint-nowarn(ifindent1)} or
    directly before {&_proparse_ prolint-nowarn(ifindent2)}
    the statement. See also: suppress warnings.


    ifindent

    ifindent: IF statement with inconsistent indenting

    This page is only a quick overview of this rule, for more details see "ifindent-details".

    Rule "ifindent" will by default not run, because it is not listed in file prolint/rules/rules.d.
    Instead, the rules ifindent1 and
    ifindent2 are listed by default, these two rules include the entire source of "ifindent".

    If you plan to use both "ifindent1" and "ifindent2", you may consider to use "ifindent" instead because it will perform better.


    In that case, follow these steps:

    1. copy prolint/rules/ifindent1.p to prolint/custom/rules/ifindent1.p
    2. remove all of the contents from prolint/custom/rules/ifindent1.p but be sure to keep the parameters: include

      {prolint/core/ruleparams.i}

    3. create a file prolint/custom/rules/rules.d similar to prolint/rules/rules.d and make sure it contains a reference to "infindent1".

      You now have a stub override for ifindent1.p.

    4. Repeat steps 1-3 for ifindent2.p
    5. copy prolint/rules/ifindent1.p to prolint/custom/rules/ifindent.p
    6. include {prolint/rules/ifindent.p} in prolint/custom/rules/ifindent.p, remove all other lines.
    7. add "ifindent" to file prolint/custom/rules/rules.d

    How to suppress warnings:



    You can put the directive {&_proparse_ prolint-nowarn(ifindent)} directly before
    the IF statement. See also: suppress warnings.

    implementation:
    This rule is implemented in Turbolint.


    ifindent1

    ifindent1: IF statement with indenting that could indicate a bug

    This page is only a quick overview of this rule, for more details see "ifindent-details".


    When you run rule "infindent1", you are actually running rule "infindent" but without the informational warning messages.

    Rule "ifindent1" gives this warning when it finds an IF statement with confusing indentation.

      IF ready THEN              
         ASSIGN 
            a = x
            b = y.
            c = z.                  

    It looks like the second statement ( "c = z." ) will only be executed when the IF-condition is true.

    the risc:

    Confusing layout, the compiler may not do what the programmer intended

    how to solve this:

    In the given example, the statement "c = z." should have less indentation - same as the IF keyword.

    How to suppress these warnings:

    You can put the directive {&_proparse_ prolint-nowarn(ifindent1)} directly before
    the IF statement. See also: suppress warnings.

    implementation:
    This rule is implemented in Turbolint.


    ifindent2

    ifindent2: IF statement with questionable indenting or couldn't check

    This page is only a quick overview of this rule, for more details see "ifindent-details".


    When you run rule "infindent2", you are actually running rule "infindent" but only for the informational warning messages.

    warning messages given

    • 201: informational. The next statement after the IF isn't in the same file as the IF,
      so we can't meaningfully compare indents.

    • 202: informational. The next statement node after the IF has less indent than the IF.
    • 203: informational. A subnode of the IF is in a different file, so no meaningful
      indent comparison can be made on this node. Only one of these warnings are
      output per IF statement, as an indicator.

    How to suppress these warnings:

    You can put the directive {&_proparse_ prolint-nowarn(ifindent2)} directly before
    the IF statement. See also: suppress warnings.

    implementation:
    This rule is implemented in Turbolint.


  • ifparens

    IF function is confusing, use parentheses

    Rule "ifparens" gives this warning when it finds an IF function (not
    an IF statement) where ELSE is followed by an operator. This may cause
    confusion, which can be solved by putting parentheses around the IF
    function.

    ASSIGN answer = IF x>0
                       THEN 10
                       ELSE 20 
                    + 3000.
    DISPLAY answer.
    

    'answer' will be 3020 when x<=0.

    'answer' will be 10 (not 3010) when x>0. The programmer may have
    been confused and intended the value of 'answer' to be 3010.

    the risc:

    The IF function can be very confusing, depending on how the code is indented.
    There is a good chance that the statement containing the IF function does not
    behave how the programmer intended. Parentheses around the IF fuction
    can make a huge difference and will certainly make the statement easier to
    understand.

    how to solve this:

    /* simply add parentheses around the IF function: */
    ASSIGN answer = (IF x>0
                       THEN 10
                       ELSE 20) 
                     + 3000.
    
    /* or: */
    ASSIGN answer = IF x>0
                       THEN 10
                       ELSE (20 + 3000).
    

    How to suppress these warnings:

    You can put the directive {&_proparse_ prolint-nowarn(ifparens)} directly before
    the statement that contains the IF function. See also: suppress warnings.


    inclowercase

    Compile will fail on Unix, use only lower-case includefiles

    Rule "inclowercase" will raise this warning when it finds an include directive that has uppercase characters. For example:

       {subdirectory/IncludeFile.i}
    

    The reason for this warning is that this sourcefile will not compile on Unix. Change the name to all lowercase so the source can be compiled on Unix and on Windows.
    The assumption is of course, that sourcefiles are FTP'd to Unix with the "lowercase name conversion" option set in the FTP program.

    Compiled r-code used to be platform indepedent: you could develop on Windows, compile on Windows and simply FTP the r-code to Unix and it would just run fine (even though Progress did not actually guarantee that).
    But this breaks if you develop on 32-bit Windows and deploy to 64-bit Unix: you just have to compile on the target platform to get working r-code. This rule helps to locate include directives that won't compile on Unix in your new and existing code.


    incslash

    Compile will fail on Unix, don't use backslash in includefiles

    Rule "incslash" will raise this warning when it finds an include directive with backslashes. For example:

       {subdirectory\includefile.i}
    

    The reason for this warning is that this sourcefile will not compile on Unix. Change the backslash into a forward slash so the source can be compiled on Unix and on Windows.

    Compiled r-code used to be platform indepedent: you could develop on Windows, compile on Windows and simply FTP the r-code to Unix and it would just run fine (even though Progress did not actually guarantee that).
    But this breaks if you develop on 32-bit Windows and deploy to 64-bit Unix: you just have to compile on the target platform to get working r-code. This rule helps to locate include directives that won't compile on Unix in your new and existing code.


    leavemodal

    Modal Leave Trigger -- has RETURN NO-APPLY

    Rule "leavemodal" gives this warning when it finds a LEAVE trigger in the user-interface code that contains a RETURN NO-APPLY statement.

    "return no-apply" in a leave trigger is bad because it creates a modal UI, where the user isn't free to
    enter data in the order they choose. 99% of the time it can be fixed by just removing the line. You need to have code later (e.g. at save time) to validate the data anyway, since you can't guarantee that the user will put focus in every field.


    lexcolon

    block header should terminate with a COLON

    Rule "lexcolon" identifies any block header that does not terminate with a colon. It checks PROCEDURE, FUNCTION, FOR, REPEAT, and DO.

    Examples of such headers would be:

    FOR EACH customer NO-LOCK. /* Rewrite to FOR EACH customer NO-LOCK: */ 
        DISPLAY customer. 
    END. 
    
    PROCEDURE.  /* Rewrite to PROCEDURE: */ 
       MESSAGE 'HELLO'. 
    END PROCEDURE. 
    

    This is purely an issue of style, not substance - and in some cases increases readability of the source code.


    matches

    MATCHES statement used in this program

    Rule "matches" gives this warning when it finds a MATCHES keyword.

    the risc:

    MATCHES is generally bad for indexing.

    How to suppress these warnings:

    You can put directive {&_proparse_ prolint-nowarn(matches)} directly before
    the the statement that contains the MATCHES keyword.
    See also: suppress warnings.


    maxchar

    String constant too long for Tranman

    Rule "maxchar" gives this warning when it finds a string constant that is longer that 188 characters,
    unless the string has the :U attribute (untranslatable).

    the risc:

    "Translation Manager" can not import those long strings. It has an index on string value, and indexes in Progress are
    limited to 188 chars max.

    how to solve this:

    Cut the string constant in smaller pieces.

    implementation:
    This rule is implemented in Turbolint.


    message

    use MESSAGE only in debug-mode

    Rule "message" gives this warning when it finds a MESSAGE statement, except if the MESSAGE statement is found in ADM or ADM2 includefiles or
    if the MESSAGE statement was generated by the Application Builder (like the message that says that the specified .wrx could not be found).

    the risc:

    Many frameworks are using alternative error handling functions not including the MESSAGE ... VIEW-AS ALERT-BOX statement, like dialogs with context-sensitive help or built-in logging functionality.
    In organizations where such frameworks are used, it is usually forbidden to use the MESSAGE statement and use the alternative instead; this rule helps to enforce this.


    While debugging a program it is common practice to place some MESSAGE statements in the source. Unfortunately, programmers sometimes forget to remove some of those MESSAGE statements
    at deployment.

    how to solve this:

    If it's a MESSAGE statement used for debugging, just delete the statement.

    Else, replace the MESSAGE statement by a call to your error handling function.

    How to suppress these warnings:

    You can put directive {&_proparse_ prolint-nowarn(message)} directly before
    the MESSAGE statement.
    See also: suppress warnings.


    messagetype

    ALERT BOX is missing alert type

    Rule "messagetype" gives this warning when it finds a MESSAGE VIEW-AS ALERT-BOX statement that does not specify a messagetype like ERROR, WARNING, INFORMATION, QUESTION or MESSAGE.

    the risc:

    Not really a risc, but the MessageBox does not have an icon.

    How to suppress these warnings:

    You can put directive {&_proparse_ prolint-nowarn(messagetype)} directly before
    the MESSAGE statement.
    See also: suppress warnings.


    mustincludepcf

    Batch Programs Must Include PCF

    The PCF rule gives a warning whenever the following include is not found in the compilation unit: {sys/batch_api.i}. This is a rule specific to EarthLink. All batch jobs must invoke the Process Control Framework and report their status to it. This rule should be disabled by anyone not at EarthLink. If encountered in a non-batch program, this indicates that the developer is using the wrong rule profile.

    The Risk:
    Violation of EarthLink coding standards.

    How to solve this:
    Include {sys/batch_api.i} somewhere near the top of the program.

    How to suppress these warnings:
    Disable the rule or run a rule profile that does not have it enabled.


    nameconv

    VARIABLE .... should start with "..."

    Rule "nameconv" looks at the names of variables (and other names like parameters, temptables and procedures) to check
    for naming conventions.

    How to customize this rule:
    Since naming conventions are different in every organization, you
    will probably want to customize this rule. To do so you only have to
    override procedure "prolint/rules/namecheck.p":

    Do not change procedure "prolint/rules/namecheck.p", because your changes will
    be overwritten when you upgrade Prolint. Instead, copy
    "prolint/rules/namecheck.p" to "prolint/custom/rules/namecheck.p" and
    then change your custom copy.


    nestedfunc

    function is defined inside a code block

    Prolint rule "nestedfunc" warns when it finds a function definition (or a procedure definition) inside a code block.
    For example:

    FOR EACH customer :
        FUNCTION Foo RETURNS CHARACTER (city AS CHARACTER) :
        END FUNCTION.
    END.
    

    ... because it is plain silly and ugly to do that, even if the compiler accepts it.
    Functions (and procedures) should not be defined inside code blocks.


    nobrackets

    handle:[METHODNAME] should have brackets

    Rule "nobrackets" gives this warning when it finds a handle:method statement without brackets. The following methods are watched for by this rule:

         
         hQuery:OPEN-QUERY.
         hQuery:GET-FIRST.
         hQuery:GET-NEXT.
         hQuery:GET-PREV.
         hQuery:GET-LAST.
         hQuery:CLOSE-QUERY.
    

    Each of these methods should really end with brackets, just like any other method. Other methods (not included in this list) will be reported by rule "noeffect" because methods without brackets look like properties.

    how to solve this:

    Use brackets, like in the following example:

         
         hQuery:OPEN-QUERY().
         hQuery:GET-FIRST().
         hQuery:GET-NEXT().
         hQuery:GET-PREV().
         hQuery:GET-LAST().
         hQuery:CLOSE-QUERY().
    

    How to suppress these warnings:

    You can put directive {&_proparse_ prolint-nowarn(nobrackets)} directly before
    the offending statement.
    See also: suppress warnings.


    nobreakby

    Unnecessary Break By encountered

    The rule “nobreakby” gives a warning if it encounters a “BREAK BY” without a corresponding FIRST() FIRST-OF() LAST() or LAST-OF() function. This can be solved by removing the word “BREAK”.

    The Risk:
    A BREAK BY causes extra sorting before processing the selected record set. If you are writing a report that needs control breaks, this can save a bit of effort. If you do not need the break points, you are adding unnecessary overhead to your program – BY without the BREAK will give you the sort order you need.

    How to solve this:
    Either add a FIRST/FIRST-OF/LAST/LAST-OF() function or remove the BREAK keyword.

    How to suppress these warnings:
    You can put the directive {&_proparse_prolint-nowarn(nobreakby)} directly before the statement that contains the BREAK BY keyword. See also: suppress warnings.


    nocomment

    PROCEDURE/FUNCTION [name] is not commented

    Rule "nocomment" gives this warning when it finds an internal procedure or user-defined function which does not begin with a comment.

    PROCEDURE WhatAreWeDoingHere :
       DEFINE INPUT  PARAMETER dx AS DECIMAL NO-UNDO.
       DEFINE INPUT  PARAMETER dy AS DECIMAL NO-UNDO.
       DEFINE OUTPUT PARAMETER dz AS DECIMAL NO-UNDO.
       
       - a bunch of arcane code here -
       
    END PROCEDURE.
    

    An unmodified default UIB-generated comment is NOT accepted as a valid comment.

    the risc:

    Too less comments make it difficult to review code. At the least one might expect that procedures and functions document what their purposes are.

    how to solve this:

    Add a comment directly before the first statement inside the procedure (or function):

    PROCEDURE WhatAreWeDoingHere :
    /* purpose : in this procedure we will do an attempt to ....
       params  : ...  */
       DEFINE INPUT  PARAMETER dx AS DECIMAL NO-UNDO.
       DEFINE INPUT  PARAMETER dy AS DECIMAL NO-UNDO.
       DEFINE OUTPUT PARAMETER dz AS DECIMAL NO-UNDO.
       
       - a bunch of arcane code here -
       
    END PROCEDURE.
    

    Or write the comment directly before the procedure (or function):

    /* WhatAreWeDoingHere : in this procedure we will do an attempt to ....
       params  : ...  */
    PROCEDURE WhatAreWeDoingHere :
       DEFINE INPUT  PARAMETER dx AS DECIMAL NO-UNDO.
       DEFINE INPUT  PARAMETER dy AS DECIMAL NO-UNDO.
       DEFINE OUTPUT PARAMETER dz AS DECIMAL NO-UNDO.
       
       - a bunch of arcane code here -
       
    END PROCEDURE.
    

    How to suppress these warnings:

    You can put the directive {&_proparse_ prolint-nowarn(nocomment)} directly before
    the PROCEDURE/FUNCTION keyword. See also: suppress warnings.


    noeffect

    statement has no effect

    Rule "noeffect" gives this warning when it finds a statement which does
    nothing. For example, these are a valid statements, but they do nothing:

         
         1 + 1.
         hEditor:READ-ONLY.

    These statements compile and run fine, but nothing happens. They probably don't do what the programmer intended to do, so they are probably errors.

    Less obvious, and more likely to cause problems, is the following statement.
    The programmer might have expected the EQ to do an assignment,
    but in fact, EQ only does a comparison. This statement does
    nothing other than an equality comparison between i and 12:

         i eq 12.

    Known issue: methods without brackets:

    Progress allows to discard brackets on method calls when there are no parameters, for example:

         hTT:CREATE-BUFFER 

    instead

         hTT:CREATE-BUFFER() 

    The first form will raise the "Statement has no effect" warning although the statement does have effect.
    The reason is that Prolint has to assume that :CREATE-BUFFER is an attribute instead of a method, simply because it doesn't see brackets.

    Rule noeffect excludes warnings for the following list of methods: QUERY-OPEN,GET-NEXT,GET-FIRST,GET-PREV,GET-lAST,CLOSE-QUERY. The reason why they are excluded is that these methods are very commonly used without brackets, the enormous number of warnings from these methods obscure the more important warnings with real bug potential. To compensate for excluding these methods from "noeffect", there is another rule "nobrackets" which reports these methods only.

    the risc:

    The source does nothing, and is probably an programming error.

    how to solve this:

    Don't use statements that have no effect.

    Use brackets on object methods like hTT:CREATE-BUFFER().

    How to suppress these warnings:

    You can put directive {&_proparse_ prolint-nowarn(noeffect)} directly before
    the offending statement.
    See also: suppress warnings.


    noerror

    FIND without NO-ERROR

    Rule "noerror" gives this warning when it finds a FIND statement without NO-ERROR.

    example:

    FIND FIRST customer NO-LOCK. 

     

    the risc:

    The program may raise a run-time error when the requested record is not found.

    How to resolve this:

    Do not assume that the record will be found: add NO-ERROR and IF AVAILABLE logic.


    noglobaldefine

    Global Defined Preprocessors are not allowed

    The rule “noglobaldefine” presents a warning when it encounters an uncommented string “&GLOB”. This practice, while necessary in certain limited cases, encourages bad practices and makes code very difficult to maintain.

    The Risk:
    As a general rule, the average programmer cannot explain the difference between &GLOBAL-DEFINE and &SCOPED-DEFINE. As a result, they tend to use Globals much more than they should. Using a globally defined preprocessor at the top of a program is the same as a scoped defined preprocessor in the same location. Using it buried in an include (where it is intended to be used) makes it very difficult to view its impact.

    How to solve this:
    Change it to a &SCOPED-DEFINE if at all possible, move it to the parent file if necessary. Consider all other options prior to using it, then if you must use it for your circumstance…

    How to suppress these warnings:
    You can put the directive {&_proparse_prolint-nowarn(noglobaldefine)} directly before the definition of the preprocessor. See also: suppress warnings.


    nohardcodeemail

    Hard Coded Email Addresses are not allowed

    The rule “nohardcodeemail” gives a warning when it finds what appears to be a hard coded email address within a uncommented literal. It looks for any occurrence of “x@x” where x is any string.

    The Risk:
    Email addresses change over time, and the proper email address should be retrieved from some database or configuration file source rather than hardcoding it in a program.

    How to solve this:
    Get the email address out of the database, or fool the system by stringing it together x + @ + x.

    How to suppress these warnings:
    See how to solve above.


    nolike

    LIKE is Not Allowed When Defining Variables Or Temp Table Fields

    The rule “nolike” presents a warning when it encounters a definition of a variable, parameter, or temp-table field “like” a field in the database. Defining a temp table like another or like a database table is acceptable. It is variables, parameters, and fields that are not.
    The Risk:
    Using LIKE in code embeds the CRC of the referenced table in your program. If this is the only reference to the table in your program, it can be missed in cross-reference searches and not be noticed as needing a recompile when the table changes. It also requires the maintenance programmer to know the attributes of the table.field, and copies far more attributes into the r-code than is likely that the coder intended. It also expands the scope of any regression testing that may need to occur if the referenced table.field (or even table) is modified – often unnecessarily.
    How to solve this:
    Hand-write the variable/field specifications in your code.

    How to suppress these warnings:
    You can put the directive {&_proparse_prolint-nowarn(nolike)} directly before the statement that contains the LIKE keyword. See also: suppress warnings


    nolonglines

    Line is longer than 80 characters

    The rule “nolonglines” provides a warning if the source line is longer than 80 characters. You may wish to customize the ‘80’ for your environment and editing tools.
    The Risk:
    It is sometimes difficult to understand programs with lines longer than your editor can support. Many unix editors are set to the emulator’s width, and typically that is 80.

    How to solve this:
    Reformat your code to <80 characters.

    How to suppress these warnings:
    Disable the rule or change the width.


    nonestinc

    Nesting Include Files is not difficult to maintain

    The rule “nonestinc” provides a warning if you are including a include file within another include file (nesting include files).
    The Risk:
    When you have multiple levels of nested includes, the maintenance of programs becomes increasingly difficult. In addition, you can start constructs in one include file and end them in another. Imagine starting a transaction in include A, then ending it in include D. Are you even aware that you are in a transaction during includes B and C?
    How to solve this:
    Best answer: reorganize your program for maintainability.

    How to suppress these warnings:
    You can put the directive {&_proparse_prolint-nowarn(nonestinc)} directly before the embedded include line. See also: suppress warnings.


    nooutputto

    Instead of Output To, Use Log Manager

    The rule “nooutputto” gives a warning if the program redirects output to another source (OUTPUT TO textfile.csv). This is in support of an EarthLink standard, as the primary use of OUTPUT TO is to generate log information. A Log Manager was created for this purpose, and the standard is to use it.
    The Risk:
    Violation of EarthLink standard, and chaotic log file management (even if you’re not EarthLink).

    How to solve this:
    Use Log Manager.

    How to suppress these warnings:
    You can put the directive {&_proparse_prolint-nowarn(nooutputto)} directly before the statement that contains the OUTPUT TO statement. See also: suppress warnings.


    nopprsininclude

    No Preprocessors in include

    (explanation what this rule does, should go here. I don't know what the rule does)

    This is a contributed rule, which means it is not part of the standard Prolint setup but you can download it separately from the Subversion reporistory and save it in your local prolint/contribs/rules path.
    See http://www.oehive.org/node/1150 for more information on contributed rules.

    Actually this is just an experiment, belonging to the proposal which is described in http://www.oehive.org/node/1150


    noproparse

    Proparse required but not found

    Prolint is configured to use rules that require proparse.dll, but proparse.dll is not found.

    Possible causes:

  • proparse is not installed. It is a separate product, available from http://www.joanju.com/proparse
  • Prolint searches for "proparse/proparse.dll" in the PROPATH. Perhaps you need to change PROPATH or move directory "proparse" into a
    directory that is listed in PROPATH.

    Not every rule requires proparse. For example, a rule that searches
    "WHOLE-INDEX" in the XREF file may be able to do its work without using proparse.
    These rules will still work if proparse is not found.

    To suppress the warning you can modify file "prolint/rules/rules.d" to make sure that you are not trying to run rules that require proparse.


  • noundo

    variable/temp-table/parameter [name] defined without NO-UNDO

    Rule "noundo" gives this warning when it finds a "DEFINE VARIABLE" or "DEFINE TEMP-TABLE" statement without NO-UNDO.

    the risc:

    Slower performance, more IO to the LBI-file.

    known issue:

    A temp-table might be NO-UNDO without explicit definition. For example:

       DEFINE TEMP-TABLE tt_testB LIKE tt_testA.
    

    In this example, temp-table tt_testB is implicitly NO-UNDO if tt_testA was explicitly defined with NO-UNDO.
    We can't verify this in this little code snippet, but Prolint might have been able to figure it out... but Prolint doesn't care. It simply
    will raise the warning "temp-table 'tt_testB' defined without NO-UNDO".

    Personally I don't believe this is a bug in Prolint. After all, tt_testB may be NO-UNDO but it really is defined without NO-UNDO, isn't it?

    how to solve this:

    Be explicit. Make a habit of adding NO-UNDO to all variables, parameters and temp-tables.


    It is not necessary to add NO-UNDO to parameters for (external) DLL functions.


    If you really want a temp-table to be not NO-UNDO you might also add the undocumented option UNDO, although some will argue that undocumented features should never be used:

    DEFINE TEMP-TABLE ttCustomer UNDO
       FIELD CustNum AS INTEGER.
    

    How to suppress these warnings:

    You can put directive {&_proparse_ prolint-nowarn(noundo)} directly before
    the DEFINE keyword.
    See also: suppress warnings.

    implementation:
    This rule is implemented in Turbolint.


    nowait

    Exclusive Lock used without NO-WAIT

    The rule “nowait” provides a warning if the program accesses a record with an EXCLUSIVE-LOCK and does not include a NO-WAIT. This rule is most effective for WebSpeed code.
    The Risk:
    A background process that attempts to get an exclusive lock on a record/row can wait until the value of the –lkwtmo parameter expires. This can cause a timeout on either the WebSpeed agent or the web server.

    How to solve this:
    Add a no-wait and logic to deal with the LOCKED condition, or do not use EXCLUSIVE-LOCK.

    How to suppress these warnings:
    You can put the directive {&_proparse_prolint-nowarn(nowait)} directly before the statement that contains the EXCLUSIVE-LOCK. See also: suppress warnings.


    nowhere

    no WHERE-clause on table [buffername]

    Rule "nowhere" gives this warning when it finds a "FOR", "FIND" or "OPEN QUERY" statement without a WHERE-clause.
    In this case Prolint should also give a "wholeindex" warning.

    the risc:

    Progress may fetch too many records from the table, resulting in bad performance.

    how to solve this:

    Rewrite the statement so that it contains a WHERE-clause.

    How to suppress these warnings:

    You can put directive {&_proparse_ prolint-nowarn(nowhere)} directly before the statement.
    See also: suppress warnings.


    obsoletenodes

    [obsolete keyword] used, rewrite to [modern alternative]

    Rule "obsoletenodes" gives this warning when it finds an obsolete Progress keyword in your source.

    the risc:

    Obsolete keywords may be removed from a future version of the 4GL compiler, although PSC is not likely to actually do that. A better reason to avoid obsolete keywords is that 4GL offers new ways to perform the same task, sometimes for better performance, sometimes for other reasons like OS-portability.

    configuration:

    You can configure this rule by editing file "prolint/rules/persist/obsoletenodes.d".

    This file contains data for a temp-table, which is defined as follows:

       DEFINE TEMP-TABLE tt-Obsolete NO-UNDO
              FIELD obsoleteNode   AS CHAR
              FIELD severityLevel  AS INTEGER
              FIELD warningMessage AS CHAR
              INDEX obsoleteNode   IS PRIMARY obsoleteNode.
    
    • obsoleNode : not the obsolete keyword itself, but the NodeType according to Proparse for the obsolete keyword. The NodeType is almost always the same as the keyword without dashes (for example keyword "WORK-TABLE" has nodetype "WORKTABLE").
    • severitylevel : enter the unknown value (questionmark) or an integer 1..9. When the severity level is the unknown value, Prolint will report warnings using the severity level that was defined in the current profile for this rule.
    • warningmessage: this character string can contain &1. &1 is substituted by the actual node text.

    How to suppress these warnings:

    You can put directive {&_proparse_ prolint-nowarn(obsoletenodes)} directly before the statement.
    See also: suppress warnings.


    oflink

    OF used instead WHERE

    Rule "oflink" gives this warning when it finds a statement that uses OF instead WHERE, as in "EACH order OF customer".

    the risc:

    If an "OF"-join is used, you risk that Progress may select the wrong index. Especially when you modify or add indexes and recompile the source in the future.

    how to solve this:

    Rewrite the statement so that it contains a WHERE-clause.

    How to suppress these warnings:

    You can put directive {&_proparse_ prolint-nowarn(oflink)} directly before the statement.
    See also: suppress warnings.


    prolint

    File not found

    Prolint did not find the file you wanted to lint.

    Prolint is installed more than once

    Prolint found more than one directory where Prolint is installed. The list of directories where Prolint is installed, is in registry
    "HKEY_CURRENT_USER/SOFTWARE/prolint" key "found_in"


    publicvar

    replace public variable &1 by property

    Rule "publicvar" gives this warning when it finds a DEFINE PUBLIC VARIABLE statement in a class. The statement should be replaced by a (public) PROPERTY.

    Motivation

    Encapsulation: a class is responsible for its own state. A public variable allows other objects and procedures outside the class to manipulate the state of the class, so that can't be right.
    Replacing the variable with a PROPERTY with a SET accessor gives control back to the class: the accessor is triggered each time the property value changes. Wether or not the SET accessor is empty is beyond the scope of this rule.

    Known issue

    Prolint assumes that "CLASS" is the very first statement in a class file, and the implementation of this rule is based on that assumption. However in OpenEdge 10.1B the new statement "USING" can be placed before "CLASS". We will have to fix that in Prolint later.


    query

    GET PREV|LAST QUERY statement used without the SCROLLING keyword in the DEFINE statement

    Rule "query" gives this warning when it finds a GET PREV or GET LAST statement for a query that was defined without the SCROLLING option. This is to warn for compatibility with Oracle dataservers: Oracle requires the SCROLLING option when you want to GET PREV or GET LAST.


    recid

    RECID used, rewrite to ROWID

    This rule is obsolete and is replaced by rule obsoletenodes.

    Rule "recid" gives this warning whenever it finds
    the RECID keyword in the source.

    Progress supports the RECID function and RECID data type mainly for backward compatibility.
    For most applications, use the ROWID function instead.

    How to suppress these warnings:

    You can put directive {&_proparse_ prolint-nowarn(recid)} directly before
    the statement.
    See also: suppress warnings.

    implementation:
    This rule is implemented in Turbolint.


    release

    Release may be used incorrectly

    The rule “release” provides a warning any time it runs across the RELEASE statement. This is because most programmers have a misconception of what the command actually does.

    The Risk:
    Developers typically think that RELEASE will release a lock, which does not occur until the end of the transaction. Release simply moves the cursor off the current record, making it no longer available to the remainder of the program. There are times when this is exactly the desired functionality, in which case see “How to suppress” below.

    How to solve this:
    Nothing to be solved, if the developer is using the RELEASE statement as intended. Remove it if not.

    How to suppress these warnings:
    You can put the directive {&_proparse_prolint-nowarn(release)} directly before the RELEASE statement. See also: suppress warnings.


    runargs

    run-time arguments in RUN statement

    Rule "runargs" gives this warning when it finds a run
    statement with run-time arguments.

    the risc:

    The program will not be able to execute when it gets
    deployed to a site with only a runtime license.

    How to resolve this:

    It is often just a typo, some common examples are:

    1. lost count of parentheses. The extra parens are
      interpreted as run-time parameters:

      RUN something ("","")).
    2. forgot to put a period at the end of the line. The
      next line (up to the period) is a runtime argument.

      RUN something ("","")
      a = b + c.

    How to suppress these warnings:

    You can put directive {&_proparse_ prolint-nowarn(runargs)} directly before
    the RUN statement.
    See also: suppress warnings.

    implementation:
    This rule is implemented in Turbolint.


    runasnotfound

    proc [procname] not found on server [handle]

    Rule "runasnotfound" gives this warning when it finds a RUN statement
    for an external procedure on an Appserver, while this external procedure can not be
    found. It tries to prevent Progress run-time error 293.


    The rule can only work if the source for the appserver partition is
    available in the Propath of the Prolint session.
    .

    /* Prolint will check for runs in appserver context: */
    RUN subdir/filename.p ON hAppServer.
    
    /* Prolint will NOT check if subdir/filename.p exists: */
    RUN subdir/filename.p.
    

    See also rule runnotfound to check for
    "RUN" statements that don't have the "ON [SERVER]" option.

    the risc:

    The rule was initially designed to prevent run-time error 293.

    How to suppress these warnings:

    You can put the directive {&_proparse_ prolint-nowarn(runasnotfound)} directly before
    the RUN statement. See also: suppress warnings.


    runname

    progname in RUN-statement is not UNIX-compatible

    Rule "runname" gives this warning when it finds a "RUN program" statement where the name of the program
    contains backslashes instead of forwardslashes or when the name contains uppercase characters.

    Prolint also looks at RUN VALUE(expression) statements, but then it can only raise the warning if the expression
    contains one or more string literals, and one of these strings contains backslashes or uppercase characters.

    RUN-statement does not end with period

    Prolint performs this extra test while it is running rule "runname". Every statement has to end in a period, but
    when running on a Provision license it is sometimes possible to have a RUN statement that doesn't end with a period.
    Such a program won't work when deployed to a site with a runtime license.

    the risc:

    It may lead to "file not found"-errors if programs are copied to Unix.

    how to solve this:

    Replace backslashes by forwardslashes, make sure to use only lowercase characters in program names.

    How to suppress these warnings:

    You can put directive {&_proparse_ prolint-nowarn(runname)} directly before the RUN statement.
    See also: suppress warnings.

    implementation:
    This rule is implemented in Turbolint.


    runnotfound

    proc [procname] not found

    Rule "runnotfound" gives this warning when it finds a RUN statement
    for an external procedure, while the external procedure can not be
    found. It tries to prevent Progress run-time error 293.
    .

    /* Prolint will check if subdir/filename.p exists: */
    RUN subdir/filename.p.
    
    /* Prolint will NOT check for runs in appserver context: */
    RUN subdir/filename.p ON hAppServer.
    

    See also rule runasnotfound to check for
    "RUN ... ON SERVER ..." statements.

    the risc:

    The rule was initially designed to prevent run-time error 293.

    It turns out that the rule mostly detects 'dead code': RUN statements in
    conditional code that never gets executed, probably because it is obsolete.

    CASE menu-choice:
      WHEN "something obsolete" THEN RUN old/obsolete.p.
    

    false positives:

    The rule cannot follow program flow and will raise a warning in the
    following example, although it should not:

    CustomOptionsAvailable = SEARCH("custom/options.r")<>?.
    
    IF CustomOptionsAvailable THEN
       RUN custom/options.p.
    

    How to suppress these warnings:

    You can put the directive {&_proparse_ prolint-nowarn(runnotfound)} directly before
    the RUN statement. See also: suppress warnings.


    sepdbui

    Separate UI from DB-access (UI in line nnn of <filename>)

    Rule "sepdbui" gives this warning when it finds statements for User-Interface and also statements for database access in the same compilation-unit.

    The text of the warning contains the first line number where a User-Interface statement was encountered.

    The first occurence of a database access is found on the line number listed in the "line" column of the Results window.

    Statements for User-Interface include keywords such as ENABLE, DISABLE, MESSAGE, DISPLAY, APPLY or references to form widgets like buttons or fill-ins.


    Statements for database acces include any reference to a record buffer, unless it's a buffer for a temp-table record.

    the risc:

    The program is not ready for n-tier deployment; it is not easily possible to reuse the database logic with a different user-interface.

    implementation:
    This rule is implemented in Turbolint.


    sequence

    CURRENT-VALUE|NEXT-VALUE [sequencename] statement used

    Rule "sequence" gives this warning when it finds a CURRENT-VALUE or NEXT-VALUE statement. This is to test for Oracle compatibility:

    1. With Oracle you can't use "current-val" until you have run "next-value" at least once.
    2. The following is invalid via Oracle: "current-value(seq) = 1."

    Sequence name [sequencename] has an invalid ending (_SEQ).

    In Oracle, the name of a sequence can not end with "_seq"


    shared

    avoid SHARED on {variable|temp-table|buffer} name

    Rule "shared" gives this warning when it finds a "DEFINE" statement that contains a SHARE phrase (like SHARED, NEW SHARED, NEW GLOBAL SHARED).

    Shared objects are generally considered as "not done", especially in a non-modal application since procedures may be run in random order and in multiple instances.
    In general it is much better to avoid SHARED objects and replace them by parameters, or have those objects stored in persistent procedures and maintained by its internal procedures.

    exceptions:

    1. shared streams are allowed, because they cannot easily be replaced by parameters and because you can't get a handle to a stream object.
    2. NEW GLOBAL SHARED procedure-handles are allowed, for the purpose of running persistent super-procedures (or so-called libraries like windows.p).

      Well, to be honest, the rule can't tell if a handle is really a procedure-handle, so
      it actually allows all new global shared handles.

    the risc:

    Confusion and bugs when multiple instances of the procedure are running.

    how to solve this:

    If it's just a simple variable replace it by parameters.

    If it's a more complex object like a temp-table, store it in a persistent procedure (perhaps a super-procedure) and have it maintained by
    internal procedures in that 'super', or create the object where you would normally define it as NEW SHARED and access it by its object-handle where you would normally define the SHARED object.

    How to suppress these warnings:

    You can put directive {&_proparse_ prolint-nowarn(shared)} directly before
    the DEFINE keyword.
    See also: suppress warnings.


    sharelock

    FIND/FOR/OPEN QUERY [recordname] has no NO-LOCK/EXCLUSIVE-LOCK

    Rule "sharelock" gives this warning when it finds a "FIND", "FOR" or "OPEN QUERY" statement where no explicit NO-LOCK or EXCLUSIVE-LOCK is specified.

    the risc:

    Progress will use the default lock, which is SHARE-LOCK (unless the program is compiled using the -NL session parameter).


    SHARE-LOCK is more expensive than NO-LOCK in terms of server resources. Anyway it's always better to explicitly specify the lock type and not rely on default behavior for this matter.

    how to solve this:

    Specify the type you want: NO-LOCK, SHARE-LOCK or EXCLUSIVE-LOCK.

    How to suppress these warnings:

    You can put directive {&_proparse_ prolint-nowarn(sharelock)} directly before the statement.
    See also: suppress warnings.


    sortaccess

    SORT-ACCESS found in xref on table [tablename]

    Rule "sortaccess" scans the XREF file and gives this warning when it finds "SORT-ACCESS".


    This happens when you use the BY option in a query, while the specified
    BY order is such that Progress can not select an existing index that completely fits. This means that Progress will
    have to sort the result-set "on the fly" before it presents the results to the program.

    the risc:

    Sorting is noticeable slow if the result-set contains many records.

    how to solve this:

    Make sure the result-set is small by specifying a really restrictive WHERE-clause. Or: change the design of
    the program so you can accept the sort order of an already existing index. Or: add a new index to the table.

    note:

    When Prolint gives this warning and also a "no WHERE-clause used" warning for the same line (see rule nowhere), then you
    are pretty sure that performance will be bad.


    Even worse: when Prolint gives this warning and also a "WHOLE-INDEX" warning (see rule wholeindex), then you
    should really worry about performance.

    How to suppress these warnings:

    Directive {&_proparse_ prolint-nowarn(sortaccess)} does not help, because this rule only reads the XREF file and is not based on proparse.
    You might use file "nowarn.lst" in directory prolint/settings or one of its subdirectories.
    See also: suppress warnings.


    strattrib

    wrong/no string attributes on [string]

    Rule "strattrib" gives this warning when it finds a string literal that has no string attributes or wrong string attributes.
    String attributes are used primarily by the Translation Manager. Examples of strings with attributes are: "welcome":T, "prolint/rules":U.

    NO string attributes is just that: no string attributes.
    WRONG string attributes are all string attributes except :U and :T (you will probably want to modify this, take a look at the source in prolint/rules/strattrib.p)

    the risc:

    The program is not ready for Translation Manager.

    how to solve this:

    Simply add string attributes to every string literal.

    How to suppress these warnings:

    You can put directive {&_proparse_ prolint-nowarn(strattrib)} directly before the statement that contains string literals.
    See also: suppress warnings.

    implementation:
    This rule is implemented in Turbolint.


    streamclose

    purpose : examine all named or unnamed streams, defines and usage, and warn if:
    1. A stream is defined but not used
    2. A stream is opened but not closed
    3. A stream is opened for INPUT/OUTPUT but not closed before it's used for OUTPUT/INPUT

    Reasons:
    1. Obvious, defining something that's not used is useless
    2. This will leave you with output on places you don't want it or with system resources not freed.
    3. This will probably give you a runtime-error about conflicting use (99).

    Fixes:
    1. Delete the define.
    2. Close the stream after you are done with it.
    3. Close the stream after you are done with it. Perhaps better to not reuse the same stream for input and output use?


    define stream s1.
    define stream s2.
    define stream sUnused. /* Error, stream defined but not used */
    output to c:\temp\test1.txt.
    output stream s1 to value(value1). /* Error, stream s1 opened for output but not closed */
    output close.
    input through ls. /* Error, unnamed-input opened for input but not closed */
    output stream s2 to "c:/temp/1.txt".
    input stream s2 from "c:/temp/1.txt". /* Error, stream s2 opened for input before closed for output */


    substitute

    replace string concatenation by SUBSTITUTE

    Rule "substitute" gives this warning when it finds a
    concatenation of several strings. For example :

    MESSAGE "customer " + fill-in-1:screen-value + " already
    exists".

    the risc:

    A translator will only get to see the separate
    strings, e.g. "customer " and " already exists" and will
    have a hard time understanding in which context these
    two fragments are used.

    how to solve this:

    use the SUBSTITUTE() function:

    MESSAGE SUBSTITUTE("customer &1 already exists", fill-
    in-1:screen-value).

    How to suppress these warnings:

    You can put directive {&_proparse_ prolint-nowarn(
    substitute)} directly before the offending statement.
    See also: suppress warnings.

    implementation:
    This rule is implemented in Turbolint.


    tablename

    field [fieldname] must be qualified with tablename

    Rule "tablename" gives this warning when it finds a filedname that is not qualified with a tablename.

    FOR EACH customer :
      DISPLAY cust-num.  /* should be customer.cust-num */
    END.
    /* typo in variable: should be "name" instead "nane" */
    DEFINE VARIABLE nane AS CHARACTER NO-UNDO.
    ASSIGN 
       name = "Bill".  /* expect to assign variable, but assigns customer.name instead! */
    

    the risc:

    Humans cannot see if "cust-num" is a variable or a database fieldname. Hard to find run-time bugs can result from small typos as illustrated in the example.


    tableusage

    TEMP-TABLE|BUFFER [name] is not used

    Rule "tableusage" is a rule that identifies unused TEMP-TABLE, WORK-TABLE, and BUFFER definitions.

    Notes:

    The author, Igor Natanzon says:

    • Line number for the END keyword is not a reliable block indicator (
      ie. END. END. on one line). However, as each block is processed
      separately, I haven't noticed any problems yet. I suspect it should
      not be a problem because procedures, functions, and event triggers
      cannot be nested (or can they??).
    • Blocks I consider local for buffer define purpose are PROCEDURE,
      FUNCTION, METHOD, and ON event trigger. Any more? Any other define is
      considered global.

    ttlock

    [NO-]LOCK on TEMP-TABLE|WORK-TABLE has no effect

    Rule "ttlock" is part of an effort to clean up source code by eliminating useless commands.
    A locking statement (NO-LOCK, EXCLUSIVE-LOCK, SHARE-LOCK) has no effect when applied to temp-table and work-tables, so perhaps it should be taken out.

    
    def temp-table tttest no-undo 
        field x as char. 
    
    def temp-table tttest1 no-undo 
        field y as char. 
    
    /* All statements below will be reported by ProLint */ 
    repeat preselect each tttest no-lock, each tttest1 where tttest1.y = tttest.x exclusive-lock: 
        find next tttest1. 
        display y. 
    end. 
    
    find first tttest exclusive-lock. 
    
    if not can-find(first tttest1 share-lock) then return. 
    
    

    Notes:

    • Dynamic queries (bufferHandle:FIND-FIRST()) are not checked at this point.
    • Operations checked are FOR, FIND, CAN-FIND, PRESELECT
    • Does not look at OPEN QUERY yet.

    ttnoindex

    TempTable [name] defined like [table] has no index defined.

    Rule "ttnoindex" gives this warning when it finds a DEFINE TEMPTABLE statement that is defined LIKE another table but doesn't contain the USE-INDEX statement and hasn't defined an index.

    The risc:

    If the LIKE table has a large list of indexes then the temptable will inherit all those indexes, thereby increasing the overhead.

    Known issue:

    The TempTable definition may be like another temptable or table definition that has only the indexes that are needed.

    How to solve this:

    Make a habit of specifying the exact indexes that the program will be using regardless of whether they are the same as the LIKE table.

    How to suppress these warnings:

    You can put directive {&_proparse_prolint-nowarn(ttnoindex)} directly before the DEFINE keyword. See also: suppress warnings.


    undoretry

    "UNDO" defaults to "UNDO, RETRY"

    Rule "undoretry" gives this warning when it finds an UNDO statement without options like RETRY/LEAVE/UNDO/NEXT.

    the risc:

    "UNDO" behaves like "UNDO, RETRY" but RETRY is almost never desired.

    How to suppress these warnings:

    You should specify at least something like "UNDO, LEAVE labelname", or even "UNDO, RETRY" to confirm the default.


    See also: suppress warnings.


    uninproc

    internal procedure [name] is not used

    Rule "uninproc" gives this warning when it finds an internal procedure that is never called from inside the compilation unit.

    the risc:

    Internal procedures that are never called, obscure the sourcefile and waste memory at run-time.

    known issues:

    The rule can not really know for sure if an internal procedure is never called: because a rule can only look at one compilation unit at a time, it can not see if the I.P. is called from an other external procedure.


    unquoted

    unquoted string

    Rule "unquoted" gives this warning when it finds a string literal without quotes.

    Strings "w1" and "w2" are not quoted:

    DEFINE VARIABLE RADIO-SET-2 AS INTEGER
    VIEW-AS RADIO-SET VERTICAL
    RADIO-BUTTONS w1, 0, w2, 1, "w3", 2
    SIZE 19 BY 2 NO-UNDO.
    

    Progress will create a file named "logfile" in the current directory:

    DEFINE VARIABLE logfile AS CHARACTER INITIAL "c:\temp\errorlog.txt".
    OUTPUT TO logfile.
    

    the risc:

    The OUTPUT TO example shows a possible bug, the RADIO-SET example shows strings that should have been translatable.

    How to suppress these warnings:

    You can put the directive {&_proparse_ prolint-nowarn(unquoted)} directly before
    the IF statement. See also: suppress warnings.


    upcasepreproces

    Preprocessors should be in UPPERCASE

    The rule “upcasepreproces” gives a warning if a &GLOBAL-DEFINE or &SCOPED-DEFINE preprocessor is not all upper case. This is simply a standard that you can choose to follow or not. Using upper case for constants is a broad industry standard, and preprocessors are routinely used as constants. Even when used for other purposes, it helps readability and understanding if the preprocessor name is upper case.

    The Risk:
    No risk, this is a style/maintainability issue.

    How to solve this:
    Change your preprocessor to upper case.

    How to suppress these warnings:
    Just turn it off if this is not your standard. There is never a good reason to suppress, if it is your standard.


    use-index

    avoid using USE-INDEX

    Rule "use-index" gives this warning whenever it finds
    the USE-INDEX keyword in the source.

    the risc:

    If you specify USE-INDEX, Progress will not
    automatically select an index for you. The automatic
    index selector is believed to be very good.

    Even if you manually picked the best index there is
    still a risc: this index will still be used after the
    database schema gets changed, even if another index would
    be better at that time.

    As I understand it (correct me if I am wrong) there
    are two query engines in Progress: the "old" one (already existed in V6) and the "new" one which is highly
    optimized for joins and supports multi-bracketing.
    If you specify USE-INDEX you
    force Progress to use the "old" query engine, so
    performance may actually be worse than without the USE-INDEX
    option. There are even folks who say that if Progress would
    automatically pick a different index than you would,
    then either you picked the wrong one or the indexes
    in the database schema are not well-defined...

    how to solve this:

    Delete the USE-INDEX option, then compile and look at the
    XREF output: Progress shows here which index (or
    indexes) it has selected.

    How to suppress these warnings:

    You can put directive {&_proparse_ prolint-nowarn(use-index)} directly before
    the statement.
    See also: suppress warnings.


    usingpkg

    USING package, replace by USING type (without wildcards)

    Rule "usingpkg" warns when it finds a USING statement with package globs. In other words:

    USING package.ClassName

    is recommended instead of:

    USING package.*

    The use of fully qualified class names, rather than package globs, seems to be a commonly preferred practice in other languages with similar language features.


    varusage

    Variable usage

    Rule "varusage" tests for two different events:

    • variable [identifier] is never accessed
    • variable [identifier] in procedure [procname] hides object in program scope

    variable [identifier] is never accessed

    This means your program defines a variable and perhaps assigns a value to that variable,
    but the value of that variable is never accessed. It does not necessarily mean that the variable
    itself appears nowhere in the source.

    Let's have a look at a small example program:

    DEFINE VARIABLE var_1 AS DECIMAL NO-UNDO.
    DEFINE VARIABLE var_2 AS DECIMAL NO-UNDO.
    DEFINE VARIABLE var_3 AS DECIMAL NO-UNDO.
      
    var_1 = SQRT(var_2 * var_2 + var_3 * var_3).
    

    In this example, variable var_1 is never accessed.
    It has a value, but this value is never used for output or assignment to another field/variable. It may be a left-over from a piece of obsolete code that was
    supposed to be deleted.

    the risc:

    variable var_1 is a waste of space, both in the sourcefile as in memory at runtime, and assigning a value to it is also a waste of time.

    how to solve this:

    You can simply delete the definition of variable var_1.
    This may cause a compile error if the variable appears anywhere in the source like in the given example.
    Disclaimer: read the source carefully before you delete the variable, Prolint may have reported a false positive!

    variable [identifier] in procedure [procname] hides object in program scope

    This means that a local variable inside an internal procedure has the same name as a variable which is defined at the program scope.
    An example:

    DEFINE VARIABLE firstname AS CHARACTER NO-UNDO.
    PROCEDURE FindSalesRep :     
       DEFINE VARIABLE firstname AS CHARACTER NO-UNDO.
       DEFINE VARIABLE lastname  AS CHARACTER NO-UNDO.
       
       FIND FIRST salesrep EXCLUSIVE-LOCK NO-ERROR.
       ASSIGN 
          firstname = ENTRY(1,salesrep.repname," ":U)
          lastname  = ENTRY(2,salesrep.repname," ":U)
          salesrep.repname = lastname + ", ":U + firstname.
          
    END PROCEDURE.
    

    Because procedure FindSalesRep defines a local variable firstname, there is no way it can access the variable firstname which was
    defined at the program level.

    the risc:

    Well, technically this is not a bad thing, but a programmer could easily get confused when he/she reads the sourcecode.


    One might argue that the variable at the program level should not have been
    defined at all, or should at least have been defined with a different name that indicates its large scope.

    how to solve this:

    Remove one of the variables, or rename one of them. It is usually preferred to remove the variable at the program level; keep the scope of variables as short as possible.


    How to suppress these warnings:

    You can put the directive {&_proparse_ prolint-nowarn(varusage)} directly before
    the DEFINE statement that defines the variable (or parameter). See also: suppress warnings.

    Be careful though, because in this particular case the directive may cause confusion. Example:

    DEFINE VARIABLE firstname AS CHARACTER NO-UNDO.
    PROCEDURE FindSalesRep :     
       {&_proparse_ prolint-nowarn(varusage)}
       DEFINE VARIABLE firstname AS CHARACTER NO-UNDO.
       DEFINE VARIABLE lastname  AS CHARACTER NO-UNDO.
       
       FIND FIRST salesrep EXCLUSIVE-LOCK NO-ERROR.
       ASSIGN 
          firstname = ENTRY(1,salesrep.repname," ":U)
          lastname  = ENTRY(2,salesrep.repname," ":U)
          salesrep.repname = lastname + ", ":U + firstname.
          
    END PROCEDURE.
    

    In this example the directive was placed at the local variable. As a result, this definition is now completely invisible to Prolint, so Prolint
    will:

  • a. not notice that "firstname" is defined twice, so will not warn about hiding objects,
  • b. think that 'salesrep.repname = lastname + ", ":U + firstname' actually accesses the variable at the program scope.
  • Same source, different placement of the directive:

    {&_proparse_ prolint-nowarn(varusage)}
    DEFINE VARIABLE firstname AS CHARACTER NO-UNDO.
    PROCEDURE FindSalesRep :     
       DEFINE VARIABLE firstname AS CHARACTER NO-UNDO.
       DEFINE VARIABLE lastname  AS CHARACTER NO-UNDO.
       
       FIND FIRST salesrep EXCLUSIVE-LOCK NO-ERROR.
       ASSIGN 
          firstname = ENTRY(1,salesrep.repname," ":U)
          lastname  = ENTRY(2,salesrep.repname," ":U)
          salesrep.repname = lastname + ", ":U + firstname.
          
    END PROCEDURE.
    

    In this case the large-scoped definition is hidden for Prolint. Prolint will not check if this large-scoped variable is ever accessed.

    The difference between these two placements may not be clear until there are more internal procedures and functions, some of which actually do access the variable at the program-level.

    implementation:
    This rule is implemented in Turbolint.


    version

    Expected proparse version X, found version Y

    Prolint was tested on version X of proparse.dll but you have version Y. There is no way of predicting how Prolint will behave, but Prolint will try to continue anyway.

    If version X is newer than Y you can upgrade proparse, see download proparse.

    If version X is older than Y you should download the matching version of Prolint.


    weakchar

    the "Weak Character" test

    Rule "weakchar" gives this warning when it finds an ' IF charvar="" '
    test that does not take into consideration that charvar might also
    have the UNKNOWN value.

       IF AVAILABLE customer THEN
          IF customer.e-mail <> "" THEN
             RUN SendMail (customer.e-mail, subject, body).
          ELSE
             IF customer.fax <> "" THEN
                RUN SendFax (customer.fax, subject, body).
    
    

    Trouble will happen if customer.e-mail happens to have the UNKNOWN value. The IF
    condition did not check that.

    So what is the proper way to check if a character field is not blanc
    or unknown? You can think of several solutions, it is a matter of
    taste or style, or perhaps even code-religion to pick the best. Here are a couple of
    possible solutions:

       IF customer.e-mail > "" THEN ...
    
       IF NOT (customer.e-mail LE "") THEN ...
       
       IF NOT (customer.e-mail="" or customer.e-mail=?) THEN ...
    
       IF customer.e-mail<>"" AND customer.e-mail<>? THEN ...
    

    This prolint rule gives probably many false positives and false negatives, it
    needs improvement. One difficult issue is: what about the ELSE branche
    of the IF statement?

    How to suppress these warnings:

    You can put the directive {&_proparse_ prolint-nowarn(weakchar)} directly before
    the IF statement. See also: suppress warnings.


    when

    wrong usage of ASSIGN..WHEN.. statement

    Rule "when" gives this warning when it finds an ASSIGN statement with a WHEN clause, if the WHEN clause reads variables that are assigned to in that same ASSIGN statement.

    DEFINE VARIABLE a AS INTEGER INITIAL 1.
    DEFINE VARIABLE b AS INTEGER INITIAL 2.
    DEFINE VARIABLE c AS INTEGER INITIAL 3.
    DEFINE VARIABLE d AS INTEGER INITIAL 4.
    ASSIGN 
       a = b
       c = 100 WHEN (a = b).
    /* c is now still 3 */
    ASSIGN 
       a = b
       c = 200 WHEN (d = 4).
    

    You might expect that (a = b) is True after executing the first line, so c will be assigned the value 100.

    It doesn't work that way. All WHEN clauses are evaluated first (a = b is still False) before any assigments take place.

    Prolint will not complain about the second ASSIGN statement because the WHEN clause does not access any variables that are on the left-hand side of any of the = signs.

    the risc:

    It is not commonly known that the boolean values of WHEN clauses are evaluated before the assignments take place. Either the program will not do what the programmer intended, or the program is confusing to most other programmers.

    how to solve this:

    Try to avoid using WHEN in an ASSIGN statement; rewrite to IF.

    How to suppress these warnings:

    You can put the directive {&_proparse_ prolint-nowarn(when)} directly before
    the ASSIGN statement. See also: suppress warnings.


    where-cando

    CAN-DO Functions used in WHERE clause

    Rule "where-cando" gives this warning when it finds a WHERE clause that contains a CAN-DO function.

    the risc:

    Performance is bad

    How to suppress these warnings:

    You can put the directive {&_proparse_ prolint-nowarn(where-cando)} directly before
    the statement that contains the WHERE clause. See also: suppress warnings.


    where-udf

    User Defined Function used in WHERE clause

    Rule "where-udf" gives this warning when it finds a user defined funtion in a WHERE clause.

    the risc:

    If the user defined function accesses the database (contains FIND, FOR, QUERY) then you may get run-time error 7254 (in Progress 9) or may corrupt your database (in Progress 8). See also knowledge base articles 19561 and 20031.

    how it works:

    Prolint first tries to find the name of the function in your "whitelist" to see if the function must be regarded as harmless. If the name is not in the whitelist, Prolint tries to find the implementation of the function in the current compilation-unit. If the implementation is found and contains no statements that can possibly access the database, then the function is regarded as harmless. Otherwise, the function is regarded as possibly dangerous and Prolint will raise its warning.

    How to suppress these warnings:

    You can maintain a "whitelist" of user defined functions for which you know they are harmless: create a file named "where-udf.lst" in directory "prolint/settings" and/or in directory "local-prolint/settings" (both lists will be imported if they exist). File where-udf.lst is a normal text-file, each line contains one function-name (without quotes or other delimiters).

    You can put directive {&_proparse_ prolint-nowarn(where-udf)} directly before
    the DEFINE keyword.
    See also: suppress warnings.


    wholeindex

    WHOLE-INDEX found in xref on table [tablename]

    Rule "wholeindex" scans the XREF file and gives this warning when it finds "WHOLE-INDEX".

    This happens when you did not specify a WHERE-clause in your query, or a WHERE-clause with fields that
    did not make it possible for Progress to set good index brackets.

    The result can be that all records in [tablename] must be fetched and evaluated to check the WHERE-criteria. This is
    bad for performance if there are many records in the table.

    WHOLE-INDEX is not always slow. In the following example, Progress will report WHOLE-INDEX on the "FIND NEXT"
    statement only because there is no WHERE-clause specified:

    FIND customer WHERE customer.cust-num = 5 NO-LOCK NO-ERROR.
    IF AVAILABLE customer THEN
       FIND NEXT customer NO-LOCK NO-ERROR.
    

    In this particular example the FIND NEXT statement will perform pretty good despite the WHOLE-INDEX warning.


    By the way, Prolint will also raise warning "no WHERE-clause used on customer" in this example (see rule nowhere).

    Performance indicator:

    • on "wholeindex" and "nowhere", performance may not be so bad (but you should rewrite the statement anyway)
    • on "wholeindex" without "nowhere", performance is probably bad
    • on "sortaccess" and "nowhere", performance is probably horrible
    • on "sortaccess" plus "wholeindex" without "nowhere" performance is a potential nightmare.

    the risc:

    Possibly slow

    how to solve this:

    How to suppress these warnings:

    Put directive {&_proparse_ prolint-nowarn(wholeindex)} in the source just before the line that causes the warning.

    You might also use file "nowarn.lst" in directory prolint/settings or one of its subdirectories.
    See also: suppress warnings.