Supported Linter rules

ipt has a list of linter rules builtin.

Some explanation about the short details for each rule:

Rule name

The build in rule name that is always printed alongside the error messages

Fixable

yes, if ipt can apply automatic fixes. If fixes are applied we don’t output additional linting messages.

Severity

The error message level that an error message will have, if this rule creates one. This can be one of:

  • error, code which Igor Pro will not accept or lead definitely to problems (code is 100% buggy)

  • warning, suggestions for better code or potential problems

  • info, anything else

  • none, if there are no error messages at all

Features

The list of all features this rule supports. For a detailed description see below. none, if no features are supported.

Note

Good Igor procedure code should have no errors or warnings when running ipt lint.

You can selectively include or exclude specific rules that should be checked and/or applied. Using --include ruleName you can include a rule and using --exclude ruleName you can exclude one. You can also apply multiple rules using this argument multiple times or list multiple rules separated by a comma like --include ruleName1,ruleName2. If no rules are explicitely included, all rules are included. If you want to have a list of all available rules you can call ipt lint --list.

Some of these linting rules support multiple features. You can think of them as if they are multiple rules but closely related together, so it’s easier to organize them as a single one. The generated error message will always contain the rule name and the feature name in the form of ruleName:featureName. You can use the same format to include/exclude them using --include ruleName:featureName or --exclude ruleName:featureName. NOLINT will be handled the same. If you want to specify all features of a rule, you can use the rule name itself like --include ruleName or use * as the feature name like --include ruleName:*.

If you want to see the full list of supported features of a rule, you can refer to this documentation or call ipt lint --list ruleName. The output will always start with the pseudo feature *.

Bugprone: contradicting /O and /FREE flag

Rule name

BugproneContradictingOverrideAndFreeFlag

Fixable

no

Severity

error

Features

none

Many operations like Make allow to create global resources that are located in the current data folder or local ones which are not attached to any data folder. Igor Pro has the convention that the operation flag /O overrides the specified global resource if it exists, or creates a new one if doesn’t. The operation flag /FREE is used to create a resource that isn’t attached to any data folder (e.g. a free wave).

If the user provides both flags, the behavior can be unexpected and prone to bugs.

Bugprone: loop initialization assignment

Rule name

BugproneLoopInitializationAssignment

Fixable

no

Severity

warning

Features

none

Igor supports multiple assignment operators that can be used in a for loop initializer. We recommend that you only use the standard assignment using the = operator to prevent unintentional bugs in your code.

example.ipf
Function test(variable a)
    for(a += 1; a < 10; a++)
//      ^^^^^^
        print a
    endfor
End

Using other assignment operators other than = will update the variable depending on its original state, which can be misleading when initialising variables in a for loop. Usually this place is used to set a loop variable to a fixed state which is fully dependend on the expression on the right side of =.

Bugprone: Missing switch default case

Rule name

BugproneMissingSwitchDefaultClause

Fixable

yes

Severity

warning

Features

none

Every switch and strswitch statement should have at least one default case which handles all remaining cases. Even if the switch statement handled all known cases it is always a good practice to handle the unknown error case.

The default case has to have at least one statement. If you want to keep your default case empty, you can always add a single break statement. If you have multiple cases which share the same code like your default case, you have to list the default case last to keep it non empty.

example.ipf
Function test(variable a)
    switch(a)
        case 1:
            print "one"
            break
        case 2:
            print "two"
            break
        case 3:
        case 4:
        default:
            print "many"
            break
    endswitch
End

The fixing routine of this rule adds a default case to the switch statement with a single break statement and a // FIXME(BugproneMissingSwitchDefaultClause) comment.

Fixed statement
Function test(variable a)
    switch(a)
        default:
            // FIXME(BugproneMissingSwitchDefaultClause)
            break
    endswitch
End

Bugprone: reserved keywords as identifier

Rule name

BugproneReservedKeywordsAsIdentifier

Fixable

no

Severity

warning

Features

none

Igor Pro historically allows the use of reserved keywords as the names of functions, macros, variables, strings, etc. The use of such keywords may cause conflicts in certain scenarios where the use of such names is not allowed, or may change the behavior in an unintended way. Therefore, it’s better not to use reserved keywords as identifiers at all.

The full list of reserved keywords are: as, ASCII85Begin, ASCII85End, break, case, catch, char, complex, Constant, continue, default, define, DFREF, do, double, elif, else, else, elseif, End, endfor, endif, EndMacro, EndStructure, endswitch, endtry, float, for, FuncRef, FUNCREF, Function, if, ifdef, ifndef, include, Inf, int, int16, int32, int64, int8, Macro, Menu, NaN, NVAR, override, Picture, pragma, Proc, return, static, StrConstant, string, strswitch, STRUCT, Structure, SubMenu, SVAR, switch, threadsafe, try, uchar, uint, uint16, uint32, uint64, uint8, undef, variable, vs, WAVE, while and Window.

Codestyle: default pragmas

Rule name

CodeStyleDefaultPragmas

Fixable

yes

Severity

error and warning

Features

none

According to our coding conventions we recommend a set of default pragmas:

#pragma TextEncoding="UTF-8"
#pragma rtGlobals=3
#pragma rtFunctionErrors=1

If some pragmas are missing, this rule will add them to the start of the file. If the pragmas exists but set to a different value, this rule will just create a warning.

This rule will only create an error message, if the value type is incorrect (e.g. an integer for #pragma TextEncoding) or a different text encoding than UTF-8 was set.

Codestyle: Empty line between code blocks

Rule name

CodeStyleEmptyLineBetweenCodeBlocks

Fixable

yes

Severity

none

Features

none

Ensure that there is at least a single empty line between each code blocks, such as a function or macro. A blank line is not added between pragmas, includes, and constants.

before
#pragma rtGlobals=3
#pragma rtFunctionErrors=1
#include "foo"
#include "bar"
Function foo()
End
Function bar()
End
after
#pragma rtGlobals=3
#pragma rtFunctionErrors=1

#include "foo"
#include "bar"

Function foo()
End

Function bar()
End

This rule will attach comments to the next code block when a new empty line is inserted.

before
Function foo()
End
// cmt
Function bar()
End
after
Function foo()
End

// cmt
Function bar()
End

Codestyle: #endif comment

Rule name

CodeStyleEndIfComment

Fixable

yes

Severity

warning

Features

CodeBlock, Statement

Ensures that the #endif part of an #ifdef or #ifndef has a correct comment assigned.

before
#ifdef Foo
Function test()
    #ifndef Bar
    print 1
    #endif
End
#endif
after
#ifdef Foo
Function test()
    #ifndef Bar
    print 1
    #endif // !Bar
End
#endif // Foo

If an #endif already contained a comment, it will be replaced with the expected one.

The feature CodeBlock will search for all #ifdef and #ifndef in the global scope outside of functions and macros and the feature Statement will only search inside functions and macros.

Codestyle: Fallthrough case require comment

Rule name

CodeStyleFallthroughCaseRequireComment

Fixable

no

Severity

warning

Features

none

Every case statement in switch control flow blocks where the code block following the case line does not have a statement that exits the block must have a comment that starts with fallthrough. This comment is enforced such that the developer has to explicitly express that fallthrough is intentional.

Running ipt lint with --fix will add a comment FIXME(CodeStyleFallthroughCaseRequireComment) to those cases that should be marked as fallthrough cases but are not.

before
Function test(variable a)
    switch(a)
        case 1:
            print "apple"
        case 2:
        case 3: // fallthrough
        case 4: // fallthrough, some other comment
        case 5:
            print "banana"
            break
    endswitch
End
after
Function test(variable a)
    switch(a)
        case 1: // FIXME(CodeStyleFallthroughCaseRequireComment)
            print "apple"
        case 2: // FIXME(CodeStyleFallthroughCaseRequireComment)
        case 3: // fallthrough
        case 4: // fallthrough, some other comment
        case 5:
            print "banana"
            break
    endswitch
End

Note

At the current state of ipt’s development only the following statements are detected at the top level of such code blocks: break, continue, return, Abort, Quit and IPT_NORETURN. If such exit statement is nested in a an inner code block, like if, then it is not detected as a statement that exits the current code block.

The conditional operations AbortOnValue and AbortOnRTE are not detected as exit statements.

Codestyle: No fixme

Rule name

CodeStyleNoFixme

Fixable

no

Severity

warning

Features

none

Will report all FIXME comments generated by ipt as a warning. This is helpful to find all code locations that have not been fixed. New FIXME comments generated by the current iteration of IPT won’t be reported as a warning.

Fix old style function argument

Rule name

FixOldStyleFunctionArgument

Fixable

yes

Severity

warning

Features

none

This rule searches for all Igor Pro 6 and earlier style function arguments and converts them into inline style which was introduced in Igor Pro 7.

example.ipf
Function test(a)
    variable a

    print a
End
ipt lint –fix example.ipf
Function test(variable a)

    print a
End

If the declaration contains a comment at the end, it is considered as documentation for this argument and ipt won’t transform the function signature. It is up to you, to move this comment to the function documentation and rerun ipt again.

Fix empty line after function header

Rule name

FixEmptyLineAfterFunctionHeader

Fixable

yes

Severity

none

Features

none

Ensures that after each function declaration we always have a single empty line. This is according to our coding conventions.

example.ipf
Function test(variable a)
    print a
End
ipt lint –fix example.ipf
Function test(variable a)

    print a
End

Readability: Else after return

Rule name

ReadabilityElseAfterReturn

Fixable

yes

Severity

warning

Features

none

If an if statement has two or more branches and one of its branches contains a statement that exits the control flow (e.g. a return or IPT_NORETURN), it should be refactored to have only one branch that exits the control flow instead.

Let’s say we have the following example…

example.ipf
Function test(variable a)

    if(a > 10)
        return 1
    else
        print "small number"
    endif
End

Considering the two if branches: One success part if the condition a > 10 succeeds and one else part if the condition failed. The success part of the if condition exits the control flow with the return keyword. This can be refactored into a better readible version that has only one branch:

ipt lint –fix example.ipf
Function test(variable a)

    if(a > 10)
        return 1
    endif

    print "small number"
End

This rule can also be applied on large if statements with multiple elseifs.

example2.ipf
Function test(variable a)

    if(a < 10)
        return 1
    elseif(a < 20)
        print 2
    else
        return 3
    endif
End
ipt lint –fix example2.ipf
Function test(variable a)

    if(a < 10)
        return 1
    endif

    if(a < 20)
        print 2
    else
        return 3
    endif
End

This rule won’t touch elseifs if the branch to a preceding condition doesn’t exit the current control flow.

Function test(variable a)

    // will be unchanged and no warning messages will be generated
    if(a < 10)
        print "small number"
    elseif(a < 20)
        return 2
    else
        print "big number"
    endif
End

The output of this rule has an additional benefit: The coverage data created by igortest is more accurate and doesn’t contain an uncovered endif at the end of the function block.

This rule has two features and the following NOLINT behavior:

  • ReadabilityElseAfterReturn:If will be checked at the line of each if or elseif statement that has a code block that exits the control flow. If a NOLINT was found at this place, it won’t be fixed and further checks for additional elseifs or else won’t be done.

Note

If you have multiple nested if statements, the rule is not applied recursively on the result of the previous iteration. This is a limitation of ipt and will be addressed in a future release. It is recommended to rerun ipt lint multiple times until there is no change in the output.

Readability: Missing Parenthesis

Rule name

ReadabilityMissingParenthesis

Fixable

yes

Severity

warning

Features

BinaryChild, BinaryParent, DollarChild, TernaryChild

Igor Pro provides a list of operators that can be used to calculate the result of an expression. Some of them are pretty common like addition + or multiplication * and some are rarely used like left shift << or bitwise xor %^. All of them have their own priority assigned in which order they are executed if no parenthesis are used. Some of these priorities are intuitive (see PEMDAS, BODMAS, BIDMAS or BEDMAS) and some are not.

This linter rule enforces parenthesis for operators which priority are not intuitive and make it visible for all readers.

Original code

Modified code

Explanation

a + b * c

a + b * c

Will be unchanged as PEMDAS rules apply

a + b == c

(a + b) == c

== has a lower priority than +

a << b + c

(a << b) + c

<< has a higher priority than +

a == b == c

a == (b == c)

== are evaluated from right to left

$"a" + "b"

$("a" + "b")

$ has a set of special rules in Igor Pro which are not intuitive

a && b ? c : d

(a && b) ? c : d

&& has a higher priority than the ternary operator ?:

The listed features defines at which places this rule will be checked:

BinaryChild

Will check both children of a binary expression for parenthesis

BinaryParent

Will check a binary expression itself

DollarChild

Will check the children of a $ expression

TernaryChild

Will check the children of a ternary expression ?:

Readability: One line variable initialization

Rule name

ReadabilityOneLineVariableInitialization

Fixable

yes

Severity

warning

Features

none

A single declaration line can contain multiple variable declarations and/or definitions.

example.ipf
Function test()

    // line with multiple declarations
    variable a, b
    // line with multiple definitions
    variable c = 1, d = 2
    // mixed line
    variable e, f = 3, g, h
End

This rule enforces that each definition is in their own line without any other definition or declaration. You can still list multiple declarations in one line.

If a line with multiple definitions or a mixed line ends with a comment, no fix is applied.