chplcheck
chplcheck is a linter for the Chapel programming language implemented in
Python using the Python bindings for the compiler frontend.
It is intended to catch stylistic mistakes and bad practices in Chapel programs.
It is also intended to be customizable and extensible, using a system of named
‘rules’ that lead to warnings.
chplcheck supports the Language Server Protocol, allowing it to be used as
part of your favorite editor. The following image demonstrates its use in
Neovim:
Getting Started
The easiest way to make chplcheck available on your command line is by using the
chplcheck Makefile target. This will build the Dyno compiler frontend and the
Python bindings for Dyno if needed, and place chplcheck into $CHPL_HOME/bin.
Make sure that you satisfy the requirements for building the Python bindings.
cd $CHPL_HOME
make chplcheck
chplcheck --help
Saving the following file into myfile.chpl:
1record MyRecord {}
2
3for i in 1..10 do {
4 writeln("Hello, world!");
5}
The linter is run as follows:
> chplcheck myfile.chpl
path/to/myfile/myfile.chpl:1: node violates rule CamelCaseRecords
path/to/myfile/myfile.chpl:3: node violates rule DoKeywordAndBlock
path/to/myfile/myfile.chpl:3: node violates rule UnusedLoopIndex
Enabling / Disabling Rules
Each rule, such as CamelCaseRecords, can be individually enabled or
disabled from the command line using --enable-rule and --disable-rule.
To silence the warning about unused loop indices such as i in the above
code, we can invoke chplcheck as follows:
> chplcheck myfile.chpl --disable-rule UnusedLoopIndex
path/to/myfile/myfile.chpl:1: node violates rule CamelCaseRecords
path/to/myfile/myfile.chpl:3: node violates rule DoKeywordAndBlock
Some rules are disabled by default. One such rule is UseExplicitModules, which
warns against letting Chapel automatically create the top-level module in a file.
> chplcheck myfile.chpl --enable-rule UseExplicitModules
path/to/myfile/myfile.chpl:1: node violates rule CamelCaseRecords
path/to/myfile/myfile.chpl:1: node violates rule UseExplicitModules
path/to/myfile/myfile.chpl:3: node violates rule DoKeywordAndBlock
path/to/myfile/myfile.chpl:3: node violates rule UnusedLoopIndex
To get a list of all available rules, use the --list-rules flag. To see
which rules are currently enabled, use the --list-active-rules flag. If you
have loaded custom rules, these will be included
in the output.
> chplcheck --list-rules
...
> chplcheck --list-active-rules
...
Rules can also be ignored on a case-by-case basis by adding a
@chplcheck.ignore attribute with a string argument stating the rule to
ignore. For example:
@chplcheck.ignore("CamelCaseRecords")
record MyRecord {}
This will suppress the warning about MyRecord not being in camelCase.
Note
chplcheck.ignore is a Chapel attribute and is subject to the same
limitations as other attributes in the language. This means that it cannot be used to ignore all warnings; for
example it currently cannot be used on an if statement.
Note
There is currently no way to ignore more than one rule at at time for a
given statement. Adding multiple chplcheck.ignore annotations will
result in a compilation error.
Fixits
Some rules have fixits associated with them. Fixits are suggestions for how to
resolve a given issue, either by editing the code or by adding
@chplcheck.ignore. If using chplcheck as a command line tool, you can
apply these fixits by using the --fixit flag. When using chplcheck from
an editor, the editor may provide a way to apply fixits directly with a Quick
Fix.
When using the command line, a few additional flags are available to control how fixits are applied:
--fixit: Apply fixits to the file. By default, this is done in-place, overwriting the original file with the fixed version.--fixit-suffix <suffix>: Apply fixits to a new file with the given suffix appended to the original file name. For example,--fixit-suffix .fixedwould create a new file namedmyfile.chpl.fixedwith the fixits applied.--interactive: Starts an interactive session where you can choose which fixits to apply.
Rule Specific Settings
Some rules have individual settings that can be adjusted to customize their
behavior. These are specified from the command line as
--setting RuleName.SettingName=Value.For example, a rule like
NoFuncNamed could have a setting called Name to select what function
names are disallowed. This setting could be adjusted as follows:
--setting NoFuncNamed.Name="foo".
These settings are local to a given rule. Settings can also be global, as long
as at least 1 rule opts in to using them. For example, custom rules could conform
to the setting MyIgnoreList, which could be set as
--setting MyIgnoreList="foo,bar".
Configuration Files
chplcheck can be configured without passing command line flags using a
configuration file. This file can be specified using the --config (also
-c) flag. The configuration file can either be a YAML file or a specific
TOML file. For example, running chplcheck -c config.yaml will load the
configuration from config.yaml. Additionally, chplcheck will look for
configuration files in the current directory named chplcheck.cfg or
.chplcheck.cfg (in that order).
Most command line options can be specified in the configuration file. For
example, the following YAML configuration file will explicitly enable the
UseExplicitModules rule and disable the UnusedLoopIndex and
UnusedFormal rules:
enable-rule: ["UseExplicitModules"]
disable-rule: ["UnusedLoopIndex", "UnusedFormal"]
TOML configuration files can also be used, for example the following is the same configuration as above:
[tool.chplcheck]
enable-rule = ["UseExplicitModules"]
disable-rule = ["UnusedLoopIndex", "UnusedFormal"]
This configuration can also be added to a Mason
configuration file. chplcheck will automatically use a configuration file
contained in a Mason.toml file in the current directory.
Setting Up In Your Editor
chplcheck uses the Language Server Protocol (LSP) to integrate with compatible
clients. If your editor supports LSP, you can configure it to display
linting warnings via chplcheck. See the
Editor Support page for details on a specific
editor.
Writing New Rules
Rules are written using the Python bindings for Chapel’s compiler frontend. In
essence, a rule is a Python function that is used to detect issues with the
AST. When registered with chplcheck, the name of the function becomes the name
of the rule (which can be used to enable and disable the rule, as per the
above sections). To mark a Python function as representing a rule,
chplcheck’s Python API provides two decorators. These decorators correspond
to the two ‘flavors’ of rules in the linter: ‘basic’ and ‘advanced’.
Basic Rules
Basic rules are specified using a pattern.
This pattern represents which AST nodes should be scrutinized to check if something.
The driver.basic_rule decorator is used to specify such rules. For instance,
the following basic rule checks that explicit modules have PascalCase naming:
@driver.basic_rule(Module)
def PascalCaseModules(context, node):
return node.kind() == "implicit" or check_pascal_case(node)
The Module argument to basic_rule specifies that the linter should call
the PascalCaseModules function with each Module node it encounters. If
the function returns True, no warning should be emitted. If the function
returns False, the linter should produce a warning. The conditional returns
True for all implicit modules, regardless of their name: this is because
implicit modules are named after the file they are in, so the user cannot “fix”
the code by editing it. For explicit modules, a helper function
check_pascal_case is used to ensure that the node’s name is appropriately
cased.
Patterns can be more advanced than simply specifying an AST node type. The
following rule makes more use of patterns by specifying that it should be
applied only to if-statements that just have a boolean literal as their
condition.
@driver.basic_rule([Conditional, BoolLiteral, chapel.rest])
def BoolLitInCondStmt(context, node):
return False
Advanced Rules
Sometimes, specifying a pattern is not precise enough to implement a rule. For
example, a linting check might require considering two sibling nodes or other
less-straightforward relationships than “does it match the pattern?”. This is
the purpose of advanced rules. These functions are called with the root AST
node (usually a top-level Module). Then, it is the responsibility
of the function to find and yield AST nodes that should be warned about.
For instance, at the time of writing, the following code implements the rule
checking for unused formals.
@driver.advanced_rule
def UnusedFormal(context, root):
formals = dict()
uses = set()
for (formal, _) in chapel.each_matching(root, Formal):
# For now, it's harder to tell if we're ignoring 'this' formals
# (what about method calls with implicit receiver?). So skip
# 'this' formals.
if formal.name() == "this":
continue
# extern functions have no bodies that can use their formals.
if formal.parent().linkage() == "extern":
continue
formals[formal.unique_id()] = formal
for (use, _) in chapel.each_matching(root, Identifier):
refersto = use.to_node()
if refersto:
uses.add(refersto.unique_id())
for unused in formals.keys() - uses:
yield formals[unused]
This function performs _two_ pattern-based searches: one for formals, and one for identifiers that might reference the formals. It then emits a warning for each formal for which there wasn’t a corresponding identifier.
Location Rules
Sometimes, a linter rule is not based on a pattern of AST nodes, but rather on a
textual pattern in the source code. For example, a rule might check that all lines
in a file are indented with spaces, not tabs. To support this, chplcheck has
a third type of rule: location rules. These rules are specified using the
driver.location_rule decorator. Location rules yield a RuleLocation
object that specifies the textual location of the issue. A RuleLocation has
a path, a start position, and an end position. The start and end positions are
tuples of line and column numbers, where the first character in the file is at
line 1, column 1.
Alternatively, a location rule can yield a LocationRuleResult object which
wraps a RuleLocation object along with other data, like fixits.
The following example demonstrates a location rule that checks for tabs:
@driver.location_rule
def NoTabs(context: chapel.Context, path: str, lines: List[str])
for line, text in enumerate(lines, start=1):
if '\t' in text:
yield RuleLocation(line, (i, 1), (i, len(line) + 1))
Making Rules Ignorable
The linter has a mechanism for marking a rule as supporting the @chplcheck.ignore attribute. When rules are marked as such, the linter will automatically
provide a fixit to apply the attribute.
Ignorable basic rules should return BasicRuleResult with ignorable set
to True rather than just a boolean. The BasicRuleResult constructor
takes a AstNode as an argument, which is the node that the rule is being
applied to. For example, the following defines a basic rule that is ignorable:
@driver.basic_rule(chapel.Function)
def NoFunctionFoo(context, node):
if node.name() == "foo":
return BasicRuleResult(node, ignorable=True)
return True
Ignorable advanced rules should yield a AdvancedRuleResult with anchor
set rather than just a AstNode. The AdvancedRuleResult constructor
takes an AstNode as an argument, which is the node that the rule is being
applied to. The anchor is the node should have a @chplcheck.ignore
annotation to suppress the warning. anchor and node can be the same
node. For example, the following defines an advanced rule that is ignorable:
@driver.advanced_rule
def NoLoopIndexI(context, root):
for loop, _ in chapel.each_matching(root, IndexableLoop):
idx = loop.index()
if idx.name() == "i":
yield AdvancedRuleResult(idx, anchor=loop)
Since loop indices can’t have attributes applied to them directly, the rule above uses the parent loop as an anchor. Applying the attribute to the loop will silence the warning on the index.
Fixits
Rules can have fixits associated with them, which allow chplcheck to
automatically resolve issues it encounters. To define a fixit, the rule should
construct a Fixit object and associate it with its result
(BasicRuleResult or AdvancedRuleResult for basic and advanced rules,
respectively). This can be done in two ways (see below).
A Fixit contains a list of Edit objects to apply to the code and an
optional description, which is shown to the user when the fixit is applied.
Edit objects contain a file path, a range defined by start and end
positions, and the text to replace inside of that range. The recommend way to
create an Edit object is to use the Edit.build class method, which
takes a chapel.Location and the text to replace it with.
For example, the following defines a rule that has a fixit associated with it:
@driver.basic_rule(chapel.Function)
def NoFunctionFoo(context, node):
if node.name() == "foo":
fixit = Fixit.build(Edit.build(node.name_location(), "bar"))
fixit.description = "Replace 'foo' with 'bar'"
return BasicRuleResult(node, fixits=[fixit])
return True
Note
The fixit for ‘NoFunctionFoo’ demonstrated here is not production-ready. It does not rename the uses of the function, nor does it check for conflicts with other names in the file. It is intended only to demonstrate the API for defining fixits. The same is true for various other versions of this example in this section.
The above code snippet directly uses a fixits= argument to the
BasicRuleResult constructor. This is one of two ways to associate fixits with
rules. Both advanced and basic rule results support this constructor argument.
Constructing the Edit objects can become relatively complicated for more
powerful auto-fixes. In practice, chplcheck developers have observed
that the code to construct the necessary edits can be longer than the code
implementing the rule. To provide a separation of concerns between
“what should be warned about” and “how to fix the warning”, chplcheck
provides a second mechanism to attach fixits to rules: the @driver.fixit
decorator.
This decorator accepts the rule-to-be-fixed as an argument, and wraps a function
that accepts the result of the rule. The wrapped function should use
the information returned by the rule to construct a Fixit object or a list of
Fixit objects.
For example, the snippet above can be written using the decorator as follows:
@driver.basic_rule(chapel.Function)
def NoFunctionFoo(context, node):
return node.name() != "foo"
@driver.fixit(NoFunctionFoo)
def FixNoFunctionFoo(context, result: BasicRuleResult):
fixit = Fixit.build(Edit.build(result.node.name_location(), "bar"))
fixit.description = "Replace 'foo' with 'bar'"
return fixit
Multiple fixits can be attached to a rule by using the @driver.fixit decorator
several times.
Particularly complicated rules can do a number of AST traversals and computations
to determine whether a warning should be emitted. The information gathered
in the process of these computations may be required to issue a fixit. When
using the @driver.fixit decorator, it appears as though this information
is lost. To address this, both BasicRuleResult and AdvancedRuleResult
accept an additional data argument. This argument can be of any type.
When decorator-based fixit functions are invoked, this data can be accessed
as a field on the result object.
@driver.basic_rule(chapel.Function)
def NoFunctionFoo(context, node):
if node.name() == "foo":
return BasicRuleResult(node, data="some data")
return
@driver.fixit(NoFunctionFoo)
def FixNoFunctionFoo(context, result: BasicRuleResult):
print(result.data) # prints "some data"
fixit = Fixit.build(Edit.build(result.node.name_location(), "bar"))
fixit.description = "Replace 'foo' with 'bar'"
return fixit
Note
The API for defining fixits is still under development and may change in the future.
Settings
Some rules may need to be configurable from the command line. For example, a
TabSize rule might need to know what the tab size is set to. Rules can
declare what settings they need with the settings argument, which is a list
of setting names. Settings that begin with . are consided local to the rule,
while settings that do not are considered global. For example, the following
rule declares rule TabSize with a setting Size:
@driver.basic_rule(chapel.Function, settings=[".Size"])
def TabSize(context, node, Size = None):
s = int(Size)
print("Tab size is", s)
# ...logic to check tab size...
The setting is then accessed as a keyword argument to the rule function.
If the setting is not provided, a default value of None is used. The setting is
always provided as a string, if a different type is required (like an integer or
a comma-separated-list) the rule author must write code to convert the types.
The TabSize.Size setting can now be set from the command line with --setting TabSize.Size=4.
Adding Custom Rules
Developers may have their own preferences for their code they would like to be
enforced by a linter. Rather than adding their own rule to rules.py,
developers can load a custom rule file that contains all of their custom rules.
For example, the following code is a complete definition of two new rules for
chplcheck. Note that the top-level function must be named rules and take
one argument.
# saved in file `myrules.py`
import chapel
def rules(driver):
@driver.basic_rule(chapel.Function)
def NoFunctionFoo(context, node):
return node.name() != "foo"
@driver.basic_rule(chapel.Variable, default=False)
def NoVariableBar(context, node):
return node.name() != "bar"
To use these rules with chplcheck, use the --add-rules command line
argument.
Saving the following file into myfile.chpl:
1proc foo() {
2 var bar = 10;
3}
The linter is run as follows:
> chplcheck myfile.chpl --add-rules path/to/my/myrules.py --enable-rule NoVariableBar
path/to/myfile/myfile.chpl:1: node violates rule NoFunctionFoo
path/to/myfile/myfile.chpl:2: node violates rule NoVariableBar
Developers may also find it helpful to maintain documentation for their custom
rules. Adding a Python docstring to the rule function will include the
documentation in the --list-rules output. This docstring can also be used to
generate Sphinx documentation for the rule. This can be done by running the
chplcheck-docs.py script. For example:
> $CHPL_HOME/doc/util/chplcheck-docs.py path/to/my/myrules.py -o my/out/directory
This will generate a rules.rst file in my/out/directory that contains
the documentation for the rules in myrules.py. Note that this script is
currently only available in the Chapel source tree.
Flags
chplcheck supports a number of command line flags to control its behavior.
Any flag that starts with -- can also be specified
in a configuration file.
Common Flags |
Description |
|
Specify a config file to read from. |
|
Enable a rule by name. |
|
Disable a rule by name. |
|
Add custom rules from the specified file. This can be used multiple times to add multiple rule files. |
|
Set a rule-specific setting.
e.g., |
|
Skip linting the specified file. Can be used multiple times. |
|
List all available rules. |
|
List all currently enabled rules. |
|
Run with the Language Server Protocol. This should only be used when integrating with an editor. |
Flags for Fixits |
Description |
|
Apply fixits to the file. By default, this is done in-place, overwriting the original file with the fixed version. |
|
Apply fixits to a new file with the given suffix
appended to the original file name. For example,
|
|
Start an interactive session where you can choose which fixits to apply. |
Other Flags |
Description |
|
Skip linting code that is marked as
unstable (via |
|
[Don’t] skip linting the bundled Chapel modules. They are skipped by default. |
|
Consider |
|
Lint code that uses an internal prefix. This is disabled by default. |
|
Add a file to check, can be used multiple times. Files can also be listed as positional arguments. |
Current Rules
The following is a list of all the rules currently implemented in chplcheck:
CamelOrPascalCaseVariables
Is enabled by default? Yes
Warn for variables that are not ‘camelCase’ or ‘PascalCase’.
Variables should use camelCase or PascalCase naming convention.
This variable uses snake_case naming convention which violates the rule
var my_variable = 10;
This variable uses camelCase naming convention which is correct
var myVariable = 10;
This variable uses PascalCase which is also correct
var MyVariable = 10;
CamelCaseRecords
Is enabled by default? Yes
Warn for records that are not ‘camelCase’.
Records should use camelCase naming convention.
This record uses PascalCase which violates the rule
record MyRecord {
var id: int;
var name: string;
}
This record uses camelCase which is correct
record myRecord {
var id: int;
var name: string;
}
CamelCaseFunctions
Is enabled by default? Yes
Warn for functions that are not ‘camelCase’.
Functions should use camelCase naming convention.
This function uses snake_case which violates the rule
proc My_Function() {
writeln("Hello");
}
This function uses camelCase which is correct
proc myFunction() {
writeln("Hello");
}
PascalCaseClasses
Is enabled by default? Yes
Warn for classes that are not ‘PascalCase’.
Classes should use PascalCase naming convention.
This class uses snake_case which violates the rule
class movie_actor {
var name: string;
var age: uint;
}
This class uses PascalCase which is correct
class MovieActor {
var name: string;
var age: uint;
}
PascalCaseModules
Is enabled by default? Yes
Warn for modules that are not ‘PascalCase’.
Modules should use PascalCase naming convention.
This module uses camelCase which violates the rule.
module modX {
var x: string = "Module MX";
proc printX() {
writeln(x);
}
}
This module uses PascalCase which is correct.
module ModX {
var x: string = "Module MX";
proc printX() {
writeln(x);
}
}
UseExplicitModules
Is enabled by default? Yes
Warn for code that relies on auto-inserted implicit modules.
Code should use explicit module declarations rather than relying on auto-inserted implicit modules.
This code relies on an implicit module which violates the rule
writeln("Hello, World!");
This code uses an explicit module which is correct
module MyModule {
proc main(){
writeln("Hello, World!");
}
}
DoKeywordAndBlock
Is enabled by default? Yes
Warn for redundant ‘do’ keyword before a curly brace ‘{‘.
Using both the ‘do’ keyword and curly braces is redundant.
for i in 1..10 do {
writeln(i);
}
ControlFlowParentheses
Is enabled by default? Yes
Warn for unnecessary parentheses in conditional statements and loops.
Conditional statements in Chapel do not require parentheses around the condition. The following demonstrate this, the two if statements are equivalent.
config const value = 5;
if (value > 0) then
writeln("Value is positive");
if value > 0 then
writeln("Value is positive");
SimpleBoolConditional
Is enabled by default? Yes
Warn for boolean conditionals that can be simplified.
Conditionals that result in a single boolean value do not need to be written
as a conditional expression. The following demonstrates this.
The expressions for A and B are equivalent.
config const x = 1;
const A = x == 1;
const B = if x == 1 then true else false;
This is also true for if statements which return a boolean value on both
branches. The procedures foo and bar are equivalent.
proc foo(x) {
if x == 1 then
return true;
else
return false;
}
proc bar(x) {
return x == 1;
}
NestedCoforalls
Is enabled by default? Yes
Warn for nested ‘coforall’ loops, which could lead to performance hits.
Nested coforall loops can lead to performance issues because each coforall creates one task per iteration. A nested coforall creates N×M tasks, which can overwhelm the runtime with excessive task creation overhead.
This creates 10 × 10 = 100 tasks (one for each (i,j) pair). With larger ranges, this quickly becomes problematic: e.g., 1000 × 1000 = 1,000,000 tasks
coforall i in 1..10 {
coforall j in 1..10 {
writeln(i, j);
}
}
This creates only 10 tasks (one per value of i). The inner loop runs serially within each task.
coforall i in 1..10 {
for j in 1..10 {
writeln(i, j);
}
}
BoolLitInCondStmt
Is enabled by default? Yes
Warn for boolean literals like ‘true’ in a conditional statement.
Boolean literals like ‘true’ or ‘false’ should not be used in conditional statements.
The condition is always true, so the if statement is unnecessary.
if true {
writeln("Always executes");
}
Just execute the code directly.
writeln("Always executes");
Similarly, this code will never execute, we can remove it.
if false {
writeln("Never executes");
}
ChplPrefixReserved
Is enabled by default? Yes
Warn for user-defined names that start with the ‘chpl_’ reserved prefix.
User-defined names should not start with a reserved prefix.
This variable uses a reserved prefix which violates the rule.
var chpl_myVar = 10;
This variable uses a regular name which is correct.
var myVar = 10;
MethodsAfterFields
Is enabled by default? Yes
Warn for classes or records that mix field and method definitions.
Classes and records should define all fields before methods.
This class mixes fields and methods which violates the rule.
class BadClass {
var name: string;
proc printName() {
writeln(name);
}
var age: int;
}
This class defines all fields before methods which is correct.
class GoodClass {
var name: string;
var age: int;
proc printName() {
writeln(name);
}
}
EmptyStmts
Is enabled by default? Yes
Warn for empty statements (i.e., unnecessary semicolons).
Empty statements (unnecessary semicolons) should be avoided.
This has an empty statement which violates the rule.
var x = 10;;
This has no empty statement which is correct.
var y = 10;
UnusedTupleUnpack
Is enabled by default? Yes
Warn for unused tuple unpacking, such as ‘(_, _)’.
Tuple unpacking should use at least one element, not ignore all with ‘(_, _)’.
var tuples = [(1, 2), (3, 4), (5, 6)];
All elements are ignored, the unpacking serves no purpose.
for (_, _) in tuples {
writeln("processing");
}
At least one element used.
for (i, _) in tuples {
writeln(i);
}
preferred: we can skip unpacking
for tuples {
writeln("processing");
}
ComplexLiteralOrder
Is enabled by default? Yes
Warn for complex literals that are not in a consistent order.
ConsecutiveDecls
Is enabled by default? Yes
Warn for consecutive variable declarations that can be combined.
Consecutive variable declarations of the same type can be combined.
These consecutive declarations violate the rule.
var x: int;
var y: int;
This combined declaration follows the rule.
var a, b: int;
MisleadingIndentation
Is enabled by default? Yes
Warn for single-statement blocks that look like they might be multi-statement blocks.
Single-statement blocks should not have misleading indentation.
var x = true;
The indentation suggests both statements are inside the if, but only the first is.
if x then
writeln("First");
writeln("Second");
Indentation adjusted to reflect the actual control flow.
if x then
writeln("First");
writeln("Second");
Or use braces to include both statements in the block.
if x {
writeln("First");
writeln("Second");
}
The indentation suggests both statements are inside the for, but only the first is.
for i in 1..3 do
writeln("Hello");
writeln("World");
Indentation adjusted to reflect the actual control flow.
for i in 1..3 do
writeln("Hello");
writeln("World");
Or use braces.
for i in 1..3 {
writeln("Hello");
writeln("World");
}
UnusedFormal
Is enabled by default? Yes
Warn for unused formals in functions.
Function formals should be used in the function body.
This function has an unused formal which violates the rule.
proc badProc(x: int, y: int) {
writeln(x);
}
This function uses all formals which is correct.
proc goodProc(x: int, y: int) {
writeln(x, y);
}
UnusedTaskIntent
Is enabled by default? Yes
Warn for unused task intents in functions.
UnusedTypeQuery
Is enabled by default? Yes
Warn for unused type queries in functions.
UnusedLoopIndex
Is enabled by default? Yes
Warn for unused index variables in loops.
Loop indices should be used, or omitted if not needed.
Unused index,it will be flagged by the rule.
for i in 1..10 {
writeln("Hello");
}
Index used this is the preferred style.
for i in 1..10 {
writeln(i);
}
Index omitted,this is also preferred.
for 1..10 {
writeln("Hello");
}
SimpleDomainAsRange
Is enabled by default? Yes
Warn for simple domains in loops that can be ranges.
IncorrectIndentation
Is enabled by default? Yes
Warn for inconsistent or missing indentation
Code should have consistent indentation within blocks.
This has inconsistent indentation which violates the rule.
proc foo() {
writeln("Hello, World!");
writeln("Hello, World!");
}
This has consistent indentation which follows the rule.
proc foo() {
writeln("Hello, World!");
writeln("Hello, World!");
}
MissingInIntent
Is enabled by default? Yes
Warn for formals used to initialize fields that are missing an ‘in’ intent.
LineLength
Is enabled by default? Yes
Warn for lines that exceed a maximum length. By default, the maximum line length is 80 characters. This can be configured with –setting LineLength.Max=<number>.
- Settings:
.Max
Lines should not exceed the maximum length (default 80 characters).
Exceeds 80 characters which will be flagged by the rule.
proc sendMessage(recipientName: string, messageContent: string, priority: int): bool {
return true;
}
Reflowed to multiple lines, preferred style.
proc sendMessage(
recipientName: string,
messageContent: string,
priority: int
): bool {
return true;
}