-
Notifications
You must be signed in to change notification settings - Fork 424
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Auto-generate documentation for chplcheck lint rules (#26208)
Adds documentation for `chplcheck` rules, auto-generated from the existing `chplcheck` rule defintions. This PR makes the following changes: 1. `make docs` will now build a `rules.rst` file which is included in the `chplcheck` documentation which lists all lint rules - This is enabled by the `doc/util/chplcheck-docs.py`, which is also available to users for their own custom rule files - Rules can also have examples, specified in `tools/chplcheck/examples`. These are included in the online docs and provide reasoning to users about why a lint rule exists - ![Screenshot 2024-12-16 at 3 00 10 PM](https://github.com/user-attachments/assets/7896d06d-0e45-4ae3-845d-51ac7b022876) - ![Screenshot 2024-12-16 at 2 57 24 PM](https://github.com/user-attachments/assets/254241fd-d58f-4645-9acb-ec5ed1290009) 2. The `chplcheck` langauge server will provides a link straight to rule documentation ![Screenshot 2024-11-04 at 4 08 44 PM](https://github.com/user-attachments/assets/e96ee882-324c-4561-8731-24ab254b51cc) Future work: - Add more examples to `tools/chplcheck/examples` [Reviewed by @DanilaFe]
- Loading branch information
Showing
26 changed files
with
359 additions
and
20 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,210 @@ | ||
#!/usr/bin/env python3 | ||
|
||
""" | ||
Auto-generates a *.rst for the chplcheck rules defined | ||
`$CHPL_HOME/tools/chplcheck/src/rules.py` | ||
""" | ||
|
||
import os | ||
import typing | ||
from dataclasses import dataclass | ||
import argparse as ap | ||
import ast | ||
import shutil | ||
import chpl2rst | ||
|
||
|
||
@dataclass | ||
class Rule: | ||
name: str | ||
description: str | ||
patterns: typing.List[str] | ||
default: bool | ||
settings: typing.List[str] | ||
example_text: str = "" | ||
|
||
def rst(self): | ||
lines = [] | ||
lines.append(self.name) | ||
lines.append("~" * len(self.name)) | ||
lines.append("") | ||
lines.append( | ||
"Is enabled by default? " + ("Yes" if self.default else "No") | ||
) | ||
lines.append("") | ||
|
||
if self.description: | ||
lines.append(self.description) | ||
lines.append("") | ||
|
||
if self.settings: | ||
lines.append("Settings:") | ||
for setting in self.settings: | ||
lines.append(f" - ``{setting}``") | ||
lines.append("") | ||
|
||
if self.example_text: | ||
lines.append(self.example_text) | ||
lines.append("") | ||
|
||
return "\n".join(lines) | ||
|
||
def add_example(self, example_directory: str): | ||
# find the example file | ||
example_file = os.path.join(example_directory, self.name + ".chpl") | ||
if not os.path.exists(example_file): | ||
return | ||
|
||
with open(example_file) as handle: | ||
pieces = chpl2rst.to_pieces(handle, False) | ||
rstoutput = chpl2rst.gen_rst(pieces, example_file) | ||
|
||
self.example_text = rstoutput | ||
|
||
|
||
def find_rules(file: str): | ||
|
||
def get_rule(func) -> typing.Optional[Rule]: | ||
""" | ||
Given a function definition, return a Rule object if it is a rule, otherwise None | ||
""" | ||
if not isinstance(func, ast.FunctionDef): | ||
return None | ||
|
||
# find all decorators by walking decorator_list and filtering for Attribute | ||
decorators = [ | ||
i | ||
for d in func.decorator_list | ||
for i in ast.walk(d) | ||
if isinstance(i, ast.Attribute) | ||
] | ||
|
||
# keep only the decorators that are rules | ||
decorators = [ | ||
d | ||
for d in decorators | ||
if d.attr in ["basic_rule", "advanced_rule", "location_rule"] | ||
] | ||
# if there are no decorators, this is not a rule | ||
if not decorators: | ||
return None | ||
|
||
# get the name of the rule | ||
name = func.name | ||
# get the docstring of the rule | ||
description = ast.get_docstring(func) or "" | ||
|
||
# filter out decorators that are not calls | ||
decorators_filtered = [ | ||
d | ||
for d in decorators | ||
if isinstance(d, ast.Call) and isinstance(d.func, ast.Attribute) | ||
] | ||
|
||
# get the patterns, if they exist | ||
# the patterns are the first argument of the decorator | ||
# we get the string representation of the pattern | ||
patterns = [ | ||
ast.unparse(d.args[0]) | ||
for d in decorators_filtered | ||
if len(d.args) > 0 | ||
] | ||
|
||
# determine if the rule is enabled by default | ||
# default = any(d.attr == 'default' for d in decorators) | ||
default_settings = [ | ||
ast.unparse(next(k.value for k in d.keywords)) | ||
for d in decorators_filtered | ||
if len(d.keywords) > 0 | ||
and any(k.arg == "default" for k in d.keywords) | ||
] | ||
is_default = not any(d == "False" for d in default_settings) | ||
|
||
# grab the settings for the rule | ||
settings = [] | ||
settings_node = [ | ||
next(k.value for k in d.keywords) | ||
for d in func.decorator_list | ||
if isinstance(d, ast.Call) | ||
and isinstance(d.func, ast.Attribute) | ||
and len(d.keywords) > 0 | ||
and any(k.arg == "settings" for k in d.keywords) | ||
] | ||
if settings_node: | ||
# settings node should be a list with 1 element, an ast.List object | ||
# get each the value of each element from the list | ||
settings = [s.value for s in settings_node[0].elts] | ||
|
||
return Rule(name, description, patterns, is_default, settings) | ||
|
||
with open(file) as f: | ||
tree = ast.parse(f.read()) | ||
|
||
# find the rules function using a matcher | ||
rules_def_func = None | ||
for node in ast.walk(tree): | ||
if isinstance(node, ast.FunctionDef) and node.name == "rules": | ||
rules_def_func = node | ||
break | ||
|
||
if rules_def_func is None: | ||
return [] | ||
|
||
rules = [get_rule(r) for r in rules_def_func.body] | ||
return list([r for r in rules if r is not None]) | ||
|
||
|
||
def rst_rules(rules): | ||
return "\n".join([r.rst() for r in rules]) | ||
|
||
|
||
def output_rules(rules: typing.List[Rule], output_dir: str): | ||
# remove the existing output directory | ||
if os.path.exists(output_dir): | ||
shutil.rmtree(output_dir) | ||
os.makedirs(output_dir) | ||
|
||
# output the rules file | ||
with open(os.path.join(output_dir, "rules.rst"), "w") as f: | ||
f.write(rst_rules(rules)) | ||
|
||
|
||
def main(): | ||
a = ap.ArgumentParser() | ||
a.add_argument( | ||
"-r", | ||
"--rules", | ||
default=[], | ||
action="append", | ||
help="Rules to generate documentation for", | ||
) | ||
a.add_argument( | ||
"-o", | ||
"--output", | ||
default="chplcheck-rules-out", | ||
help="Directory where all the relevant docs files will be written", | ||
) | ||
a.add_argument( | ||
"--examples-directory", | ||
default=None, | ||
help="Directory where all the relevant examples are located", | ||
) | ||
args = a.parse_args() | ||
|
||
rules: typing.List[Rule] = [] | ||
|
||
# collect the rules | ||
for rule_file in args.rules: | ||
rules.extend(find_rules(rule_file)) | ||
|
||
# collect the examples | ||
if args.examples_directory: | ||
for rule in rules: | ||
rule.add_example(args.examples_directory) | ||
|
||
# output the rules | ||
output_rules(rules, args.output) | ||
|
||
|
||
if __name__ == "__main__": | ||
main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
--enable-rule ConsecutiveDecls --enable-rule BoolLitInCondStatement --enable-rule UseExplicitModules --enable-rule CamelOrPascalCaseVariables --enable-rule NestedCoforalls --internal-prefix myprefix_ --internal-prefix _ --skip-unstable |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
../../tools/chplcheck/examples/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
--disable-rule UseExplicitModules |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
*.chpl.fixed |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
--stop-after-pass=parseAndConvertUast |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
/* | ||
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"); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
ControlFlowParentheses.chpl:7: node violates rule ControlFlowParentheses |
Oops, something went wrong.