Contributing
It is very much encouraged to first read the architectural guide to familiarize yourself with the glossary and rough outline of the project.
For general best practices refer to the contributing guidelines of the ALPM project.
Adding New Lint Rules
Prerequisites
Before adding a new lint rule, there are a few things that should be taken care of first:
- Define the problem your lint rule should detect and, if applicable, what needs to be done to resolve it.
- Read the LintRuletrait implementation to get an overview of the API surface.
- Take a look at a few existing LintRule implementationsand theExampleto get a rough idea on how to write a lint.
Checklist
As a guideline, it is advised to follow this checklist:
- Lint rule file created in correct scope directory.
- Scope, Group and Level are set correctly.
- 
Rule is registered in LintStore::register().
- Documentation is correct.
- Eventual links or configuration options are exposed via their respective functions.
- At least two tests have been written and are passing. If possible include real-world examples that inspired this rule when writing tests.
LintRule implementation
Determine the scope
Figure out which scope the new lint will be applied to:
- LintScope::SourceInfo- For- .SRCINFOfile-specific lints
- LintScope::PackageBuild- For- PKGBUILDfile-specific lints
- LintScope::PackageInfo- For- .PKGINFOfile-specific lints
- LintScope::BuildInfo- For- .BUILDINFOfile-specific lints
- LintScope::SourceRepository- For lints that need both- PKGBUILDand- .SRCINFO
- LintScope::Package- For lints that need both- .PKGINFOand- .BUILDINFO
See the LintScope API documentation for more detailed information about these scopes.
Create the lint rule file
Create a new file in the submodules of the respective scope in src/lint_rules. E.g. src/lint_rules/source_info/my_new_lint.rs.
It is encouraged to use the Example template as a starting point.
The example gives you a general structure, example functions and commented out optional functions with an explanation on when/how to use them.
If you notice that the example is outdated, please open an issue!
Register the lint rule
Once you are finished writing your lint, add the lint to the LintStore's register() method.
This is fairly straightforward and only involves a single import and one line.
The following example shows how to add MyNewLint to src/lint_rules/store.rs:
#![allow(unused)] fn main() { fn register(&mut self) { self.lint_constructors = vec![ // ... DuplicateArchitecture::new_boxed, MyNewLint::new_boxed, // <--- UnsafeChecksum::new_boxed, // ... ]; } }
NOTE Don't forget to keep that array sorted when adding new lints! We want to keep things organized.
Reporting issues
When your lint rule detects problems, it must report them using the LintIssue type.
Picking the correct issue type and using good wording is crucial to provide a good user experience.
Inside each LintIssue is a LintIssueType, which is the type that actually provides detailed information about the issue.
Take a look at the SourceInfoIssue enum go get an idea of how this looks like.
Be aware that SourceInfoIssue and equivalents also implement Into<LintIssueType> for your convenience.
LintIssues and its LintIssueType are converted to LintIssueDisplay before being printed, to ensure a consistent formatting across the whole linting framework.
The LintIssueDisplay documentation explains best how this works, what types of fields there are and how the final layout looks like.
The documentation of SourceInfoIssue and its equivalent types also provide detailed information on how the various variants' fields are used to create a LintIssueDisplay.
Creating LintIssue instances
In your Lintrule::run() method, push LintIssue instances to the issues vector:
#![allow(unused)] fn main() { fn run(&self, resources: &Resources, issues: &mut Vec<LintIssue>) -> Result<(), Error> { // Your linting logic here if problem_detected { issues.push(LintIssue::from_rule( SourceInfoIssue::BaseField { field_name: "pkgname".to_string(), value: "invalid-name".to_string(), context: "Invalid package name format".to_string(), architecture: None, }.into(), )); } Ok(()) } }
In the example above, a new LintIssue is created from your lint rule's data, in combination with a [LintIssueType::SourceInfo] that is created from the SourceInfoIssue.into() call.
In this example, the SourceInfoIssue::BaseField variant is used to indicate problems on the pkgname field of a SourceInfo's pkgbase section.
Extra links
You can provide extra links for more information to a lint error message.
For this, implement the extra_links function of the LintRule trait for your implementation.
    /// A link map of `name -> URL` that provide extra context to people that encounter this lint.
    fn extra_links(&self) -> Option<BTreeMap<String, String>>;
Configuration options
If your lint rule needs to be configurable, you might have to add a new configuration options.
For this, you need to adjust the configuration struct in the alpm-lint-config crate.
Edit alpm-lint-config/src/lint_rule.rs and add your new option to the create_lint_rule_config! macro at the bottom of the file:
#![allow(unused)] fn main() { linting_config! { /// This is a test option test_option: String = "This is an option", /// Description of your new configuration option my_config_option: bool = false, // Add this line } }
Afterwards reference it in your lint rule's configuration_options() method as shown in example lint rule.
Write integration tests
Every lint rule must have at least two integration tests in tests/lint_rule_tests/{scope}/{rule_name}.
One for a pass run where the rule is not triggered on correct data, and one fail run where the lint rule properly detects an issue.
If your lint rule covers multiple cases, cover all of these cases via tests.
Also, check out the tests/fixtures.rs module in case you need stub data for testing.