Compare commits

...

373 commits

Author SHA1 Message Date
Sijawusz Pur Rahnama
a42b218ca6
Merge pull request #465 from crystal-ameba/fix-issue-464
Revert to `nil` in `Rule::Lint::Typos.BIN_PATH` if `Process.find_executable` fails
2024-07-25 14:26:44 +02:00
Sijawusz Pur Rahnama
a836e2c8d0 Revert to nil if Process.find_executable fails 2024-07-25 13:33:31 +02:00
Sijawusz Pur Rahnama
e0fa8bbcc2
Merge pull request #461 from crystal-ameba/dependabot/github_actions/docker/build-push-action-6 2024-06-18 11:59:56 +02:00
dependabot[bot]
c4cc71e248
Bump docker/build-push-action from 5 to 6
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 5 to 6.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v5...v6)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-17 21:45:07 +00:00
Sijawusz Pur Rahnama
6d03cef6df
Merge pull request #460 from crystal-ameba/fix-issue-459
Make sure we only return files from `GlobUtils#expand` method
2024-04-17 23:46:44 +02:00
Sijawusz Pur Rahnama
f12e7f6c5d Add spec 2024-04-17 23:43:03 +02:00
Sijawusz Pur Rahnama
1bd59c1bf0 Make GlobUtils extend self for easier access 2024-04-17 23:42:05 +02:00
Sijawusz Pur Rahnama
5403aee899 Make sure we only return files from GlobUtils#expand method 2024-04-17 12:07:44 +02:00
dependabot[bot]
e6a5fa9d71
Merge pull request #458 from crystal-ameba/dependabot/github_actions/szenius/set-timezone-2.0 2024-04-10 22:03:00 +00:00
dependabot[bot]
a3f906a38a
Bump szenius/set-timezone from 1.2 to 2.0
Bumps [szenius/set-timezone](https://github.com/szenius/set-timezone) from 1.2 to 2.0.
- [Release notes](https://github.com/szenius/set-timezone/releases)
- [Commits](https://github.com/szenius/set-timezone/compare/v1.2...v2.0)

---
updated-dependencies:
- dependency-name: szenius/set-timezone
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-10 21:30:02 +00:00
Sijawusz Pur Rahnama
107c6e0ea6
Merge pull request #455 from crystal-ameba/tweak-spec-filename-rule 2024-03-10 00:51:12 +01:00
Sijawusz Pur Rahnama
a661cf10fc Ignore files with extensions other than .cr 2024-03-09 22:38:47 +01:00
Sijawusz Pur Rahnama
a2c9aa67cc Add missed spec case for files outside spec/ folder 2024-03-09 22:38:23 +01:00
Sijawusz Pur Rahnama
e2d6c69039 Include SpecFilename rule properties in its documentation 2024-03-09 22:37:58 +01:00
Sijawusz Pur Rahnama
63be60ce96
Merge pull request #452 from crystal-ameba/reopen-issue-447 2024-01-22 19:30:21 +01:00
Sijawusz Pur Rahnama
17084f4a1d Remove naive solution for #447 2024-01-22 17:15:52 +01:00
Sijawusz Pur Rahnama
590640b559
Merge pull request #451 from crystal-ameba/tweak-useless-assign
Refactor `Lint/UselessAssign` rule a bit
2024-01-20 16:15:56 +01:00
Sijawusz Pur Rahnama
3bea264948 Refactor Lint/UselessAssign rule a bit 2024-01-19 20:11:56 +01:00
Sijawusz Pur Rahnama
7f50ff90fd
Merge pull request #450 from crystal-ameba/fix-issue-447
Exclude reporting type declarations passed as call arguments
2024-01-18 07:52:48 +01:00
Sijawusz Pur Rahnama
a79e711fae Exclude reporting type declarations passed as call arguments 2024-01-18 00:34:28 +01:00
Sijawusz Pur Rahnama
28fafea19f
Merge pull request #449 from crystal-ameba/fix-issue-446
Do not report type declarations within generic records
2024-01-16 09:20:20 +01:00
Sijawusz Pur Rahnama
f2677d68f6 Do not report type declarations within generic records 2024-01-16 02:04:41 +01:00
Vitalii Elenhaupt
b56d34715d
Merge pull request #445 from straight-shoota/infra/makefile
Enhance `Makefile`
2024-01-15 19:07:14 +02:00
Johannes Müller
1398c0ee8f
Enhance Makefile
* `install` recipe does not rebuild binary
* Add `help` target and documentation
* Add several config variables
* Add sources as dependencies for (in-)validation

Based on template
https://gist.github.com/straight-shoota/275685fcb8187062208c0871318c4a23
2024-01-14 13:29:29 +01:00
Sijawusz Pur Rahnama
734bb2a7f1
Merge pull request #443 from crystal-ameba/fix-issue-442
Do not report type declarations within `lib` definitions
2024-01-10 09:12:37 +01:00
Sijawusz Pur Rahnama
98d5bc720a Skip lib definitions altogether 2024-01-10 01:14:03 +01:00
Sijawusz Pur Rahnama
d23ad7f0ab Make Scope#*_def? methods accept check_outer_scopes parameter 2024-01-10 01:10:36 +01:00
Sijawusz Pur Rahnama
b6bd74e02f
Merge pull request #434 from crystal-ameba/misc-refactors
v1.6.1
2024-01-09 21:12:19 +01:00
Sijawusz Pur Rahnama
ce3f2b7e4b Add QoL Variable#reference(scope) method 2024-01-01 14:49:27 +01:00
Sijawusz Pur Rahnama
444b07c179 Bump version to 1.6.1 2024-01-01 14:46:32 +01:00
Sijawusz Pur Rahnama
e99a69765f Few refactors 2024-01-01 14:46:32 +01:00
Sijawusz Pur Rahnama
6d0b12c70f Cleanup docs 2024-01-01 14:46:32 +01:00
Sijawusz Pur Rahnama
65ab317a3b Merge delegate calls 2024-01-01 14:46:32 +01:00
Sijawusz Pur Rahnama
452a7a867e
Merge pull request #430 from crystal-ameba/fix-issue-429
Report unused type declarations in `Lint/UselessAssign` rule
2024-01-01 12:22:01 +01:00
Sijawusz Pur Rahnama
5a24f1eba5 Add UselessAssign#exclude_type_declarations 2023-12-29 01:50:19 +01:00
Sijawusz Pur Rahnama
aeffa6ad00 Add test spec covering accessor macros 2023-12-28 15:46:32 +01:00
Sijawusz Pur Rahnama
4567293add Drop type_definition? check from Scope#top_level? 2023-12-28 15:43:29 +01:00
Sijawusz Pur Rahnama
a49faa33a9 Refactor Lint/UselessAssign spec 2023-12-28 15:43:29 +01:00
Sijawusz Pur Rahnama
1dd531740c Fix reported ameba issue 2023-12-28 15:43:29 +01:00
Sijawusz Pur Rahnama
1b661d633d Refactor ScopeVisitor to ignore accessor macros 2023-12-28 15:43:29 +01:00
Sijawusz Pur Rahnama
9745637cf9 Update UselessAssign rule to report unreferenced type declarations 2023-12-28 15:43:29 +01:00
Sijawusz Pur Rahnama
4ad151e5e0 Do not automatically consider type definitions as referenced 2023-12-28 15:43:29 +01:00
Sijawusz Pur Rahnama
c9bc01f88c
Merge pull request #439 from crystal-ameba/fix-issue-353
Make `Lint/SharedVarInFiber` rule account for `loop { ... }`
2023-12-28 15:31:43 +01:00
Sijawusz Pur Rahnama
1feb5c279b Add test spec covering the loop { … } block to Lint/SharedVarInFiber rule specs 2023-12-28 15:31:11 +01:00
Sijawusz Pur Rahnama
57898fd797 Make BranchVisitor treat loop { … } calls as branchable 2023-12-28 15:31:11 +01:00
Sijawusz Pur Rahnama
46a42ee9e8
Merge pull request #438 from crystal-ameba/add-error-as-allowed-variable-name
Add `error` to the `RescuedExceptionsVariableName#allowed_names`
2023-12-28 14:32:45 +01:00
Sijawusz Pur Rahnama
61afa5bb2b Add error to the RescuedExceptionsVariableName#allowed_names 2023-12-28 13:07:14 +01:00
Sijawusz Pur Rahnama
9bb6c9ac75
Merge pull request #436 from crystal-ameba/cleanup-properties-macro
Make `RuleConfig#properties` accept only `Call` nodes
2023-12-28 09:24:09 +01:00
Sijawusz Pur Rahnama
954345d316
Merge pull request #435 from crystal-ameba/revert-pr-394
Revert "Merge pull request #394 from stufro/388-raise-on-invalid-file…
2023-12-28 09:20:27 +01:00
Sijawusz Pur Rahnama
55f3ec53b7 Make RuleConfig#properties accept only Call nodes
Add optional named argument `as`, in order to specify the property type
2023-12-28 04:48:18 +01:00
Sijawusz Pur Rahnama
26d9bc0bd0 Revert "Merge pull request #394 from stufro/388-raise-on-invalid-file-path"
This reverts commit 18d193bd08, reversing
changes made to 7b8316f061.
2023-12-28 02:03:44 +01:00
Sijawusz Pur Rahnama
47088b10ca Add some more excluded operators to BinaryOperatorParameterName rule 2023-11-24 21:13:04 +01:00
Sijawusz Pur Rahnama
9f9d5fae32
Merge pull request #428 from crystal-ameba/revert-incorrect-excessive-allocations-condition
Revert "Fix `Performance/ExcessiveAllocations` to exclude `each` call…
2023-11-18 09:50:45 +01:00
Sijawusz Pur Rahnama
5e70ae4f8c
Merge pull request #427 from crystal-ameba/fix-gha-ci-badge-in-readme
Fix GitHub Actions CI badge in `README.md`
2023-11-18 06:34:34 +01:00
Sijawusz Pur Rahnama
82e0e53080 Revert "Fix Performance/ExcessiveAllocations to exclude each calls without a block"
This reverts commit 29e29b8e1d.
2023-11-17 19:48:41 +01:00
Sijawusz Pur Rahnama
1b8523def6
Fix GitHub Actions CI badge in README.md 2023-11-17 19:23:06 +01:00
Sijawusz Pur Rahnama
a88033c8ce
Merge pull request #426 from crystal-ameba/fix-issue-409
Do not report expanded arguments in `ShadowingOuterLocalVar` rule
2023-11-17 19:00:34 +01:00
Sijawusz Pur Rahnama
30e3816ed1 Do not report expanded arguments in ShadowingOuterLocalVar rule 2023-11-17 18:36:19 +01:00
Sijawusz Pur Rahnama
5aac63ea74
Merge pull request #425 from crystal-ameba/prepare-release-1.6.0
Prepare release 1.6.0
2023-11-17 18:14:20 +01:00
Sijawusz Pur Rahnama
10b577d23a Use square brackets for %w and %i array literals 2023-11-17 17:34:39 +01:00
Sijawusz Pur Rahnama
06dc201344 Bump supported crystal version to 1.10 2023-11-17 16:22:31 +01:00
Sijawusz Pur Rahnama
d079f4bae6 Bump version to 1.6.0 2023-11-14 12:34:44 +01:00
Sijawusz Pur Rahnama
0461fff702 Relax crystal version in shard.yml 2023-11-14 12:34:44 +01:00
Sijawusz Pur Rahnama
22e2d1de00 Cleanup my involvement status 2023-11-14 12:34:44 +01:00
Sijawusz Pur Rahnama
810a3440dd Remove shard version specifier from README.md 2023-11-14 12:34:44 +01:00
Sijawusz Pur Rahnama
f3f1f3a2ab Misc cleanups/refactors 2023-11-14 12:34:44 +01:00
Sijawusz Pur Rahnama
547fec5a94 Refactor TypeNames to report the name itself only 2023-11-14 11:22:17 +01:00
Sijawusz Pur Rahnama
a8b8c35cc7 Fix usage of deprecated methods 2023-11-14 11:22:17 +01:00
Sijawusz Pur Rahnama
11bf9ffcdc Remove unused include AST::Util 2023-11-14 11:22:17 +01:00
Sijawusz Pur Rahnama
52ccf23ef9 Remove deprecated Assignment#transformed? method 2023-11-14 11:22:17 +01:00
Sijawusz Pur Rahnama
b3f11913ed Reports also methods not ending with ? suffix in PredicateName rule 2023-11-14 11:22:17 +01:00
Sijawusz Pur Rahnama
633ed7538e Use prefer_name_location: true in PredicateName rule 2023-11-14 11:22:17 +01:00
Sijawusz Pur Rahnama
15d241e138 Add spec for AST::Util#{static,dynamic}_literal? 2023-11-14 11:22:17 +01:00
Sijawusz Pur Rahnama
52a3e47a3b
Merge pull request #423 from crystal-ameba/lint-not-nil-after-no-bang-reports-match-calls
Make `Lint/NotNilAfterNoBang` report calls to `#match`
2023-11-14 10:25:44 +01:00
Sijawusz Pur Rahnama
3b87aa6490
Merge pull request #424 from crystal-ameba/report-string-literals-in-ascii-identifiers-rule
Report symbol literals in `Naming/AsciiIdentifiers` rule
2023-11-14 10:24:29 +01:00
Sijawusz Pur Rahnama
018adb54be Add AsciiIdentifiers#ignore_symbols property 2023-11-14 05:22:29 +01:00
Sijawusz Pur Rahnama
be76b3682a Report string literals in AsciiIdentifiers rule 2023-11-14 05:15:38 +01:00
Sijawusz Pur Rahnama
775650c882 Add MultiAssign to NodeVisitor::NODES 2023-11-14 05:00:49 +01:00
Sijawusz Pur Rahnama
21a406e56d Make Lint/NotNilAfterNoBang report calls to #match 2023-11-14 03:50:58 +01:00
Sijawusz Pur Rahnama
0b225da9ba
Merge pull request #422 from crystal-ameba/refactor-adding-issues-with-name-location
Make it easier to add issues for nodes with name location preference
2023-11-13 19:16:14 +01:00
Sijawusz Pur Rahnama
0a2609c1b4 Add ip to the list of BlockParameterName#allowed_names 2023-11-12 11:59:06 +01:00
Sijawusz Pur Rahnama
06952fc7d3 Add op to the list of BlockParameterName#allowed_names 2023-11-12 11:36:03 +01:00
Sijawusz Pur Rahnama
f984d83b05 Use name(_end)_location helpers consistently 2023-11-12 10:24:12 +01:00
Sijawusz Pur Rahnama
98cc6fd612 Make it easier to add issues for nodes with name location preference 2023-11-12 10:23:36 +01:00
Sijawusz Pur Rahnama
6caf24ad6d
Merge pull request #421 from crystal-ameba/add-binary-operator-parameter-name-rule
Add `Naming/BinaryOperatorParameterName` rule
2023-11-12 09:56:26 +01:00
Sijawusz Pur Rahnama
e62fffae80
Merge pull request #419 from crystal-ameba/add-block-parameter-name-rule
Add `Naming/BlockParameterName` rule
2023-11-12 09:55:50 +01:00
Sijawusz Pur Rahnama
61ccb030bd Fix newly found offenses 2023-11-11 19:08:14 +01:00
Sijawusz Pur Rahnama
971bff6c27 Add Naming/BlockParameterName rule 2023-11-11 19:07:46 +01:00
Sijawusz Pur Rahnama
bf4219532f Add Naming/BinaryOperatorParameterName rule 2023-11-11 18:48:26 +01:00
Sijawusz Pur Rahnama
a40f02f77f
Merge pull request #420 from crystal-ameba/add-spec-filename-rule
Add `Lint/SpecFilename` rule
2023-11-11 08:26:33 +01:00
Sijawusz Pur Rahnama
bee4472a26
Merge pull request #418 from crystal-ameba/add-rescued-exceptions-variable-name-rule
Add `Naming/RescuedExceptionsVariableName` rule
2023-11-10 15:56:34 +01:00
Sijawusz Pur Rahnama
28014ada67 Add Lint/SpecFilename rule 2023-11-10 15:41:54 +01:00
Sijawusz Pur Rahnama
1d76a7c71a Add Naming/RescuedExceptionsVariableName rule 2023-11-10 13:26:31 +01:00
Sijawusz Pur Rahnama
0abb73f0b6
Merge pull request #414 from crystal-ameba/add-ascii-identifiers-rule
Add `Naming/AsciiIdentifiers` rule
2023-11-10 01:59:04 +01:00
Sijawusz Pur Rahnama
fd44eeba08 Add Naming/AsciiIdentifiers rule 2023-11-10 01:55:19 +01:00
Sijawusz Pur Rahnama
cc23e7a7e7
Merge pull request #415 from crystal-ameba/add-accessor-method-name-rule
Add `Naming/AccessorMethodName` rule
2023-11-09 10:34:37 +01:00
Sijawusz Pur Rahnama
964d011d53 Add Naming/AccessorMethodName rule 2023-11-09 10:30:59 +01:00
Sijawusz Pur Rahnama
3f1e925e07
Merge pull request #417 from crystal-ameba/fix-issue-400
Fix false positive with dynamic literals in `Lint/LiteralsComparison`
2023-11-09 09:09:54 +01:00
Sijawusz Pur Rahnama
e84cc05f0f Fix false positive with dynamic literals in Lint/LiteralsComparison 2023-11-09 08:20:35 +01:00
Sijawusz Pur Rahnama
7ceb3ffad9
Merge pull request #416 from crystal-ameba/add-filename-rule
Add `Naming/Filename` rule
2023-11-09 07:58:44 +01:00
Sijawusz Pur Rahnama
b9ce705a47 Add Naming/Filename rule 2023-11-09 07:07:45 +01:00
Sijawusz Pur Rahnama
881209d54e
Merge pull request #412 from crystal-ameba/group-documentation-rules
Move documentation-related rules into its own group
2023-11-09 06:19:36 +01:00
Sijawusz Pur Rahnama
bcb72fb3c3
Merge pull request #413 from crystal-ameba/group-naming-rules
Move naming-related rules into its own group
2023-11-09 06:18:48 +01:00
Sijawusz Pur Rahnama
b25dc402c8 Group naming-related rules 2023-11-09 00:16:29 +01:00
Sijawusz Pur Rahnama
8569355b5a Move documentation-related rules into its own group 2023-11-08 18:35:32 +01:00
Sijawusz Pur Rahnama
0c6745781e
Merge pull request #381 from crystal-ameba/add-typos-rule
Add `Lint/Typos` rule
2023-11-08 13:01:52 +01:00
Sijawusz Pur Rahnama
891cad2610 Install typos-cli on macOS CI 2023-11-08 02:24:35 +01:00
Sijawusz Pur Rahnama
0140fd3573 Add Lint/Typos rule 2023-11-08 02:24:35 +01:00
Sijawusz Pur Rahnama
9f6615bdfd
Merge pull request #380 from crystal-ameba/add-documentation-admonition-rule
Add `Lint/DocumentationAdmonition` rule
2023-11-06 17:03:50 +01:00
Sijawusz Pur Rahnama
1fccbfc8b8 Set timezone to UTC in CI 2023-11-06 16:59:09 +01:00
Sijawusz Pur Rahnama
c2b5e9449c Do not report TODO admonitions 2023-11-06 16:59:09 +01:00
Sijawusz Pur Rahnama
d5ac394d19 Switch only FIXME comment to TODO 2023-11-06 16:59:09 +01:00
Sijawusz Pur Rahnama
bdbb79f1fa Fix nonexistent method name used in error message 2023-11-06 16:59:09 +01:00
Sijawusz Pur Rahnama
1b342e8257 Make TODOFormatter's configuration file path configurable
Fixes the case where formatter specs were deleting project's `.ameba.yml` file
2023-11-06 16:59:09 +01:00
Sijawusz Pur Rahnama
23c61e04c0 Add Lint/DocumentationAdmonition rule 2023-11-06 16:59:09 +01:00
Sijawusz Pur Rahnama
ddb6e3c38f
Merge pull request #390 from crystal-ameba/refactor-rules-cli-switch
Refactor `--rules` CLI switch output + add `--describe <rule-name>` CLI switch
2023-11-05 06:44:55 +01:00
Sijawusz Pur Rahnama
ef16ad6471 Add presenter specs 2023-11-05 06:39:24 +01:00
Sijawusz Pur Rahnama
1b57e2cad5 Make Formatter::Util extend itself for easier access 2023-11-05 06:08:40 +01:00
Sijawusz Pur Rahnama
3d3626accc Introduced new presenter abstraction 2023-11-04 01:44:59 +01:00
Sijawusz Pur Rahnama
bede3f97a1 Colorize code in rule descriptions too 2023-11-04 00:49:11 +01:00
Sijawusz Pur Rahnama
8ff621ba66 Add --describe CLI switch 2023-11-04 00:49:11 +01:00
Sijawusz Pur Rahnama
f1f21ac94d Refactor --rules CLI switch output 2023-11-04 00:49:11 +01:00
Sijawusz Pur Rahnama
1718945523 Refactor ExplainFormatter a bit 2023-11-04 00:49:11 +01:00
Sijawusz Pur Rahnama
c9538220c6
Merge pull request #407 from crystal-ameba/crystal-next-compatibility
fix: crystal next compatibility
2023-10-09 23:47:32 +02:00
Vitalii Elenhaupt
789e1b77e8
fix: crystal next compatibility
refs https://github.com/crystal-lang/crystal/pull/11597
fixes https://github.com/crystal-ameba/ameba/issues/406
2023-10-06 18:57:39 +03:00
Sijawusz Pur Rahnama
7174e81a13
Merge pull request #401 from crystal-ameba/dependabot/github_actions/docker/login-action-3
Bump docker/login-action from 2 to 3
2023-09-12 23:41:13 +02:00
Sijawusz Pur Rahnama
29f84921b5
Merge pull request #402 from crystal-ameba/dependabot/github_actions/docker/build-push-action-5
Bump docker/build-push-action from 4 to 5
2023-09-12 23:41:02 +02:00
Sijawusz Pur Rahnama
c7f3fe78aa
Merge pull request #403 from crystal-ameba/dependabot/github_actions/docker/metadata-action-5
Bump docker/metadata-action from 4 to 5
2023-09-12 23:40:53 +02:00
Sijawusz Pur Rahnama
2d9db35ec4
Merge pull request #404 from crystal-ameba/dependabot/github_actions/docker/setup-qemu-action-3
Bump docker/setup-qemu-action from 2 to 3
2023-09-12 23:40:44 +02:00
Sijawusz Pur Rahnama
dfda3d7677
Merge pull request #405 from crystal-ameba/dependabot/github_actions/docker/setup-buildx-action-3
Bump docker/setup-buildx-action from 2 to 3
2023-09-12 23:40:34 +02:00
dependabot[bot]
0829f70256
Bump docker/setup-buildx-action from 2 to 3
Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 2 to 3.
- [Release notes](https://github.com/docker/setup-buildx-action/releases)
- [Commits](https://github.com/docker/setup-buildx-action/compare/v2...v3)

---
updated-dependencies:
- dependency-name: docker/setup-buildx-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-12 21:20:47 +00:00
dependabot[bot]
53b311c5eb
Bump docker/setup-qemu-action from 2 to 3
Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 2 to 3.
- [Release notes](https://github.com/docker/setup-qemu-action/releases)
- [Commits](https://github.com/docker/setup-qemu-action/compare/v2...v3)

---
updated-dependencies:
- dependency-name: docker/setup-qemu-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-12 21:20:44 +00:00
dependabot[bot]
867ddb4fbd
Bump docker/metadata-action from 4 to 5
Bumps [docker/metadata-action](https://github.com/docker/metadata-action) from 4 to 5.
- [Release notes](https://github.com/docker/metadata-action/releases)
- [Upgrade guide](https://github.com/docker/metadata-action/blob/master/UPGRADE.md)
- [Commits](https://github.com/docker/metadata-action/compare/v4...v5)

---
updated-dependencies:
- dependency-name: docker/metadata-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-12 21:20:41 +00:00
dependabot[bot]
6724f9a0e0
Bump docker/build-push-action from 4 to 5
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 4 to 5.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v4...v5)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-12 21:20:37 +00:00
dependabot[bot]
6389edc5fa
Bump docker/login-action from 2 to 3
Bumps [docker/login-action](https://github.com/docker/login-action) from 2 to 3.
- [Release notes](https://github.com/docker/login-action/releases)
- [Commits](https://github.com/docker/login-action/compare/v2...v3)

---
updated-dependencies:
- dependency-name: docker/login-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-12 21:20:34 +00:00
Vitalii Elenhaupt
0ab39a025b
Merge pull request #399 from crystal-ameba/dependabot/github_actions/actions/checkout-4
Bump actions/checkout from 3 to 4
2023-09-05 08:15:00 +03:00
dependabot[bot]
135ff87c7e
Bump actions/checkout from 3 to 4
Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-04 21:50:31 +00:00
Vitalii Elenhaupt
18d193bd08
Merge pull request #394 from stufro/388-raise-on-invalid-file-path
Raise error when passed invalid file paths
2023-08-10 13:08:00 +03:00
Stuart Frost
f96cb01015 Ensure test cleanup occurs 2023-08-05 19:28:58 +01:00
Stuart Frost
1b85ba6f22 Refactor: use unless instead of if not 2023-08-05 19:28:47 +01:00
Stuart Frost
eb60b25c4e Refactor building default globs 2023-08-05 16:15:50 +01:00
Stuart Frost
7690074cab Conditionally add !lib to default globs 2023-08-04 21:48:35 +01:00
Vitalii Elenhaupt
7b8316f061
Bump v1.5.0 2023-07-28 22:40:22 +03:00
Stuart Frost
b2069ea4ff Add extra test cases 2023-07-27 09:29:28 +01:00
Stuart Frost
e85531df6c Rename test fixture to source.cr 2023-07-27 09:24:26 +01:00
Stuart Frost
07aebfc84a
Merge branch 'master' into 388-raise-on-invalid-file-path 2023-07-26 15:22:04 +01:00
Vitalii Elenhaupt
8ef588dc6d
Merge pull request #393 from stufro/362-raise-on-invalid-config-file-path
Raise error when passed invalid config file path
2023-07-26 17:19:44 +03:00
Stuart Frost
3b9c442e09 Raise error when passed invalid file paths 2023-07-26 15:01:59 +01:00
Stuart Frost
88e0437902 Move fixture file to spec/fixtures directory 2023-07-25 10:00:16 +01:00
Stuart Frost
4741c9f4c4 Reword generic error message on config load 2023-07-25 08:46:13 +01:00
Stuart Frost
d9b2d69055 Reword error when file doesn't exist
Applied suggestion from PR

Co-authored-by: Vitalii Elenhaupt <3624712+veelenga@users.noreply.github.com>
2023-07-25 08:43:49 +01:00
Stuart Frost
5f878fb40f Move missing config file check into Ameba::Config 2023-07-24 19:10:52 +01:00
Stuart Frost
01a943d0d6 Raise error when passed invalid config file path 2023-07-24 15:30:38 +01:00
Sijawusz Pur Rahnama
8c9d234d0b
Merge pull request #391 from straight-shoota/feat/portability
Make postinstall portable
2023-07-16 20:42:37 +02:00
Johannes Müller
efa9c9dba0
Makefile: Remove run_file target 2023-07-15 22:47:14 +02:00
Johannes Müller
15ce5437d1
Make postinstall portable
Using `shards build` directly instead of `make build` improves portability a lot.
This should basically "just work" almost anywhere (including Windows). No need for `make` to be available and makefile compatibility doesn't matter either.
2023-07-15 10:13:53 +02:00
Johannes Müller
eacb9308a7
Utilize shards' executables
There's no need for copying the executables manually (which happens in both makefile targets `bin` and `run_file`).
Shards' `executables` takes care of that.

The makefile targets could potentially be dropped as well, I don't think there would be other uses case for those.
2023-07-15 10:13:53 +02:00
Sijawusz Pur Rahnama
a33f98624a
Merge pull request #376 from crystal-ameba/update-to-work-with-crystal-nightly 2023-07-11 23:50:14 +02:00
Sijawusz Pur Rahnama
33c8273866
Merge pull request #387 from crystal-ameba/feature/minmax-after-map-rule
Add `Performance/MinMaxAfterMap` rule
2023-07-10 16:17:53 +02:00
Sijawusz Pur Rahnama
327ed546b9
Apply suggestions from code review
Co-authored-by: Vitalii Elenhaupt <3624712+veelenga@users.noreply.github.com>
2023-07-10 16:09:01 +02:00
Sijawusz Pur Rahnama
ddff8d226b Add Performance/MinMaxAfterMap rule 2023-07-10 15:46:17 +02:00
Sijawusz Pur Rahnama
5cff76071a No need for such micro-optimizations, LLVM takes care of those 2023-07-02 14:34:25 +02:00
Sijawusz Pur Rahnama
29e29b8e1d Fix Performance/ExcessiveAllocations to exclude each calls without a block 2023-06-30 21:44:47 +02:00
Sijawusz Pur Rahnama
21051acfff
Merge pull request #386 from crystal-ameba/fix-issue-385 2023-06-30 20:58:56 +02:00
Sijawusz Pur Rahnama
abe5237802 Add Performance/ExcessiveAllocations rule 2023-06-30 15:17:40 +02:00
Sijawusz Pur Rahnama
b7b21ffeb0
Merge pull request #384 from crystal-ameba/fix-issue-383
Fix `Style/VerboseBlock` rule to work with binary operations
2023-06-29 10:20:13 +02:00
Sijawusz Pur Rahnama
4d0125a0f3 Fix Style/VerboseBlock rule to work with binary operations 2023-06-29 08:15:39 +02:00
Sijawusz Pur Rahnama
e1f5c81804
Merge pull request #379 from crystal-ameba/several-tweaks-and-refactors
Several tweaks and refactors
2023-06-14 16:54:27 +02:00
Sijawusz Pur Rahnama
16141a376e Cleanup properties definition macro 2023-06-14 15:08:19 +02:00
Sijawusz Pur Rahnama
596b0dd9d0 Misc tweaks and refactors 2023-06-14 15:06:24 +02:00
Sijawusz Pur Rahnama
b4244d4c61
Merge pull request #378 from crystal-ameba/node-visitor-category 2023-06-13 11:36:05 +02:00
Sijawusz Pur Rahnama
1931a5f4ef Make skip a named argument 2023-06-12 23:17:14 +02:00
Sijawusz Pur Rahnama
c09b36799a Make AST::NodeVisitor::Category a flag enum 2023-06-12 23:17:14 +02:00
Sijawusz Pur Rahnama
38b6751bc0 Add AST::NodeVisitor::Category simplifying code a bit 2023-06-12 23:17:14 +02:00
Sijawusz Pur Rahnama
db59b23f9b Fix specs against Crystal nightly 2023-06-10 01:11:21 +02:00
Sijawusz Pur Rahnama
9a8538aa69
Update README.md 2023-06-09 09:57:26 +02:00
Sijawusz Pur Rahnama
aceb054aa0
Merge pull request #374 from crystal-ameba/lint-documentation-rule
Add `Lint/Documentation` rule
2023-06-08 14:10:21 +02:00
Sijawusz Pur Rahnama
b156a6a6a1 Add comments to macros 2023-06-08 14:03:35 +02:00
Sijawusz Pur Rahnama
94e31d4685 Do the same in NodeVisitor 2023-06-08 04:28:37 +02:00
Sijawusz Pur Rahnama
e12d72cc88 Set the ASTNode#visibility as well 2023-06-08 04:28:17 +02:00
Sijawusz Pur Rahnama
262e31c35b Cleanup README mention (MT is enabled by default) 2023-06-08 02:26:39 +02:00
Sijawusz Pur Rahnama
c9d25f3409 Do not run disabled rules against the codebase 2023-06-08 02:24:12 +02:00
Sijawusz Pur Rahnama
7caa47fb6a Several small refactors 2023-06-08 02:04:30 +02:00
Sijawusz Pur Rahnama
4d8346509e Implement Documentation rule on top of the ScopeVisitor 2023-06-08 02:04:06 +02:00
Sijawusz Pur Rahnama
4c740f394a Implement Scope#visibility 2023-06-08 01:58:58 +02:00
Sijawusz Pur Rahnama
85c3db4d74 Move NODES constant into its proper namespace 2023-05-31 13:15:01 +02:00
Sijawusz Pur Rahnama
6e5a9a60b3 Refactor Lint::Documentation and ignore macro hooks 2023-05-29 17:09:39 +02:00
Sijawusz Pur Rahnama
09fdac6be9 Refactor Lint::Documentation rule to use a custom visitor 2023-05-29 16:33:45 +02:00
Sijawusz Pur Rahnama
1a9a58b3cd Add Lint/Documentation rule 2023-05-29 16:32:51 +02:00
Sijawusz Pur Rahnama
60948fffd0
Merge pull request #373 from crystal-ameba/fix-372
Raise when empty severity provided to `SeverityYamlConverter.from_yaml`
2023-05-09 14:28:17 +02:00
Sijawusz Pur Rahnama
d0d8b18c83
Raise when empty severity provided to SeverityYamlConverter.from_yaml 2023-05-09 13:02:10 +02:00
Sijawusz Pur Rahnama
14f6ba0c0b
Merge pull request #323 from crystal-ameba/Sija/lint-not-nil-after-no-bang-reports-rindex-calls
Make `Lint/NotNilAfterNoBang` report calls to `#rindex`
2023-05-01 11:16:42 +02:00
Vitalii Elenhaupt
149080ae16
Merge pull request #369 from daliborfilus/fix-crystal-1.8-pcre2-docker-build
Update Dockerfile for Crystal 1.8 + check if binary works
2023-04-24 09:46:09 +03:00
Dalibor Filus
454a747a68
Update Dockerfile for Crystal 1.8 + check if binary works
Crystal 1.8.0+ require pcre2.

Let's also add `RUN ameba -v` to check if the binary actually works.
Fixes cases like this:

```shell
$ docker run --rm -ti ameba:latest sh
Error loading shared library libpcre2-8.so.0: No such file or directory (needed by /usr/bin/ameba)
Error relocating /usr/bin/ameba: pcre2_get_ovector_count_8: symbol not found
Error relocating /usr/bin/ameba: pcre2_config_8: symbol not found
Error relocating /usr/bin/ameba: pcre2_get_ovector_pointer_8: symbol not found
Error relocating /usr/bin/ameba: pcre2_match_data_create_from_pattern_8: symbol not found
Error relocating /usr/bin/ameba: pcre2_jit_stack_assign_8: symbol not found
Error relocating /usr/bin/ameba: pcre2_code_free_8: symbol not found
Error relocating /usr/bin/ameba: pcre2_match_context_create_8: symbol not found
Error relocating /usr/bin/ameba: pcre2_jit_compile_8: symbol not found
Error relocating /usr/bin/ameba: pcre2_compile_8: symbol not found
Error relocating /usr/bin/ameba: pcre2_get_error_message_8: symbol not found
Error relocating /usr/bin/ameba: pcre2_jit_stack_create_8: symbol not found
Error relocating /usr/bin/ameba: pcre2_match_8: symbol not found
Error relocating /usr/bin/ameba: pcre2_match_data_free_8: symbol not found
Error relocating /usr/bin/ameba: pcre2_pattern_info_8: symbol not found
```
2023-04-22 11:41:49 +02:00
Vitalii Elenhaupt
ef2d05e48a
Merge pull request #366 from straight-shoota/fix/severity-spec-yaml-converter
Fix severity type for YAML converter spec
2023-04-05 17:35:19 +03:00
Johannes Müller
c7f2cba409
Fix severity type for YAML converter spec 2023-04-05 11:20:44 +02:00
Vitalii Elenhaupt
7c74d196d6
Bump v1.4.3 2023-03-17 10:24:44 +02:00
Vitalii Elenhaupt
adac90c7c6
Merge pull request #363 from zw963/master
Skip all config when use with --gen-config.
2023-03-09 08:34:47 +02:00
Billy.Zheng
239f64c278 Refactor 2023-03-09 12:41:40 +08:00
Billy.Zheng
e2528d93dd Refactor spec to use be_false replace eq false 2023-03-09 12:39:25 +08:00
Billy.Zheng
48c7a2bde6 Add spec for verifying skip_reading_config? flag defaults to false. 2023-03-09 02:23:48 +08:00
Billy.Zheng
d45285d1c9 Skip all config when use with --gen-config. 2023-03-08 22:34:46 +08:00
Sijawusz Pur Rahnama
3fbbe3986e
Merge pull request #361 from crystal-ameba/issue-275
Support hierarchical loading of the config file
2023-03-07 11:11:35 +01:00
Sijawusz Pur Rahnama
4c59858f25 Apply code review suggestions 2023-03-06 21:22:47 +01:00
Sijawusz Pur Rahnama
8d56f22af1 Document the new Config.load behaviour 2023-03-06 21:19:47 +01:00
Sijawusz Pur Rahnama
e481a8d139 Use FILENAME constant consistently 2023-03-06 20:54:35 +01:00
Sijawusz Pur Rahnama
102e2834b6 Honor XDG_CONFIG_HOME ENV variable
Following XDG Spec: https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
2023-03-06 20:54:35 +01:00
Sijawusz Pur Rahnama
749da0984c Rename Config::PATH to Config::DEFAULT_PATH 2023-03-06 20:54:35 +01:00
Sijawusz Pur Rahnama
9534104942 Support hierarchical loading of the config file 2023-03-06 20:54:17 +01:00
Vitalii Elenhaupt
6f05df4006
Bump v1.4.2 2023-02-22 09:40:17 +02:00
Vitalii Elenhaupt
81177dc7e4
Merge pull request #356 from crystal-ameba/chore/crystal-nightly
chore: fix crystal-nightly compatibility
2023-02-19 18:06:31 +02:00
Vitalii Elenhaupt
d03f058cae
Add semicolon to the error message 2023-02-19 17:39:25 +02:00
Vitalii Elenhaupt
be8862837f
Improve error message reported to end user 2023-02-19 14:45:07 +02:00
Vitalii Elenhaupt
63a54986dd Reformat code example 2023-02-19 13:24:54 +01:00
Vitalii Elenhaupt
801affc58b
Merge pull request #357 from crystal-ameba/fix/comments
fix(style): correct typo in rule doc
2023-02-19 13:53:49 +02:00
Vitalii Elenhaupt
c9c5fb655f
fix(style): correct typo in rule doc 2023-02-19 13:47:17 +02:00
Vitalii Elenhaupt
6717ac7b70
Merge pull request #355 from crystal-ameba/fix/unused_block_arg
fix(lint): Lint/UnusedBlockArgument is triggered by abstract def
2023-02-19 13:46:45 +02:00
Vitalii Elenhaupt
17e9566c7e
fix(lint): Lint/UnusedBlockArgument is triggered by abstract def
closes #352
2023-02-19 10:51:52 +02:00
Vitalii Elenhaupt
031c1daf58
chore: fix crystal-nightly compatibility 2023-02-19 09:30:05 +02:00
Vitalii Elenhaupt
a80672730c
Bump v1.4.1 2023-02-19 08:52:48 +02:00
Vitalii Elenhaupt
8f5d2cca6e
Merge pull request #351 from crystal-ameba/fix/useless-assign-type-def
fix(lint): useless assignment for type definition
2023-02-19 08:50:43 +02:00
Vitalii Elenhaupt
e7f4bb6ae3
Fix wording 2023-02-07 22:24:52 +02:00
Vitalii Elenhaupt
61a45d4321
Rework tests 2023-02-07 22:21:09 +02:00
Vitalii Elenhaupt
d20cc212b9
Styling changes 2023-02-07 20:03:52 +02:00
Vitalii Elenhaupt
6b2ddcb1d9
Address feedback, add tests 2023-02-07 17:19:04 +02:00
Vitalii Elenhaupt
14a9ec3a75
Incorporate changes for shadowing outer local var 2023-02-04 20:53:41 +02:00
Vitalii Elenhaupt
ddbcf5cb3f
fix(lint): useless assignment for type definition
closes #342
2023-02-04 16:57:46 +02:00
Sijawusz Pur Rahnama
de5c6ad4c6
Merge pull request #350 from crystal-ameba/dependabot/github_actions/docker/build-push-action-4 2023-01-30 22:40:28 +01:00
dependabot[bot]
00e39f6c41
Bump docker/build-push-action from 3 to 4
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 3 to 4.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v3...v4)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-30 21:36:27 +00:00
Sijawusz Pur Rahnama
bd68e8c3b3
Merge pull request #349 from crystal-ameba/Sija-patch-2
Remove SWUbanner
2023-01-28 12:18:48 +01:00
Sijawusz Pur Rahnama
8fbaf302ca
Remove SWUbanner 2023-01-28 12:14:19 +01:00
Sijawusz Pur Rahnama
2c044f8179
Merge pull request #348 from crystal-ameba/Sija-patch-2
Remove Travis CI references from `README.md`
2023-01-28 11:37:58 +01:00
Sijawusz Pur Rahnama
0efaed9091
Remove Travis CI references in README.md 2023-01-28 11:05:18 +01:00
Vitalii Elenhaupt
7be5af216e
Merge pull request #346 from crystal-ameba/remove-gitter
Remove gitter badge
2023-01-28 11:49:24 +02:00
Vitalii Elenhaupt
ff79cefaa8
Remove gitter badge 2023-01-28 09:35:55 +02:00
Vitalii Elenhaupt
7d0fcd4b87
Merge pull request #333 from crystal-ameba/Sija/release-1.4.0
Release 1.4.0
2023-01-28 09:32:50 +02:00
Vitalii Elenhaupt
a88299fa5c
Bump crystal 1.7 2023-01-23 19:35:03 +02:00
Vitalii Elenhaupt
74407cc8f5
Merge branch 'master' into Sija/release-1.4.0 2023-01-22 18:05:21 +02:00
Sijawusz Pur Rahnama
ce4dd7236a Make Lint/NotNilAfterNoBang report calls to #rindex 2023-01-10 13:37:28 +01:00
Sijawusz Pur Rahnama
e58f90009f
Merge pull request #334 from crystal-ameba/Sija/crystal-1.7.0-fixes
Fix specs on Crystal nightly (soon to be v1.7.0)
2023-01-10 13:23:18 +01:00
Sijawusz Pur Rahnama
3b79392ef2 Simplify implementation of Lint/EmptyExpression rule 2023-01-10 13:10:05 +01:00
Sijawusz Pur Rahnama
8e86374d08 Remove outdated spec 2023-01-10 13:10:05 +01:00
Sijawusz Pur Rahnama
d31e41b8a7 Fix specs on Crystal nightly (soon to be v1.7.0) 2023-01-10 13:10:05 +01:00
Sijawusz Pur Rahnama
a091af14a8
Merge pull request #341 from crystal-ameba/Sija/remove-autocorrections-from-any-instead-of-empty 2023-01-04 11:19:51 +01:00
Sijawusz Pur Rahnama
6f0b6ffcd0 Remove buggy auto-correction from Performance/AnyInsteadOfEmpty rule 2023-01-04 02:40:07 +01:00
Sijawusz Pur Rahnama
f7cb5bb563
Merge pull request #340 from crystal-ameba/Sija/fix-ci-docs-deploy 2023-01-03 19:23:34 +01:00
Sijawusz Pur Rahnama
1514fc53ca There’s no need to strip the HTML from .md docs anymoar 2023-01-03 17:04:37 +01:00
Sijawusz Pur Rahnama
853f84d954 Fix docs CI workflow 2023-01-03 16:36:33 +01:00
Sijawusz Pur Rahnama
c5748aec71
Merge pull request #339 from crystal-ameba/Sija/tweak-ci 2023-01-03 12:54:36 +01:00
Sijawusz Pur Rahnama
1ebc1a68c6 Refactor CI workflow to clearly show failed steps 2023-01-02 19:39:43 +01:00
Sijawusz Pur Rahnama
89f995f211
Merge pull request #338 from crystal-ameba/Sija/build-and-push-docker-images-to-ghcr
Build and push Docker images to GHCR
2023-01-02 19:38:04 +01:00
Sijawusz Pur Rahnama
e9226c05d5 Cleanup Makefile 2023-01-02 19:08:31 +01:00
Sijawusz Pur Rahnama
01ab89b348 Tweak CI settings 2023-01-02 19:08:27 +01:00
Sijawusz Pur Rahnama
1a60cc3c18 Update README.md in re: to the GHCR 2023-01-02 18:25:50 +01:00
Sijawusz Pur Rahnama
34620a986d Add GitHub Actions workflow to build and push Docker images to GHCR 2023-01-02 18:25:50 +01:00
Sijawusz Pur Rahnama
d5fbb07c3a
Merge pull request #337 from crystal-ameba/Sija/tweak-reported-locations
Tweak reported rule locations
2022-12-24 02:10:58 +01:00
Sijawusz Pur Rahnama
caaf803ecd Tweak reported location for Style/UnlessElse 2022-12-23 14:46:45 +01:00
Sijawusz Pur Rahnama
bbbfdfc5a2 Tweak reported location for Lint/UnusedBlockArgument 2022-12-23 14:46:23 +01:00
Sijawusz Pur Rahnama
2d9e328d97
Merge pull request #325 from FnControlOption/unless_else
Add autocorrect for `Style/UnlessElse`
2022-12-22 21:43:49 +01:00
Sijawusz Pur Rahnama
2cedd72b09 Apply review sugggestions 2022-12-22 19:18:27 +01:00
Sijawusz Pur Rahnama
4f8b79ec6b
Merge pull request #324 from crystal-ameba/Sija/extend-several-rule-with-corrections
Extend `Lint/UnusedArgument` and `Lint/UnusedBlockArgument` rules with corrections
2022-12-22 18:42:35 +01:00
Sijawusz Pur Rahnama
3586b4242f Extend Lint/UnusedBlockArgument rule with corrections 2022-12-22 17:15:46 +01:00
Sijawusz Pur Rahnama
f26cd7f823 Extend Lint/UnusedArgument rule with corrections 2022-12-22 17:15:46 +01:00
Sijawusz Pur Rahnama
f1b270dfbf
Merge pull request #335 from crystal-ameba/Sija/fix-ci-docs 2022-12-22 14:04:36 +01:00
Sijawusz Pur Rahnama
ea4439d6e2 Fix regression in docs CI 2022-12-22 13:45:27 +01:00
Sijawusz Pur Rahnama
c02f7fdc33
Merge pull request #332 from crystal-ameba/Sija/update-ci-definitions 2022-12-22 13:38:37 +01:00
Sijawusz Pur Rahnama
b9bc5aaab2
Merge pull request #327 from crystal-ameba/Sija/specs-cleanup 2022-12-22 12:08:44 +01:00
Sijawusz Pur Rahnama
232c16d743 Add spec for 10000 Int128 literal in Style/LargeNumbers 2022-12-21 21:15:17 +01:00
Sijawusz Pur Rahnama
cfea0fda34 Fix typo 2022-12-21 21:14:29 +01:00
Sijawusz Pur Rahnama
9926f0295a Remove most of the obsolete specs 2022-12-21 21:13:22 +01:00
Sijawusz Pur Rahnama
1ba6fcb55c Enhance spec helpers a bit in re: to the spec context 2022-12-21 17:34:17 +01:00
Sijawusz Pur Rahnama
e9f3bbaeff Fail with the additional context 2022-12-21 17:34:10 +01:00
Sijawusz Pur Rahnama
7c617b5a7b Make spec helper methods private 2022-12-21 17:34:05 +01:00
Sijawusz Pur Rahnama
5ff8d2593a Add Dependabot config 2022-12-21 16:29:13 +01:00
Sijawusz Pur Rahnama
e76de37b7d Update GitHub Actions 2022-12-21 16:08:16 +01:00
Sijawusz Pur Rahnama
2cfbe97b6d
Merge pull request #328 from crystal-ameba/Sija/remove-openssl-from-docker-image 2022-12-20 17:49:17 +01:00
Sijawusz Pur Rahnama
6aa36f3d9e Remove OpenSSL from Docker image 2022-12-20 17:09:28 +01:00
Sijawusz Pur Rahnama
206b5ab604 Refactor: Source#correct -> Source#correct? 2022-12-20 17:04:34 +01:00
Sijawusz Pur Rahnama
1af70749b8 Bump to v1.4.0 2022-12-20 17:03:08 +01:00
Sijawusz Pur Rahnama
cabf203e0d
Merge pull request #326 from crystal-ameba/Sija/lint-formatting 2022-12-20 16:20:48 +01:00
Sijawusz Pur Rahnama
b78e2aebc5
Merge pull request #321 from crystal-ameba/Sija/lint-missing-block-argument-rule 2022-12-20 16:20:06 +01:00
Sijawusz Pur Rahnama
47b92fbb76 Fix newly found issues 2022-12-20 15:31:14 +01:00
Sijawusz Pur Rahnama
597372c645 Add Lint/MissingBlockArgument rule 2022-12-20 15:31:14 +01:00
Sijawusz Pur Rahnama
d7154ad6d2 Fix failing spec 2022-12-20 04:10:56 +01:00
Sijawusz Pur Rahnama
3bc8bda008 Add Lint/Formatting rule 2022-12-20 04:10:56 +01:00
Sijawusz Pur Rahnama
4e3caf2986 Few tweaks and readability refactors 2022-12-20 03:26:13 +01:00
Sijawusz Pur Rahnama
ab059616b5 Switch from Hash to NamedTuple for AmbiguousAssignment::MISTAKES 2022-12-20 03:25:14 +01:00
Sijawusz Pur Rahnama
70078cf77f Synchronize properties context names in rules’ specs 2022-12-20 03:25:10 +01:00
Sijawusz Pur Rahnama
8112dddc8f Specs cleanup 2022-12-20 03:25:06 +01:00
fn ⌃ ⌥
d7795c0d7d Add autocorrect for Style/UnlessElse 2022-12-19 06:40:28 -08:00
Sijawusz Pur Rahnama
e6ebca7a5b
Merge pull request #320 from crystal-ameba/Sija/unused-block-argument
Add `Lint/UnusedBlockArgument` rule
2022-12-15 19:33:29 +01:00
Sijawusz Pur Rahnama
bb0c0eeec6 Consider previous_def implicitly accessing variables, in the same way as super does 2022-12-15 18:35:16 +01:00
Sijawusz Pur Rahnama
b6f3d41211 Small refactor 2022-12-15 18:35:16 +01:00
Sijawusz Pur Rahnama
7f6bd2289e Do not report unused block arguments in Lint/UnusedArgument rule 2022-12-15 18:35:16 +01:00
Sijawusz Pur Rahnama
858557bc07 Add Lint/UnusedBlockArgument rule 2022-12-15 18:35:16 +01:00
Sijawusz Pur Rahnama
6ffb635dcc Add Scope#yields? 2022-12-15 18:35:16 +01:00
Sijawusz Pur Rahnama
4b1378aa33
Merge pull request #319 from crystal-ameba/Sija/fix-unused-argument-with-anonymous-block-arg 2022-12-12 19:53:18 +01:00
Sijawusz Pur Rahnama
9df66e890b Do not report anonymous block arguments in Lint/UnusedArgument 2022-12-12 18:21:19 +01:00
Sijawusz Pur Rahnama
4533e52aa5
Merge pull request #317 from crystal-ameba/Sija/further-refinements 2022-12-11 13:25:07 +01:00
Sijawusz Pur Rahnama
4d9cd4d56c Fix typo 2022-12-10 00:24:04 +01:00
Sijawusz Pur Rahnama
a8fca61b22 Doc tweaks 2022-12-10 00:20:20 +01:00
Sijawusz Pur Rahnama
99c81d3630 Add spec for positive case in macro scope in Lint/LiteralsComparison rule 2022-12-09 23:53:39 +01:00
Sijawusz Pur Rahnama
2dc21a00d9 Remove treating paths as literals in AST::Util#literal? & friends 2022-12-08 17:47:22 +01:00
Sijawusz Pur Rahnama
0bd4ed0c45 Do not treat paths as literals in Lint/LiteralAssignmentsInExpressions 2022-12-08 17:47:22 +01:00
Sijawusz Pur Rahnama
1261f4ba6d Tweak README.md 2022-12-08 17:47:22 +01:00
Sijawusz Pur Rahnama
db3ef762b9 Uncomment forgotten line 2022-12-08 17:47:22 +01:00
Sijawusz Pur Rahnama
2fb453c61f Add additional spec for Lint/NotNil rule 2022-12-08 17:47:22 +01:00
Sijawusz Pur Rahnama
07ce595ef2 Readability refactors 2022-12-08 17:47:22 +01:00
Sijawusz Pur Rahnama
784e3ac616 Use bang variants in couple of places more 2022-12-08 13:59:42 +01:00
Sijawusz Pur Rahnama
5ab4b05add Fix incorrect Reportable#add_issue(&) definition 2022-12-08 13:59:03 +01:00
Sijawusz Pur Rahnama
7d88455b7f Add corrector to the Style/IsANil rule 2022-12-08 13:58:35 +01:00
Sijawusz Pur Rahnama
071a8b7afb Consistently use CRYSTAL marker for heredocs with crystal code 2022-12-08 13:57:46 +01:00
Sijawusz Pur Rahnama
2cb8c1381f Don’t treat crystal paths as literals in Lint/LiteralsComparison 2022-12-08 13:55:29 +01:00
Sijawusz Pur Rahnama
150ba6c70f
Merge pull request #314 from crystal-ameba/Sija/style-query-bool-methods-rule 2022-12-08 13:14:19 +01:00
Sijawusz Pur Rahnama
e18da84ede Add requested spec 2022-12-08 02:16:16 +01:00
Sijawusz Pur Rahnama
cdf0405496 Refactor Style/IsANil rule to use newly added path_named? helper 2022-12-08 02:16:16 +01:00
Sijawusz Pur Rahnama
f45d6a2ef0 Fix newly found issue 2022-12-08 02:16:16 +01:00
Sijawusz Pur Rahnama
1524aad299 Add Style/QueryBoolMethods rule 2022-12-08 02:16:16 +01:00
Sijawusz Pur Rahnama
1818bcfd27
Merge pull request #315 from crystal-ameba/refresh-readme 2022-12-02 16:03:59 +01:00
Vitalii Elenhaupt
4ec829e83d
Refresh readme
- [x] update running example
- [x] add Autocorrection section to Usage
- [x] move Explain issues section from Configuration to Usage
2022-12-02 09:24:15 +02:00
Sijawusz Pur Rahnama
be65ba2a92
Merge pull request #312 from crystal-ameba/Sija/refactors-round-3 2022-11-29 19:12:16 +01:00
Sijawusz Pur Rahnama
4dd62a3ed1 Colorize also the code in single backticks 2022-11-28 11:49:26 +01:00
Sijawusz Pur Rahnama
2113e8c055 Refactor MacroReferenceFinder class a bit 2022-11-28 11:38:41 +01:00
Sijawusz Pur Rahnama
a8f953a2b2 Compress identical method definitions 2022-11-28 11:38:41 +01:00
Sijawusz Pur Rahnama
b79f3e6e07 Minor refactors in Style/LargeNumbers rule 2022-11-28 11:38:41 +01:00
Sijawusz Pur Rahnama
134963ece7 Minor details 2022-11-28 11:38:41 +01:00
Sijawusz Pur Rahnama
523a622b34 Remove leftover from some merge/rebase gone wrong 2022-11-28 11:38:41 +01:00
Sijawusz Pur Rahnama
5502f0f8d1 Small refactor using Object#in? 2022-11-28 11:38:41 +01:00
Sijawusz Pur Rahnama
748cab29b4 Remove redundant empty comment lines 2022-11-28 11:38:41 +01:00
Sijawusz Pur Rahnama
629e65127d if … == nil -> unless … 2022-11-28 11:38:41 +01:00
Sijawusz Pur Rahnama
adff510cb6 Autocasting will do the work 2022-11-28 11:38:41 +01:00
Sijawusz Pur Rahnama
33ef9c6293 Refactor ExplainFormatter 2022-11-28 11:38:41 +01:00
Sijawusz Pur Rahnama
f9b05a309e Redundant whitespace before nl 2022-11-28 11:25:21 +01:00
Sijawusz Pur Rahnama
0f893971dc Avoid one-letter names if possible 2022-11-28 11:25:21 +01:00
Sijawusz Pur Rahnama
20935ae381
Merge pull request #311 from crystal-ameba/Sija/add-severity-color
Add `Severity#color`
2022-11-28 11:24:31 +01:00
Sijawusz Pur Rahnama
d19d3b78c4 Implement Severity#color property 2022-11-28 06:07:19 +01:00
Sijawusz Pur Rahnama
f89e7c2d3c
Merge pull request #310 from crystal-ameba/Sija/refactor-rule-default-group-severity 2022-11-24 15:34:09 +01:00
Sijawusz Pur Rahnama
6a180757f3 Use Enumerable#zip ftw 2022-11-23 16:16:34 +01:00
Sijawusz Pur Rahnama
1399aa3cdf Doc fixes 2022-11-23 16:16:03 +01:00
Sijawusz Pur Rahnama
75482a06cf Use warning as a default severity for Metrics and Performance groups 2022-11-23 15:06:24 +01:00
Sijawusz Pur Rahnama
3b7cd3723c Refactor rules’ default group severity handling 2022-11-23 15:04:47 +01:00
Sijawusz Pur Rahnama
e7a6b6b153
Merge pull request #309 from crystal-ameba/Sija/refactor-base-rule 2022-11-23 13:50:45 +01:00
Sijawusz Pur Rahnama
735ec2462a Refactor usages of \ to sth more readable if possible 2022-11-23 05:50:32 +01:00
Sijawusz Pur Rahnama
38eb5d5e50 Fix invalid crystal syntax in Lint/Syntax rule spec 2022-11-23 03:26:55 +01:00
Sijawusz Pur Rahnama
2c67fe2c3f Use tuple instead of an array 2022-11-23 03:23:42 +01:00
Sijawusz Pur Rahnama
8b43a40a65 Misc refactors 2022-11-23 03:22:27 +01:00
Sijawusz Pur Rahnama
ffd63ef028 Fix typo in AST::Branchable example 2022-11-23 03:21:49 +01:00
Sijawusz Pur Rahnama
4500181ddb Remove redundant blank comment lines 2022-11-23 03:21:15 +01:00
Sijawusz Pur Rahnama
60813e4899 Test generated boolean rule property 2022-11-22 19:53:27 +01:00
Sijawusz Pur Rahnama
9f670c09b5 Refactor TODOFormatter spec 2022-11-22 19:52:32 +01:00
Sijawusz Pur Rahnama
2af58cabd4 Use property? for defining Bool-returning Rule properties 2022-11-22 19:49:16 +01:00
Sijawusz Pur Rahnama
c7c75ee36a Rule#enabled -> Rule#enabled? 2022-11-22 19:47:37 +01:00
Sijawusz Pur Rahnama
31392046e0 Always return Bool value from Rule#excluded? 2022-11-22 19:45:16 +01:00
Sijawusz Pur Rahnama
39cc286263 Use tuple instead of array for Rule::SPECIAL 2022-11-22 19:44:38 +01:00
Sijawusz Pur Rahnama
6bd18f9cbf Fix typo 2022-11-22 19:43:52 +01:00
Sijawusz Pur Rahnama
a3e5f2d206
Merge pull request #302 from crystal-ameba/Sija/style-parenthesized-assignments 2022-11-20 00:39:58 +01:00
Sijawusz Pur Rahnama
5ee4074c1b Rename RedundantParentheses -> ParenthesesAroundCondition
Also rename the option: `parenthesized_assignments` -> `allow_safe_assignment`
2022-11-18 21:06:51 +01:00
Sijawusz Pur Rahnama
0b0a815c31 Add additional test case 2022-11-18 07:04:51 +01:00
Sijawusz Pur Rahnama
eabe463386 Instead of adding the new rule to support enforcing parens around assignments, refactor existing RedundantParentheses rule 2022-11-18 05:27:05 +01:00
Sijawusz Pur Rahnama
94a271b2a1 Add Style/ParenthesizedAssignments rule 2022-11-17 21:32:47 +01:00
Sijawusz Pur Rahnama
496e8930e2
Merge pull request #303 from crystal-ameba/Sija/lint-literal-assignments-in-expressions-rule
Add `Lint/LiteralAssignmentsInExpressions` rule
2022-11-17 14:19:17 +01:00
Sijawusz Pur Rahnama
243071700d
Merge pull request #306 from crystal-ameba/Sija/fix-typos
Fix typos throughout the code
2022-11-16 18:42:22 +01:00
Sijawusz Pur Rahnama
95d68114c7 Fix typos throughout the code 2022-11-16 16:55:32 +01:00
Sijawusz Pur Rahnama
f9b6b17657
Merge pull request #305 from crystal-ameba/Sija/followup-to-pr-300
Few more readability refactors
2022-11-16 16:26:59 +01:00
Sijawusz Pur Rahnama
5c08b64e72 Add Lint/LiteralAssignmentsInExpressions rule 2022-11-16 15:08:41 +01:00
Sijawusz Pur Rahnama
28e2871165
Merge pull request #301 from crystal-ameba/Sija/style-redundant-parentheses-rule
Add `Style/RedundantParentheses` rule
2022-11-16 12:22:39 +01:00
Sijawusz Pur Rahnama
b5ac5990ec
Merge pull request #304 from crystal-ameba/Sija/refactor-ast-util-literal 2022-11-16 11:01:03 +01:00
Sijawusz Pur Rahnama
935296b041 Refactor ternary handling in Style/RedundantParentheses rule 2022-11-16 03:54:31 +01:00
Sijawusz Pur Rahnama
e54029d8ed Few more readability refactors 2022-11-15 20:55:32 +01:00
Sijawusz Pur Rahnama
76a4209706 Tweak comments in AST::Util 2022-11-15 17:42:08 +01:00
Sijawusz Pur Rahnama
2fb37da80f Refactor AST::Util#literal? helper 2022-11-15 17:42:08 +01:00
Sijawusz Pur Rahnama
3340613a6a Fix newly found issue 2022-11-15 15:17:32 +01:00
Sijawusz Pur Rahnama
cbf5d3de74 Add Style/RedundantParentheses rule 2022-11-15 15:17:32 +01:00
225 changed files with 5725 additions and 2670 deletions

7
.ameba.yml Normal file
View file

@ -0,0 +1,7 @@
Documentation/DocumentationAdmonition:
Timezone: UTC
Admonitions: [FIXME, BUG]
Lint/Typos:
Excluded:
- spec/ameba/rule/lint/typos_spec.cr

7
.github/dependabot.yml vendored Normal file
View file

@ -0,0 +1,7 @@
version: 2
updates:
# Maintain dependencies for GitHub Actions
- package-ecosystem: github-actions
directory: /
schedule:
interval: daily

74
.github/workflows/cd.yml vendored Normal file
View file

@ -0,0 +1,74 @@
name: Docker image build and deploy
on:
push:
branches: [master]
release:
types: [published]
env:
# Use docker.io for Docker Hub if empty
REGISTRY: ghcr.io
# github.repository as <account>/<repo>
IMAGE_NAME: ${{ github.repository }}
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
# This is used to complete the identity challenge
# with sigstore/fulcio when running outside of PRs.
id-token: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Setup Docker Buildx
uses: docker/setup-buildx-action@v3
# Login against a Docker registry except on PR
# https://github.com/docker/login-action
- name: Log into ${{ env.REGISTRY }} registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
# Extract metadata (tags, labels) for Docker
# https://github.com/docker/metadata-action
- name: Extract Docker metadata
id: meta
uses: docker/metadata-action@v5
with:
images: |
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=tag
type=ref,event=branch
type=ref,event=pr
type=sha,format=long
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
# Build and push Docker image with Buildx
# https://github.com/docker/build-push-action
- name: Build and push Docker image
id: build-and-push
uses: docker/build-push-action@v6
with:
context: .
push: true
cache-from: type=gha
cache-to: type=gha,mode=max
platforms: |
linux/amd64
linux/arm64
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}

View file

@ -2,7 +2,9 @@ name: CI
on:
push:
branches: [master]
pull_request:
types: [opened, synchronize, reopened]
schedule:
- cron: "0 3 * * 1" # Every monday at 3 AM
@ -16,21 +18,29 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- name: Set timezone to UTC
uses: szenius/set-timezone@v2.0
- name: Install Crystal
uses: oprypin/install-crystal@v1
uses: crystal-lang/install-crystal@v1
with:
crystal: ${{ matrix.crystal }}
- name: Download source
uses: actions/checkout@v2
uses: actions/checkout@v4
- name: Install dependencies
run: shards install
env:
SHARDS_OPTS: --ignore-crystal-version
- name: Install typos-cli
if: matrix.os == 'macos-latest'
run: brew install typos-cli
- name: Run specs
run: make test
run: crystal spec
- name: Check formatting
run: crystal tool format --check
- name: Build ameba binary
run: shards build -Dpreview_mt
- name: Run ameba linter
run: bin/ameba

View file

@ -4,51 +4,32 @@ on:
push:
branches: [master]
permissions:
contents: write
jobs:
build:
build-and-deploy:
concurrency: ci-${{ github.ref }}
runs-on: ubuntu-latest
steps:
- name: Inject slug/short variables
uses: rlespinasse/github-slug-action@v3.x
uses: rlespinasse/github-slug-action@v4
- name: Install Crystal
uses: oprypin/install-crystal@v1
uses: crystal-lang/install-crystal@v1
- name: Download source
uses: actions/checkout@v2
with:
persist-credentials: false
uses: actions/checkout@v4
- name: Install dependencies
run: shards install
env:
SHARDS_OPTS: --ignore-crystal-version
- name: Build docs
run: |
sed -i -e 's:<.*>::g' README.md
crystal docs --project-version="${{ env.GITHUB_REF_SLUG }}" --source-refname="${{ env.GITHUB_SHA_SHORT }}"
- name: Upload artifacts
uses: actions/upload-artifact@v2
with:
name: docs
path: docs
deploy:
needs: build
runs-on: ubuntu-latest
steps:
- name: Download artifacts
uses: actions/download-artifact@v2
with:
name: docs
path: docs
run: crystal docs --project-version="${{ env.GITHUB_REF_SLUG }}" --source-refname="${{ env.GITHUB_SHA_SHORT }}"
- name: Deploy docs 🚀
uses: JamesIves/github-pages-deploy-action@3.7.1
uses: JamesIves/github-pages-deploy-action@v4
with:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
BRANCH: gh-pages
FOLDER: docs
CLEAN: true
branch: gh-pages
folder: docs
clean: true

View file

@ -1,13 +1,14 @@
FROM alpine:edge as builder
RUN apk add --update crystal shards openssl-dev yaml-dev musl-dev make
RUN apk add --update crystal shards yaml-dev musl-dev make
RUN mkdir /ameba
WORKDIR /ameba
COPY . /ameba/
RUN make clean && make
FROM alpine:latest
RUN apk add --update openssl yaml pcre gc libevent libgcc
RUN apk add --update yaml pcre2 gc libevent libgcc
RUN mkdir /src
WORKDIR /src
COPY --from=builder /ameba/bin/ameba /usr/bin/
RUN ameba -v
ENTRYPOINT [ "/usr/bin/ameba" ]

View file

@ -1,22 +1,92 @@
.POSIX:
all:
# Recipes
## Build ameba
## $ make
## Run tests
## $ make test
## Install ameba
## $ sudo make install
-include Makefile.local # for optional local options
BUILD_TARGET ::= bin/ameba
DESTDIR ?= ## Install destination dir
PREFIX ?= /usr/local## Install path prefix
BINDIR ?= $(DESTDIR)$(PREFIX)/bin
# The crystal command to use
CRYSTAL_BIN ?= crystal
# The shards command to use
SHARDS_BIN ?= shards
PREFIX ?= /usr/local
# The install command to use
INSTALL_BIN ?= /usr/bin/install
SHARD_BIN ?= ../../bin
CRFLAGS ?= -Dpreview_mt
build: bin/ameba
bin/ameba:
SRC_SOURCES ::= $(shell find src -name '*.cr' 2>/dev/null)
DOC_SOURCE ::= src/**
.PHONY: all
all: build
.PHONY: build
build: ## Build the application binary
build: $(BUILD_TARGET)
$(BUILD_TARGET): $(SRC_SOURCES)
$(SHARDS_BIN) build $(CRFLAGS)
docs: ## Generate API docs
docs: $(SRC_SOURCES)
$(CRYSTAL_BIN) docs -o docs $(DOC_SOURCE)
.PHONY: lint
lint: ## Run ameba on ameba's code base
lint: $(BUILD_TARGET)
$(BUILD_TARGET)
.PHONY: spec
spec: ## Run the spec suite
spec:
$(CRYSTAL_BIN) spec
.PHONY: clean
clean: ## Remove application binary
clean:
rm -f ./bin/ameba ./bin/ameba.dwarf
install: build
mkdir -p $(PREFIX)/bin
cp ./bin/ameba $(PREFIX)/bin
@rm -f "$(BUILD_TARGET)" "$(BUILD_TARGET).dwarf"
.PHONY: install
install: ## Install application binary into $DESTDIR
install: $(BUILD_TARGET)
$(INSTALL_BIN) -m 0755 "$(BUILD_TARGET)" "$(BINDIR)/ameba"
.PHONY: bin
bin: build
mkdir -p $(SHARD_BIN)
cp ./bin/ameba $(SHARD_BIN)
run_file:
cp -n ./bin/ameba.cr $(SHARD_BIN) || true
test: build
$(CRYSTAL_BIN) spec
./bin/ameba --all
cp $(BUILD_TARGET) $(SHARD_BIN)
.PHONY: test
test: ## Run the spec suite and linter
test: spec lint
.PHONY: help
help: ## Show this help
@echo
@printf '\033[34mtargets:\033[0m\n'
@grep -hE '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) |\
sort |\
awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-15s\033[0m %s\n", $$1, $$2}'
@echo
@printf '\033[34moptional variables:\033[0m\n'
@grep -hE '^[a-zA-Z_-]+ \?=.*?## .*$$' $(MAKEFILE_LIST) |\
sort |\
awk 'BEGIN {FS = " \\?=.*?## "}; {printf " \033[36m%-15s\033[0m %s\n", $$1, $$2}'
@echo
@printf '\033[34mrecipes:\033[0m\n'
@grep -hE '^##.*$$' $(MAKEFILE_LIST) |\
awk 'BEGIN {FS = "## "}; /^## [a-zA-Z_-]/ {printf " \033[36m%s\033[0m\n", $$2}; /^## / {printf " %s\n", $$2}'

130
README.md
View file

@ -1,5 +1,3 @@
[![SWUbanner](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner-direct.svg)](https://github.com/vshymanskyy/StandWithUkraine/blob/main/docs/README.md)
<p align="center">
<img src="https://raw.githubusercontent.com/veelenga/bin/master/ameba/logo.png" width="800">
<h3 align="center">Ameba</h3>
@ -10,29 +8,29 @@
</sup>
</p>
<p align="center">
<a href="https://github.com/crystal-ameba/ameba/actions?query=workflow%3ACI"><img src="https://github.com/crystal-ameba/ameba/workflows/CI/badge.svg"></a>
<a href="https://github.com/crystal-ameba/ameba/actions/workflows/ci.yml"><img src="https://github.com/crystal-ameba/ameba/actions/workflows/ci.yml/badge.svg"></a>
<a href="https://github.com/crystal-ameba/ameba/releases"><img src="https://img.shields.io/github/release/crystal-ameba/ameba.svg?maxAge=360"></a>
<a href="https://github.com/crystal-ameba/ameba/blob/master/LICENSE"><img src="https://img.shields.io/github/license/crystal-ameba/ameba.svg"></a>
<a href="https://gitter.im/veelenga/ameba?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge"><img src="https://badges.gitter.im/veelenga/ameba.svg"></a>
</p>
</p>
- [About](#about)
- [Usage](#usage)
* [Watch a tutorial](#watch-a-tutorial)
* [Run in parallel](#run-in-parallel)
- [Watch a tutorial](#watch-a-tutorial)
- [Autocorrection](#autocorrection)
- [Explain issues](#explain-issues)
- [Run in parallel](#run-in-parallel)
- [Installation](#installation)
* [As a project dependency:](#as-a-project-dependency)
* [OS X](#os-x)
* [Docker](#docker)
* [From sources](#from-sources)
- [As a project dependency:](#as-a-project-dependency)
- [OS X](#os-x)
- [Docker](#docker)
- [From sources](#from-sources)
- [Configuration](#configuration)
* [Sources](#sources)
* [Rules](#rules)
* [Explain issues](#explain-issues)
* [Inline disabling](#inline-disabling)
- [Editors & integrations](#editors--integrations)
- [Credits & inspirations](#credits--inspirations)
- [Sources](#sources)
- [Rules](#rules)
- [Inline disabling](#inline-disabling)
- [Editors \& integrations](#editors--integrations)
- [Credits \& inspirations](#credits--inspirations)
- [Contributors](#contributors)
## About
@ -51,20 +49,25 @@ Run `ameba` binary within your project directory to catch code issues:
$ ameba
Inspecting 107 files
...............F.....................F....................................................................
...............F.....................FF....................................................................
src/ameba/formatter/flycheck_formatter.cr:4:33
[W] Lint/UnusedArgument: Unused argument `location`
> source.issues.each do |e, location|
src/ameba/formatter/flycheck_formatter.cr:6:37
[W] Lint/UnusedArgument: Unused argument `location`. If it's necessary, use `_` as an argument name to indicate that it won't be used.
> source.issues.each do |issue, location|
^
src/ameba/formatter/base_formatter.cr:12:7
src/ameba/formatter/base_formatter.cr:16:14
[W] Lint/UselessAssign: Useless assignment to variable `s`
> return s += issues.size
^
Finished in 542.64 milliseconds
129 inspected, 2 failures
src/ameba/formatter/base_formatter.cr:16:7 [Correctable]
[C] Style/RedundantReturn: Redundant `return` detected
> return s += issues.size
^---------------------^
Finished in 389.45 milliseconds
107 inspected, 3 failures
```
### Watch a tutorial
@ -73,17 +76,29 @@ Finished in 542.64 milliseconds
[🎬 Watch the LuckyCast showing how to use Ameba](https://luckycasts.com/videos/ameba)
### Run in parallel
### Autocorrection
Starting from 0.31.0 Crystal [supports parallelism](https://crystal-lang.org/2019/09/06/parallelism-in-crystal.html).
It allows to run linting in parallel too.
In order to take advantage of this feature you need to build ameba with preview_mt support:
Rules that are marked as `[Correctable]` in the output can be automatically corrected using `--fix` flag:
```sh
$ crystal build src/cli.cr -Dpreview_mt -o bin/ameba
$ make install
$ ameba --fix
```
### Explain issues
Ameba allows you to dig deeper into an issue, by showing you details about the issue
and the reasoning by it being reported.
To be convenient, you can just copy-paste the `PATH:line:column` string from the
report and paste behind the `ameba` command to check it out.
```sh
$ ameba crystal/command/format.cr:26:83 # show explanation for the issue
$ ameba --explain crystal/command/format.cr:26:83 # same thing
```
### Run in parallel
Some quick benchmark results measured while running Ameba on Crystal repo:
```sh
@ -103,30 +118,14 @@ Add this to your application's `shard.yml`:
development_dependencies:
ameba:
github: crystal-ameba/ameba
version: ~> 1.0.0
```
Build `bin/ameba` binary within your project directory while running `shards install`.
You may also want to use it on [Travis](travis-ci.org):
```yaml
# .travis.yml
language: crystal
install:
- shards install
script:
- crystal spec
- crystal bin/ameba.cr
```
Using this config Ameba will inspect files just after the specs run. Travis will also fail
the build if some problems detected.
### OS X
```sh
$ brew tap veelenga/tap
$ brew tap crystal-ameba/ameba
$ brew install ameba
```
@ -135,16 +134,16 @@ $ brew install ameba
Build the image:
```sh
$ docker build -t crystal-ameba/ameba .
$ docker build -t ghcr.io/crystal-ameba/ameba .
```
To use the resulting image on a local source folder, mount the current (or target) directory into `/src`:
```sh
$ docker run -v $(pwd):/src crystal-ameba/ameba
$ docker run -v $(pwd):/src ghcr.io/crystal-ameba/ameba
```
Also available on DockerHub: https://hub.docker.com/r/veelenga/ameba
Also available on GitHub: https://github.com/crystal-ameba/ameba/pkgs/container/ameba
### From sources
@ -165,7 +164,7 @@ Generate new file by running `ameba --gen-config`.
**List of sources to run Ameba on can be configured globally via:**
- `Globs` section - an array of wildcards (or paths) to include to the
inspection. Defaults to `%w(**/*.cr !lib)`, meaning it includes all project
inspection. Defaults to `%w[**/*.cr !lib]`, meaning it includes all project
files with `*.cr` extension except those which exist in `lib` folder.
- `Excluded` section - an array of wildcards (or paths) to exclude from the
source list defined by `Globs`. Defaults to an empty array.
@ -174,8 +173,8 @@ In this example we define default globs and exclude `src/compiler` folder:
``` yaml
Globs:
- **/*.cr
- !lib
- "**/*.cr"
- "!lib"
Excluded:
- src/compiler
@ -209,19 +208,6 @@ Style/RedundantBegin:
Enabled: false
```
### Explain issues
Ameba allows you to dig deeper into an issue, by showing you details about the issue
and the reasoning by it being reported.
To be convenient, you can just copy-paste the `PATH:line:column` string from the
report and paste behind the `ameba` command to check it out.
```sh
$ ameba crystal/command/format.cr:26:83 # show explanation for the issue
$ ameba --explain crystal/command/format.cr:26:83 # same thing
```
### Inline disabling
One or more rules or one or more group of rules can be disabled using inline directives:
@ -231,18 +217,17 @@ One or more rules or one or more group of rules can be disabled using inline dir
time = Time.epoch(1483859302)
time = Time.epoch(1483859302) # ameba:disable Style/LargeNumbers, Lint/UselessAssign
time = Time.epoch(1483859302) # ameba:disable Style, Lint
```
## Editors & integrations
* Vim: [vim-crystal](https://github.com/rhysd/vim-crystal), [Ale](https://github.com/w0rp/ale)
* Emacs: [ameba.el](https://github.com/crystal-ameba/ameba.el)
* Sublime Text: [Sublime Linter Ameba](https://github.com/epergo/SublimeLinter-contrib-ameba)
* VSCode: [vscode-crystal-ameba](https://github.com/crystal-ameba/vscode-crystal-ameba)
* Codacy: [codacy-ameba](https://github.com/codacy/codacy-ameba)
* GitHub Actions: [github-action](https://github.com/crystal-ameba/github-action)
- Vim: [vim-crystal](https://github.com/rhysd/vim-crystal), [Ale](https://github.com/w0rp/ale)
- Emacs: [ameba.el](https://github.com/crystal-ameba/ameba.el)
- Sublime Text: [Sublime Linter Ameba](https://github.com/epergo/SublimeLinter-contrib-ameba)
- VSCode: [vscode-crystal-ameba](https://github.com/crystal-ameba/vscode-crystal-ameba)
- Codacy: [codacy-ameba](https://github.com/codacy/codacy-ameba)
- GitHub Actions: [github-action](https://github.com/crystal-ameba/github-action)
## Credits & inspirations
@ -254,3 +239,4 @@ time = Time.epoch(1483859302) # ameba:disable Style, Lint
## Contributors
- [veelenga](https://github.com/veelenga) Vitalii Elenhaupt - creator, maintainer
- [Sija](https://github.com/Sija) Sijawusz Pur Rahnama - contributor, maintainer

View file

@ -15,7 +15,7 @@ Benchmark.ips do |x|
20,
30,
40,
].each do |n|
].each do |n| # ameba:disable Naming/BlockParameterName
config = Ameba::Config.load
config.formatter = Ameba::Formatter::BaseFormatter.new
config.globs = get_files(n)

View file

@ -1,20 +1,22 @@
name: ameba
version: 1.3.1
version: 1.6.1
authors:
- Vitalii Elenhaupt <velenhaupt@gmail.com>
- Sijawusz Pur Rahnama <sija@sija.pl>
targets:
ameba:
main: src/cli.cr
scripts:
# TODO: remove pre-compiled executable in future releases
postinstall: make bin && make run_file
postinstall: shards build -Dpreview_mt
# TODO: remove pre-compiled executable in future releases
executables:
- ameba
- ameba.cr
crystal: "~> 1.6.0"
crystal: ~> 1.10
license: MIT

View file

@ -10,29 +10,29 @@ module Ameba::AST
describe ".of" do
context "Crystal::If" do
it "constructs a branch in If.cond" do
branch = branch_of_assign_in_def %(
branch = branch_of_assign_in_def <<-CRYSTAL
def method
if a = get_something # --> Crystal::Assign
puts a
end
end
)
CRYSTAL
branch.to_s.should eq "a = get_something"
end
it "constructs a branch in If.then" do
branch = branch_of_assign_in_def %(
branch = branch_of_assign_in_def <<-CRYSTAL
def method
if true
a = 2 # --> Crystal::Assign
end
end
)
CRYSTAL
branch.to_s.should eq "a = 2"
end
it "constructs a branch in If.else" do
branch = branch_of_assign_in_def %(
branch = branch_of_assign_in_def <<-CRYSTAL
def method
if true
nil
@ -40,45 +40,45 @@ module Ameba::AST
a = 2 # --> Crystal::Assign
end
end
)
CRYSTAL
branch.to_s.should eq "a = 2"
end
it "constructs a branch in inline If" do
branch = branch_of_assign_in_def %(
branch = branch_of_assign_in_def <<-CRYSTAL
def method(a)
a = 0 if a == 2 # --> Crystal::Assign
end
)
CRYSTAL
branch.to_s.should eq "a = 0"
end
end
context "Crystal::Unless" do
it "constructs a branch in Unless.cond" do
branch = branch_of_assign_in_def %(
branch = branch_of_assign_in_def <<-CRYSTAL
def method
unless a = get_something # --> Crystal::Assign
puts a
end
end
)
CRYSTAL
branch.to_s.should eq "a = get_something"
end
it "constructs a branch in Unless.then" do
branch = branch_of_assign_in_def %(
branch = branch_of_assign_in_def <<-CRYSTAL
def method
unless true
a = 2 # --> Crystal::Assign
end
end
)
CRYSTAL
branch.to_s.should eq "a = 2"
end
it "constructs a new branch in Unless.else" do
branch = branch_of_assign_in_def %(
branch = branch_of_assign_in_def <<-CRYSTAL
def method
unless true
nil
@ -86,188 +86,188 @@ module Ameba::AST
a = 2 # --> Crystal::Assign
end
end
)
CRYSTAL
branch.to_s.should eq "a = 2"
end
it "constructs a branch in inline Unless" do
branch = branch_of_assign_in_def %(
branch = branch_of_assign_in_def <<-CRYSTAL
def method(a)
(a = 0; b = 3) unless a == 2 # --> Crystal::Expressions
end
)
CRYSTAL
branch.to_s.should eq "(a = 0\nb = 3)"
end
end
context "Crystal::BinaryOp" do
it "constructs a branch in left node" do
branch = branch_of_assign_in_def %(
branch = branch_of_assign_in_def <<-CRYSTAL
def method(a)
(a = 2) && do_something
end
)
CRYSTAL
branch.to_s.should eq "(a = 2)"
end
it "constructs a branch in right node" do
branch = branch_of_assign_in_def %(
branch = branch_of_assign_in_def <<-CRYSTAL
def method(a)
do_something || (a = 0)
end
)
CRYSTAL
branch.to_s.should eq "(a = 0)"
end
end
context "Crystal::Case" do
it "constructs a branch in cond" do
branch = branch_of_assign_in_def %(
branch = branch_of_assign_in_def <<-CRYSTAL
def method(a)
case (a = 2)
when true then nil
end
end
)
CRYSTAL
branch.to_s.should eq "(a = 2)"
end
it "constructs a branch in when" do
branch = branch_of_assign_in_def %(
branch = branch_of_assign_in_def <<-CRYSTAL
def method(a)
case a
when a = 3 then nil
end
end
)
CRYSTAL
branch.to_s.should eq "when a = 3\n nil\n"
end
it "constructs a branch in else" do
branch = branch_of_assign_in_def %(
branch = branch_of_assign_in_def <<-CRYSTAL
def method(a)
case a
when true then nil
else a = 4
end
end
)
CRYSTAL
branch.to_s.should eq "a = 4"
end
end
context "Crystal::While" do
it "constructs a branch in cond" do
branch = branch_of_assign_in_def %(
branch = branch_of_assign_in_def <<-CRYSTAL
def method(a)
while a = 1
nil
end
end
)
CRYSTAL
branch.to_s.should eq "a = 1"
end
it "constructs a branch in body" do
branch = branch_of_assign_in_def %(
branch = branch_of_assign_in_def <<-CRYSTAL
def method(a)
while true
b = (a = 1)
end
end
)
CRYSTAL
branch.to_s.should eq "b = (a = 1)"
end
end
context "Crystal::Until" do
it "constructs a branch in cond" do
branch = branch_of_assign_in_def %(
branch = branch_of_assign_in_def <<-CRYSTAL
def method(a)
until a = 1
nil
end
end
)
CRYSTAL
branch.to_s.should eq "a = 1"
end
it "constructs a branch in body" do
branch = branch_of_assign_in_def %(
branch = branch_of_assign_in_def <<-CRYSTAL
def method(a)
until false
b = (a = 1)
end
end
)
CRYSTAL
branch.to_s.should eq "b = (a = 1)"
end
end
context "Crystal::ExceptionHandler" do
it "constructs a branch in body" do
branch = branch_of_assign_in_def %(
branch = branch_of_assign_in_def <<-CRYSTAL
def method(a)
a = 1
rescue
nil
end
)
CRYSTAL
branch.to_s.should eq "a = 1"
end
it "constructs a branch in a rescue" do
branch = branch_of_assign_in_def %(
branch = branch_of_assign_in_def <<-CRYSTAL
def method(a)
rescue
a = 1
end
)
CRYSTAL
branch.to_s.should eq "a = 1"
end
it "constructs a branch in else" do
branch = branch_of_assign_in_def %(
branch = branch_of_assign_in_def <<-CRYSTAL
def method(a)
rescue
else
a = 1
end
)
CRYSTAL
branch.to_s.should eq "a = 1"
end
it "constructs a branch in ensure" do
branch = branch_of_assign_in_def %(
branch = branch_of_assign_in_def <<-CRYSTAL
def method(a)
rescue
ensure
a = 1
end
)
CRYSTAL
branch.to_s.should eq "a = 1"
end
end
context "Crystal::MacroIf" do
it "constructs a branch in cond" do
branch = branch_of_assign_in_def %(
branch = branch_of_assign_in_def <<-CRYSTAL
def method(a)
{% if a = 2 %}
{% end %}
end
)
CRYSTAL
branch.to_s.should eq "a = 2"
end
it "constructs a branch in then" do
nodes = as_nodes %(
nodes = as_nodes <<-CRYSTAL
def method(a)
{% if true %}
a = 2
{% end %}
end
)
CRYSTAL
branch = Branch.of(nodes.macro_literal_nodes.first, nodes.def_nodes.first)
branch.to_s.strip.should eq "a = 2"
end
@ -275,36 +275,64 @@ module Ameba::AST
context "Crystal::MacroFor" do
it "constructs a branch in body" do
nodes = as_nodes %(
nodes = as_nodes <<-CRYSTAL
def method(a)
{% for x in [1, 2, 3] %}
a = 2
{% end %}
end
)
CRYSTAL
branch = Branch.of(nodes.macro_literal_nodes.first, nodes.def_nodes.first)
branch.to_s.strip.should eq "a = 2"
end
end
it "returns nil if branch does not exist" do
nodes = as_nodes %(
nodes = as_nodes <<-CRYSTAL
def method
a = 2
end
)
CRYSTAL
branch = Branch.of(nodes.assign_nodes.first, nodes.def_nodes.first)
branch.should be_nil
end
end
context "Crystal::Call" do
context "loop" do
it "constructs a branch in block" do
branch = branch_of_assign_in_def <<-CRYSTAL
def method(a)
loop do
b = (a = 1)
end
end
CRYSTAL
branch.to_s.should eq "b = (a = 1)"
end
end
context "other" do
it "skips constructing a branch in block" do
branch = branch_of_assign_in_def <<-CRYSTAL
def method(a)
1.upto(10) do
b = (a = 1)
end
end
CRYSTAL
branch.should be_nil
end
end
end
describe "#initialize" do
it "creates new branch" do
nodes = as_nodes %(
nodes = as_nodes <<-CRYSTAL
if true
a = 2
end
)
CRYSTAL
branchable = Branchable.new nodes.if_nodes.first
branch = Branch.new nodes.assign_nodes.first, branchable
branch.node.should_not be_nil
@ -313,22 +341,22 @@ module Ameba::AST
describe "delegation" do
it "delegates to_s to node" do
nodes = as_nodes %(
nodes = as_nodes <<-CRYSTAL
if true
a = 2
end
)
CRYSTAL
branchable = Branchable.new nodes.if_nodes.first
branch = Branch.new nodes.assign_nodes.first, branchable
branch.to_s.should eq branch.node.to_s
end
it "delegates locations to node" do
nodes = as_nodes %(
nodes = as_nodes <<-CRYSTAL
if true
a = 2
end
)
CRYSTAL
branchable = Branchable.new nodes.if_nodes.first
branch = Branch.new nodes.assign_nodes.first, branchable
branch.location.should eq branch.node.location
@ -338,26 +366,50 @@ module Ameba::AST
describe "#in_loop?" do
it "returns true if branch is in a loop" do
nodes = as_nodes %(
nodes = as_nodes <<-CRYSTAL
while true
a = 1
end
)
CRYSTAL
branchable = Branchable.new nodes.while_nodes.first
branch = Branch.new nodes.assign_nodes.first, branchable
branch.in_loop?.should be_true
end
it "returns false if branch is not in a loop" do
nodes = as_nodes %(
nodes = as_nodes <<-CRYSTAL
if a > 2
a = 1
end
)
CRYSTAL
branchable = Branchable.new nodes.if_nodes.first
branch = Branch.new nodes.assign_nodes.first, branchable
branch.in_loop?.should be_false
end
context "Crystal::Call" do
it "returns true if branch is in a loop" do
nodes = as_nodes <<-CRYSTAL
loop do
a = 1
end
CRYSTAL
branchable = Branchable.new nodes.call_nodes.first
branch = Branch.new nodes.assign_nodes.first, branchable
branch.in_loop?.should be_true
end
it "returns false if branch is not in a loop" do
nodes = as_nodes <<-CRYSTAL
1.upto(10) do
a = 1
end
CRYSTAL
branchable = Branchable.new nodes.call_nodes.first
branch = Branch.new nodes.assign_nodes.first, branchable
branch.in_loop?.should be_false
end
end
end
end
end

View file

@ -4,20 +4,20 @@ module Ameba::AST
describe Branchable do
describe "#initialize" do
it "creates a new branchable" do
branchable = Branchable.new as_node %(a = 2 if true)
branchable = Branchable.new as_node "a = 2 if true"
branchable.node.should_not be_nil
end
end
describe "delegation" do
it "delegates to_s to @node" do
node = as_node %(a = 2 if true)
node = as_node "a = 2 if true"
branchable = Branchable.new node
branchable.to_s.should eq node.to_s
end
it "delegates locations to @node" do
node = as_node %(a = 2 if true)
node = as_node "a = 2 if true"
branchable = Branchable.new node
branchable.location.should eq node.location
branchable.end_location.should eq node.end_location
@ -26,22 +26,22 @@ module Ameba::AST
describe "#loop?" do
it "returns true if it is a while loop" do
branchable = Branchable.new as_node %(while true; a = 2; end)
branchable = Branchable.new as_node "while true; a = 2; end"
branchable.loop?.should be_true
end
it "returns true if it is the until loop" do
branchable = Branchable.new as_node %(until false; a = 2; end)
branchable = Branchable.new as_node "until false; a = 2; end"
branchable.loop?.should be_true
end
it "returns true if it is loop" do
branchable = Branchable.new as_node %(loop {})
branchable = Branchable.new as_node "loop {}"
branchable.loop?.should be_true
end
it "returns false otherwise" do
branchable = Branchable.new as_node %(a = 2 if true)
branchable = Branchable.new as_node "a = 2 if true"
branchable.loop?.should be_false
end
end

View file

@ -7,7 +7,7 @@ module Ameba::AST
node = as_node("return 22")
flow_expression = FlowExpression.new node, false
flow_expression.node.should_not be_nil
flow_expression.in_loop?.should eq false
flow_expression.in_loop?.should be_false
end
describe "#delegation" do
@ -18,7 +18,7 @@ module Ameba::AST
end
it "delegates locations to @node" do
node = as_node %(break if true)
node = as_node("break if true")
flow_expression = FlowExpression.new node, false
flow_expression.location.should eq node.location
flow_expression.end_location.should eq node.end_location
@ -27,20 +27,20 @@ module Ameba::AST
describe "#unreachable_nodes" do
it "returns unreachable nodes" do
nodes = as_nodes %(
nodes = as_nodes <<-CRYSTAL
def foobar
return
a = 1
a = 2
end
)
CRYSTAL
node = nodes.expressions_nodes.first
flow_expression = FlowExpression.new node, false
flow_expression.unreachable_nodes.should eq nodes.assign_nodes
end
it "returns nil if there is no unreachable node after loop" do
nodes = as_nodes %(
nodes = as_nodes <<-CRYSTAL
def run
idx = items.size - 1
while 0 <= idx
@ -49,19 +49,19 @@ module Ameba::AST
puts "foo"
end
)
CRYSTAL
node = nodes.expressions_nodes.first
flow_expression = FlowExpression.new node, false
flow_expression.unreachable_nodes.empty?.should eq true
end
it "returns nil if there is no unreachable node" do
nodes = as_nodes %(
nodes = as_nodes <<-CRYSTAL
def foobar
a = 1
return a
end
)
CRYSTAL
node = nodes.expressions_nodes.first
flow_expression = FlowExpression.new node, false
flow_expression.unreachable_nodes.empty?.should eq true

View file

@ -49,47 +49,51 @@ module Ameba::AST
describe "#references?" do
it "returns true if current scope references variable" do
nodes = as_nodes %(
nodes = as_nodes <<-CRYSTAL
def method
a = 2
block do
3.times { |i| a = a + i }
end
end
)
scope = Scope.new nodes.def_nodes.first
CRYSTAL
var_node = nodes.var_nodes.first
scope.add_variable var_node
scope = Scope.new nodes.def_nodes.first
scope.add_variable(var_node)
scope.inner_scopes << Scope.new(nodes.block_nodes.first, scope)
variable = Variable.new(var_node, scope)
variable.reference nodes.var_nodes.first, scope.inner_scopes.first
variable.reference(nodes.var_nodes.first, scope.inner_scopes.first)
scope.references?(variable).should be_true
end
it "returns false if inner scopes are not checked" do
nodes = as_nodes %(
nodes = as_nodes <<-CRYSTAL
def method
a = 2
block do
3.times { |i| a = a + i }
end
end
)
scope = Scope.new nodes.def_nodes.first
CRYSTAL
var_node = nodes.var_nodes.first
scope.add_variable var_node
scope = Scope.new nodes.def_nodes.first
scope.add_variable(var_node)
scope.inner_scopes << Scope.new(nodes.block_nodes.first, scope)
variable = Variable.new(var_node, scope)
variable.reference nodes.var_nodes.first, scope.inner_scopes.first
variable.reference(nodes.var_nodes.first, scope.inner_scopes.first)
scope.references?(variable, check_inner_scopes: false).should be_false
end
it "returns false if current scope does not reference variable" do
nodes = as_nodes %(
nodes = as_nodes <<-CRYSTAL
def method
a = 2
block do
@ -97,10 +101,12 @@ module Ameba::AST
3.times { |i| b = b + i }
end
end
)
scope = Scope.new nodes.def_nodes.first
CRYSTAL
var_node = nodes.var_nodes.first
scope.add_variable var_node
scope = Scope.new nodes.def_nodes.first
scope.add_variable(var_node)
scope.inner_scopes << Scope.new(nodes.block_nodes.first, scope)
variable = Variable.new(var_node, scope)
@ -120,7 +126,7 @@ module Ameba::AST
describe "#find_variable" do
it "returns the variable in the scope by name" do
scope = Scope.new as_node("foo = 1")
scope.add_variable Crystal::Var.new "foo"
scope.add_variable(Crystal::Var.new "foo")
scope.find_variable("foo").should_not be_nil
end
@ -133,7 +139,7 @@ module Ameba::AST
describe "#assign_variable" do
it "creates a new assignment" do
scope = Scope.new as_node("foo = 1")
scope.add_variable Crystal::Var.new "foo"
scope.add_variable(Crystal::Var.new "foo")
scope.assign_variable("foo", Crystal::Var.new "foo")
var = scope.find_variable("foo").should_not be_nil
var.assignments.size.should eq 1
@ -141,7 +147,7 @@ module Ameba::AST
it "does not create the assignment if variable is wrong" do
scope = Scope.new as_node("foo = 1")
scope.add_variable Crystal::Var.new "foo"
scope.add_variable(Crystal::Var.new "foo")
scope.assign_variable("bar", Crystal::Var.new "bar")
var = scope.find_variable("foo").should_not be_nil
var.assignments.size.should eq 0
@ -150,57 +156,75 @@ module Ameba::AST
describe "#block?" do
it "returns true if Crystal::Block" do
nodes = as_nodes %(
3.times {}
)
nodes = as_nodes("3.times {}")
scope = Scope.new nodes.block_nodes.first
scope.block?.should be_true
end
it "returns false otherwise" do
scope = Scope.new as_node "a = 1"
scope = Scope.new as_node("a = 1")
scope.block?.should be_false
end
end
describe "#spawn_block?" do
it "returns true if a node is a spawn block" do
nodes = as_nodes %(
spawn {}
)
nodes = as_nodes("spawn {}")
scope = Scope.new nodes.block_nodes.first
scope.spawn_block?.should be_true
end
it "returns false otherwise" do
scope = Scope.new as_node "a = 1"
scope = Scope.new as_node("a = 1")
scope.spawn_block?.should be_false
end
end
describe "#def?" do
context "when check_outer_scopes: true" do
it "returns true if outer scope is Crystal::Def" do
nodes = as_nodes("def foo; 3.times {}; end")
outer_scope = Scope.new nodes.def_nodes.first
scope = Scope.new nodes.block_nodes.first, outer_scope
scope.def?(check_outer_scopes: true).should be_true
end
end
it "returns true if Crystal::Def" do
nodes = as_nodes("def foo; end")
scope = Scope.new nodes.def_nodes.first
scope.def?.should be_true
end
it "returns false otherwise" do
scope = Scope.new as_node("a = 1")
scope.def?.should be_false
end
end
describe "#in_macro?" do
it "returns true if Crystal::Macro" do
nodes = as_nodes %(
nodes = as_nodes <<-CRYSTAL
macro included
end
)
CRYSTAL
scope = Scope.new nodes.macro_nodes.first
scope.in_macro?.should be_true
end
it "returns true if node is nested to Crystal::Macro" do
nodes = as_nodes %(
nodes = as_nodes <<-CRYSTAL
macro included
{{ @type.each do |type| a = type end }}
end
)
CRYSTAL
outer_scope = Scope.new nodes.macro_nodes.first
scope = Scope.new nodes.block_nodes.first, outer_scope
scope.in_macro?.should be_true
end
it "returns false otherwise" do
scope = Scope.new as_node "a = 1"
scope = Scope.new as_node("a = 1")
scope.in_macro?.should be_false
end
end

View file

@ -36,6 +36,43 @@ module Ameba::AST
end
end
describe "#static/dynamic_literal?" do
[
Crystal::ArrayLiteral.new,
Crystal::ArrayLiteral.new([Crystal::StringLiteral.new("foo")] of Crystal::ASTNode),
Crystal::BoolLiteral.new(false),
Crystal::CharLiteral.new('a'),
Crystal::HashLiteral.new,
Crystal::NamedTupleLiteral.new,
Crystal::NilLiteral.new,
Crystal::NumberLiteral.new(42),
Crystal::RegexLiteral.new(Crystal::StringLiteral.new("")),
Crystal::StringLiteral.new("foo"),
Crystal::SymbolLiteral.new("foo"),
Crystal::TupleLiteral.new([] of Crystal::ASTNode),
Crystal::TupleLiteral.new([Crystal::StringLiteral.new("foo")] of Crystal::ASTNode),
Crystal::RangeLiteral.new(
Crystal::NumberLiteral.new(0),
Crystal::NumberLiteral.new(10),
true),
].each do |literal|
it "properly identifies static node #{literal}" do
subject.static_literal?(literal).should be_true
subject.dynamic_literal?(literal).should be_false
end
end
[
Crystal::ArrayLiteral.new([Crystal::Path.new(%w[IO])] of Crystal::ASTNode),
Crystal::TupleLiteral.new([Crystal::Path.new(%w[IO])] of Crystal::ASTNode),
].each do |literal|
it "properly identifies dynamic node #{literal}" do
subject.dynamic_literal?(literal).should be_true
subject.static_literal?(literal).should be_false
end
end
end
describe "#node_source" do
it "returns original source of the node" do
s = <<-CRYSTAL
@ -93,7 +130,7 @@ module Ameba::AST
it "returns false if this is a break out of loop" do
node = as_node("break")
subject.flow_command?(node, false).should eq false
subject.flow_command?(node, false).should be_false
end
it "returns true if this is a next in a loop" do
@ -103,7 +140,7 @@ module Ameba::AST
it "returns false if this is a next out of loop" do
node = as_node("next")
subject.flow_command?(node, false).should eq false
subject.flow_command?(node, false).should be_false
end
it "returns true if this is raise" do
@ -123,7 +160,7 @@ module Ameba::AST
it "returns false otherwise" do
node = as_node("foobar")
subject.flow_command?(node, false).should eq false
subject.flow_command?(node, false).should be_false
end
end
@ -195,7 +232,7 @@ module Ameba::AST
break
end
CRYSTAL
subject.flow_expression?(node).should eq false
subject.flow_expression?(node).should be_false
end
it "returns true if this until consumed by flow expressions" do
@ -213,7 +250,7 @@ module Ameba::AST
break
end
CRYSTAL
subject.flow_expression?(node).should eq false
subject.flow_expression?(node).should be_false
end
it "returns true if this expressions consumed by flow expressions" do
@ -230,7 +267,7 @@ module Ameba::AST
exp1
exp2
CRYSTAL
subject.flow_expression?(node).should eq false
subject.flow_expression?(node).should be_false
end
end
@ -242,12 +279,12 @@ module Ameba::AST
it "returns false if it has a receiver" do
node = as_node "obj.raise e"
subject.raise?(node).should eq false
subject.raise?(node).should be_false
end
it "returns false if size of the arguments doesn't match" do
node = as_node "raise"
subject.raise?(node).should eq false
subject.raise?(node).should be_false
end
end
@ -264,12 +301,12 @@ module Ameba::AST
it "returns false if it has a receiver" do
node = as_node "obj.exit"
subject.exit?(node).should eq false
subject.exit?(node).should be_false
end
it "returns false if size of the arguments doesn't match" do
node = as_node "exit 1, 1"
subject.exit?(node).should eq false
subject.exit?(node).should be_false
end
end
@ -291,12 +328,12 @@ module Ameba::AST
it "returns false if it has a receiver" do
node = as_node "obj.abort"
subject.abort?(node).should eq false
subject.abort?(node).should be_false
end
it "returns false if size of the arguments doesn't match" do
node = as_node "abort 1, 1, 1"
subject.abort?(node).should eq false
subject.abort?(node).should be_false
end
end
@ -308,12 +345,12 @@ module Ameba::AST
it "returns false if it has a receiver" do
node = as_node "obj.loop"
subject.loop?(node).should eq false
subject.loop?(node).should be_false
end
it "returns false if size of the arguments doesn't match" do
node = as_node "loop 1"
subject.loop?(node).should eq false
subject.loop?(node).should be_false
end
end

View file

@ -41,15 +41,14 @@ module Ameba::AST
describe "#branch" do
it "returns the branch of the assignment" do
nodes = as_nodes %(
nodes = as_nodes <<-CRYSTAL
def method(a)
if a
a = 3 # --> Crystal::Expressions
puts a
end
end
)
CRYSTAL
scope = Scope.new nodes.def_nodes.first
variable = Variable.new(nodes.var_nodes.first, scope)
assignment = Assignment.new(nodes.assign_nodes.first, variable, scope)
@ -58,7 +57,7 @@ module Ameba::AST
end
it "returns inner branch" do
nodes = as_nodes %(
nodes = as_nodes <<-CRYSTAL
def method(a, b)
if a
if b
@ -66,7 +65,7 @@ module Ameba::AST
end
end
end
)
CRYSTAL
scope = Scope.new nodes.def_nodes.first
variable = Variable.new(nodes.var_nodes.first, scope)
assignment = Assignment.new(nodes.assign_nodes.first, variable, scope)
@ -75,44 +74,16 @@ module Ameba::AST
end
it "returns nil if assignment does not have a branch" do
nodes = as_nodes %(
nodes = as_nodes <<-CRYSTAL
def method(a)
a = 2
end
)
CRYSTAL
scope = Scope.new nodes.def_nodes.first
variable = Variable.new(nodes.var_nodes.first, scope)
assignment = Assignment.new(nodes.assign_nodes.first, variable, scope)
assignment.branch.should be_nil
end
end
describe "#transformed?" do
it "returns false if the assignment is not transformed by the compiler" do
nodes = as_nodes %(
def method(a)
a = 2
end
)
scope = Scope.new nodes.def_nodes.first
variable = Variable.new(nodes.var_nodes.first, scope)
assignment = Assignment.new(nodes.assign_nodes.first, variable, scope)
assignment.transformed?.should be_false
end
it "returns true if the assignment is transformed by the compiler" do
nodes = as_nodes %(
array.each do |(a, b)|
end
)
scope = Scope.new nodes.block_nodes.first
variable = Variable.new(nodes.var_nodes.first, scope)
assignment = Assignment.new(nodes.assign_nodes.first, variable, scope)
assignment.transformed?.should be_true
end
end
end
end

View file

@ -0,0 +1,31 @@
require "../../../spec_helper"
module Ameba::AST
describe TypeDecVariable do
var = Crystal::Var.new("foo")
declared_type = Crystal::Path.new("String")
type_dec = Crystal::TypeDeclaration.new(var, declared_type)
describe "#initialize" do
it "creates a new type dec variable" do
variable = TypeDecVariable.new(type_dec)
variable.node.should_not be_nil
end
end
describe "#name" do
it "returns var name" do
variable = TypeDecVariable.new(type_dec)
variable.name.should eq var.name
end
it "raises if type declaration is incorrect" do
type_dec = Crystal::TypeDeclaration.new(declared_type, declared_type)
expect_raises(Exception, "Unsupported var node type: Crystal::Path") do
TypeDecVariable.new(type_dec).name
end
end
end
end
end

View file

@ -79,30 +79,35 @@ module Ameba::AST
describe "#captured_by_block?" do
it "returns truthy if the variable is captured by block" do
nodes = as_nodes %(
nodes = as_nodes <<-CRYSTAL
def method
a = 2
3.times { |i| a = a + i }
end
)
scope = Scope.new nodes.def_nodes.first
CRYSTAL
var_node = nodes.var_nodes.first
scope.add_variable var_node
scope = Scope.new(nodes.def_nodes.first)
scope.add_variable(var_node)
scope.inner_scopes << Scope.new(nodes.block_nodes.first, scope)
variable = Variable.new(var_node, scope)
variable.reference nodes.var_nodes.last, scope.inner_scopes.last
variable.reference(nodes.var_nodes.last, scope.inner_scopes.last)
variable.captured_by_block?.should be_truthy
end
it "returns falsey if the variable is not captured by the block" do
scope = Scope.new as_node %(
it "returns falsy if the variable is not captured by the block" do
scope = Scope.new as_node <<-CRYSTAL
def method
a = 1
end
)
scope.add_variable Crystal::Var.new "a"
CRYSTAL
scope.add_variable(Crystal::Var.new "a")
variable = scope.variables.first
variable.captured_by_block?.should be_falsey
end
end

View file

@ -19,33 +19,33 @@ module Ameba::AST
end
it "is 1 if there is Macro::For" do
code = %(
def initialize()
code = <<-CRYSTAL
def initialize
{% for c in ALL_NODES %}
true || false
{% end %}
end
)
CRYSTAL
node = Crystal::Parser.new(code).parse
visitor = CountingVisitor.new node
visitor.count.should eq 1
end
it "is 1 if there is Macro::If" do
code = %(
def initialize()
code = <<-CRYSTAL
def initialize
{% if foo.bar? %}
true || false
{% end %}
end
)
CRYSTAL
node = Crystal::Parser.new(code).parse
visitor = CountingVisitor.new node
visitor.count.should eq 1
end
it "increases count for every exhaustive case" do
code = %(
code = <<-CRYSTAL
def hello(a : Int32 | Int64 | Float32 | Float64)
case a
in Int32 then "int32"
@ -54,7 +54,7 @@ module Ameba::AST
in Float64 then "float64"
end
end
)
CRYSTAL
node = Crystal::Parser.new(code).parse
visitor = CountingVisitor.new node
visitor.count.should eq 2

View file

@ -1,22 +1,20 @@
require "../../../spec_helper"
module Ameba::AST
source = Source.new ""
describe FlowExpressionVisitor do
it "creates an expression for return" do
rule = FlowExpressionRule.new
FlowExpressionVisitor.new rule, Source.new %(
FlowExpressionVisitor.new rule, Source.new <<-CRYSTAL
def foo
return :bar
end
)
CRYSTAL
rule.expressions.size.should eq 1
end
it "can create multiple expressions" do
rule = FlowExpressionRule.new
FlowExpressionVisitor.new rule, Source.new %(
FlowExpressionVisitor.new rule, Source.new <<-CRYSTAL
def foo
if bar
return :baz
@ -24,13 +22,13 @@ module Ameba::AST
return :foobar
end
end
)
CRYSTAL
rule.expressions.size.should eq 3
end
it "properly creates nested flow expressions" do
rule = FlowExpressionRule.new
FlowExpressionVisitor.new rule, Source.new %(
FlowExpressionVisitor.new rule, Source.new <<-CRYSTAL
def foo
return (
loop do
@ -39,27 +37,27 @@ module Ameba::AST
end
)
end
)
CRYSTAL
rule.expressions.size.should eq 4
end
it "creates an expression for break" do
rule = FlowExpressionRule.new
FlowExpressionVisitor.new rule, Source.new %(
FlowExpressionVisitor.new rule, Source.new <<-CRYSTAL
while true
break
end
)
CRYSTAL
rule.expressions.size.should eq 1
end
it "creates an expression for next" do
rule = FlowExpressionRule.new
FlowExpressionVisitor.new rule, Source.new %(
FlowExpressionVisitor.new rule, Source.new <<-CRYSTAL
while true
next if something
end
)
CRYSTAL
rule.expressions.size.should eq 1
end
end

View file

@ -5,11 +5,11 @@ module Ameba::AST
rule = RedundantControlExpressionRule.new
describe RedundantControlExpressionVisitor do
node = as_node %(
node = as_node <<-CRYSTAL
a = 1
b = 2
return a + b
)
CRYSTAL
subject = RedundantControlExpressionVisitor.new(rule, source, node)
it "assigns valid attributes" do

View file

@ -2,39 +2,50 @@ require "../../../spec_helper"
module Ameba::AST
describe ScopeVisitor do
{% for type in %w[class module enum].map(&.id) %}
it "creates a scope for the {{ type }} def" do
rule = ScopeRule.new
ScopeVisitor.new rule, Source.new <<-CRYSTAL
{{ type }} Foo
end
CRYSTAL
rule.scopes.size.should eq 1
end
{% end %}
it "creates a scope for the def" do
rule = ScopeRule.new
ScopeVisitor.new rule, Source.new %(
ScopeVisitor.new rule, Source.new <<-CRYSTAL
def method
end
)
CRYSTAL
rule.scopes.size.should eq 1
end
it "creates a scope for the proc" do
rule = ScopeRule.new
ScopeVisitor.new rule, Source.new %(
ScopeVisitor.new rule, Source.new <<-CRYSTAL
-> {}
)
CRYSTAL
rule.scopes.size.should eq 1
end
it "creates a scope for the block" do
rule = ScopeRule.new
ScopeVisitor.new rule, Source.new %(
ScopeVisitor.new rule, Source.new <<-CRYSTAL
3.times {}
)
CRYSTAL
rule.scopes.size.should eq 2
end
context "inner scopes" do
it "creates scope for block inside def" do
rule = ScopeRule.new
ScopeVisitor.new rule, Source.new %(
ScopeVisitor.new rule, Source.new <<-CRYSTAL
def method
3.times {}
end
)
CRYSTAL
rule.scopes.size.should eq 2
rule.scopes.last.outer_scope.should_not be_nil
rule.scopes.first.outer_scope.should eq rule.scopes.last
@ -42,11 +53,11 @@ module Ameba::AST
it "creates scope for block inside block" do
rule = ScopeRule.new
ScopeVisitor.new rule, Source.new %(
ScopeVisitor.new rule, Source.new <<-CRYSTAL
3.times do
2.times {}
end
)
CRYSTAL
rule.scopes.size.should eq 3
inner_block = rule.scopes.first
outer_block = rule.scopes.last
@ -54,5 +65,33 @@ module Ameba::AST
outer_block.outer_scope.should be_nil
end
end
context "#visibility" do
it "is being properly set" do
rule = ScopeRule.new
ScopeVisitor.new rule, Source.new <<-CRYSTAL
private class Foo
end
CRYSTAL
rule.scopes.size.should eq 1
rule.scopes.first.visibility.should eq Crystal::Visibility::Private
end
it "is being inherited from the outer scope(s)" do
rule = ScopeRule.new
ScopeVisitor.new rule, Source.new <<-CRYSTAL
private class Foo
class Bar
def baz
end
end
end
CRYSTAL
rule.scopes.size.should eq 3
rule.scopes.each &.visibility.should eq Crystal::Visibility::Private
rule.scopes.last.node.visibility.should eq Crystal::Visibility::Private
rule.scopes[0...-1].each &.node.visibility.should eq Crystal::Visibility::Public
end
end
end
end

View file

@ -4,10 +4,12 @@ module Ameba::AST
describe TopLevelNodesVisitor do
describe "#require_nodes" do
it "returns require node" do
source = Source.new %(
source = Source.new <<-CRYSTAL
require "foo"
def bar; end
)
def bar
end
CRYSTAL
visitor = TopLevelNodesVisitor.new(source.ast)
visitor.require_nodes.size.should eq 1
visitor.require_nodes.first.to_s.should eq %q(require "foo")

View file

@ -10,14 +10,16 @@ module Ameba::Rule
end
it "contains rules across all the available groups" do
Rule.rules.map(&.group_name).uniq!.reject!(&.empty?).sort.should eq %w(
Rule.rules.map(&.group_name).uniq!.reject!(&.empty?).sort.should eq %w[
Ameba
Documentation
Layout
Lint
Metrics
Naming
Performance
Style
)
]
end
end
@ -25,13 +27,17 @@ module Ameba::Rule
subject = DummyRule.new
it "is enabled by default" do
subject.enabled.should be_true
subject.enabled?.should be_true
end
it "has a description property" do
subject.description.should_not be_nil
end
it "has a dummy? property" do
subject.dummy?.should be_true
end
it "has excluded property" do
subject.excluded.should be_nil
end
@ -44,25 +50,25 @@ module Ameba::Rule
it "returns false if source is not excluded from this rule" do
rule = DummyRule.new
rule.excluded = %w(some_source.cr)
rule.excluded = %w[some_source.cr]
rule.excluded?(Source.new "", "another_source.cr").should_not be_true
end
it "returns true if source is excluded from this rule" do
rule = DummyRule.new
rule.excluded = %w(source.cr)
rule.excluded = %w[source.cr]
rule.excluded?(Source.new "", "source.cr").should be_true
end
it "returns true if source matches the wildcard" do
rule = DummyRule.new
rule.excluded = %w(**/*.cr)
rule.excluded = %w[**/*.cr]
rule.excluded?(Source.new "", __FILE__).should be_true
end
it "returns false if source does not match the wildcard" do
rule = DummyRule.new
rule.excluded = %w(*_spec.cr)
rule.excluded = %w[*_spec.cr]
rule.excluded?(Source.new "", "source.cr").should be_false
end
end

View file

@ -5,92 +5,97 @@ module Ameba::Cli
describe "Cmd" do
describe ".run" do
it "runs ameba" do
r = Cli.run %w(-f silent file.cr)
r = Cli.run %w[-f silent file.cr]
r.should be_nil
end
end
describe ".parse_args" do
%w(-s --silent).each do |f|
it "accepts #{f} flag" do
c = Cli.parse_args [f]
%w[-s --silent].each do |flag|
it "accepts #{flag} flag" do
c = Cli.parse_args [flag]
c.formatter.should eq :silent
end
end
%w(-c --config).each do |f|
it "accepts #{f} flag" do
c = Cli.parse_args [f, "config.yml"]
c.config.should eq "config.yml"
%w[-c --config].each do |flag|
it "accepts #{flag} flag" do
c = Cli.parse_args [flag, "config.yml"]
c.config.should eq Path["config.yml"]
end
end
%w(-f --format).each do |f|
it "accepts #{f} flag" do
c = Cli.parse_args [f, "my-formatter"]
%w[-f --format].each do |flag|
it "accepts #{flag} flag" do
c = Cli.parse_args [flag, "my-formatter"]
c.formatter.should eq "my-formatter"
end
end
it "accepts --only flag" do
c = Cli.parse_args ["--only", "RULE1,RULE2"]
c.only.should eq %w(RULE1 RULE2)
c.only.should eq %w[RULE1 RULE2]
end
it "accepts --except flag" do
c = Cli.parse_args ["--except", "RULE1,RULE2"]
c.except.should eq %w(RULE1 RULE2)
c.except.should eq %w[RULE1 RULE2]
end
it "defaults rules? flag to false" do
c = Cli.parse_args %w(file.cr)
c.rules?.should eq false
c = Cli.parse_args %w[file.cr]
c.rules?.should be_false
end
it "defaults skip_reading_config? flag to false" do
c = Cli.parse_args %w[file.cr]
c.skip_reading_config?.should be_false
end
it "accepts --rules flag" do
c = Cli.parse_args %w(--rules)
c = Cli.parse_args %w[--rules]
c.rules?.should eq true
end
it "defaults all? flag to false" do
c = Cli.parse_args %w(file.cr)
c.all?.should eq false
c = Cli.parse_args %w[file.cr]
c.all?.should be_false
end
it "accepts --all flag" do
c = Cli.parse_args %w(--all)
c = Cli.parse_args %w[--all]
c.all?.should eq true
end
it "accepts --gen-config flag" do
c = Cli.parse_args %w(--gen-config)
c = Cli.parse_args %w[--gen-config]
c.formatter.should eq :todo
end
it "accepts --no-color flag" do
c = Cli.parse_args %w(--no-color)
c = Cli.parse_args %w[--no-color]
c.colors?.should be_false
end
it "accepts --without-affected-code flag" do
c = Cli.parse_args %w(--without-affected-code)
c = Cli.parse_args %w[--without-affected-code]
c.without_affected_code?.should be_true
end
it "doesn't disable colors by default" do
c = Cli.parse_args %w(--all)
c = Cli.parse_args %w[--all]
c.colors?.should be_true
end
it "ignores --config if --gen-config flag passed" do
c = Cli.parse_args %w(--gen-config --config my_config.yml)
c = Cli.parse_args %w[--gen-config --config my_config.yml]
c.formatter.should eq :todo
c.config.should eq ""
c.skip_reading_config?.should be_true
end
describe "-e/--explain" do
it "configures file/line/column" do
c = Cli.parse_args %w(--explain src/file.cr:3:5)
c = Cli.parse_args %w[--explain src/file.cr:3:5]
location_to_explain = c.location_to_explain.should_not be_nil
location_to_explain[:file].should eq "src/file.cr"
@ -100,59 +105,59 @@ module Ameba::Cli
it "raises an error if location is not valid" do
expect_raises(Exception, "location should have PATH:line:column") do
Cli.parse_args %w(--explain src/file.cr:3)
Cli.parse_args %w[--explain src/file.cr:3]
end
end
it "raises an error if line number is not valid" do
expect_raises(Exception, "location should have PATH:line:column") do
Cli.parse_args %w(--explain src/file.cr:a:3)
Cli.parse_args %w[--explain src/file.cr:a:3]
end
end
it "raises an error if column number is not valid" do
expect_raises(Exception, "location should have PATH:line:column") do
Cli.parse_args %w(--explain src/file.cr:3:&)
Cli.parse_args %w[--explain src/file.cr:3:&]
end
end
it "raises an error if line/column are missing" do
expect_raises(Exception, "location should have PATH:line:column") do
Cli.parse_args %w(--explain src/file.cr)
Cli.parse_args %w[--explain src/file.cr]
end
end
end
context "--fail-level" do
it "configures fail level Convention" do
c = Cli.parse_args %w(--fail-level convention)
c = Cli.parse_args %w[--fail-level convention]
c.fail_level.should eq Severity::Convention
end
it "configures fail level Warning" do
c = Cli.parse_args %w(--fail-level Warning)
c = Cli.parse_args %w[--fail-level Warning]
c.fail_level.should eq Severity::Warning
end
it "configures fail level Error" do
c = Cli.parse_args %w(--fail-level error)
c = Cli.parse_args %w[--fail-level error]
c.fail_level.should eq Severity::Error
end
it "raises if fail level is incorrect" do
expect_raises(Exception, "Incorrect severity name JohnDoe") do
Cli.parse_args %w(--fail-level JohnDoe)
Cli.parse_args %w[--fail-level JohnDoe]
end
end
end
it "accepts unknown args as globs" do
c = Cli.parse_args %w(source1.cr source2.cr)
c.globs.should eq %w(source1.cr source2.cr)
c = Cli.parse_args %w[source1.cr source2.cr]
c.globs.should eq %w[source1.cr source2.cr]
end
it "accepts one unknown arg as explain location if it has correct format" do
c = Cli.parse_args %w(source.cr:3:22)
c = Cli.parse_args %w[source.cr:3:22]
location_to_explain = c.location_to_explain.should_not be_nil
location_to_explain[:file].should eq "source.cr"
@ -166,7 +171,7 @@ module Ameba::Cli
c.globs.should be_nil
c.only.should be_nil
c.except.should be_nil
c.config.should eq Config::PATH
c.config.should be_nil
end
end
end

View file

@ -2,7 +2,7 @@ require "../spec_helper"
module Ameba
describe Config do
config_sample = "config/ameba.yml"
config_sample = "spec/fixtures/config.yml"
it "should have a list of available formatters" do
Config::AVAILABLE_FORMATTERS.should_not be_nil
@ -21,7 +21,7 @@ module Ameba
Globs: src/*.cr
CONFIG
config = Config.new(yml)
config.globs.should eq %w(src/*.cr)
config.globs.should eq %w[src/*.cr]
end
it "initializes globs as array" do
@ -32,7 +32,7 @@ module Ameba
- "!spec"
CONFIG
config = Config.new(yml)
config.globs.should eq %w(src/*.cr !spec)
config.globs.should eq %w[src/*.cr !spec]
end
it "raises if Globs has a wrong type" do
@ -51,7 +51,7 @@ module Ameba
Excluded: spec
CONFIG
config = Config.new(yml)
config.excluded.should eq %w(spec)
config.excluded.should eq %w[spec]
end
it "initializes excluded as array" do
@ -62,7 +62,7 @@ module Ameba
- lib/*.cr
CONFIG
config = Config.new(yml)
config.excluded.should eq %w(spec lib/*.cr)
config.excluded.should eq %w[spec lib/*.cr]
end
it "raises if Excluded has a wrong type" do
@ -84,6 +84,12 @@ module Ameba
config.formatter.should_not be_nil
end
it "raises when custom config file doesn't exist" do
expect_raises(Exception, "Unable to load config file: Config file does not exist") do
Config.load "foo.yml"
end
end
it "loads default config" do
config = Config.load
config.should_not be_nil
@ -127,13 +133,13 @@ module Ameba
config.sources.any?(&.fullpath.==(__FILE__)).should be_true
end
it "returns a list of sources mathing globs" do
config.globs = %w(**/config_spec.cr)
it "returns a list of sources matching globs" do
config.globs = %w[**/config_spec.cr]
config.sources.size.should eq(1)
end
it "returns a lisf of sources excluding 'Excluded'" do
config.excluded = %w(**/config_spec.cr)
it "returns a list of sources excluding 'Excluded'" do
config.excluded = %w[**/config_spec.cr]
config.sources.any?(&.fullpath.==(__FILE__)).should be_false
end
end
@ -170,12 +176,12 @@ module Ameba
name = DummyRule.rule_name
config.update_rule name, enabled: false
rule = config.rules.find!(&.name.== name)
rule.enabled.should be_false
rule.enabled?.should be_false
end
it "updates excluded property" do
name = DummyRule.rule_name
excluded = %w(spec/source.cr)
excluded = %w[spec/source.cr]
config.update_rule name, excluded: excluded
rule = config.rules.find!(&.name.== name)
rule.excluded.should eq excluded
@ -189,12 +195,12 @@ module Ameba
name = DummyRule.rule_name
config.update_rules [name], enabled: false
rule = config.rules.find!(&.name.== name)
rule.enabled.should be_false
rule.enabled?.should be_false
end
it "updates multiple rules by excluded property" do
name = DummyRule.rule_name
excluded = %w(spec/source.cr)
excluded = %w[spec/source.cr]
config.update_rules [name], excluded: excluded
rule = config.rules.find!(&.name.== name)
rule.excluded.should eq excluded
@ -204,12 +210,12 @@ module Ameba
group = DummyRule.group_name
config.update_rules [group], enabled: false
rule = config.rules.find!(&.name.== DummyRule.rule_name)
rule.enabled.should be_false
rule.enabled?.should be_false
end
it "updates a group by excluded property" do
name = DummyRule.group_name
excluded = %w(spec/source.cr)
excluded = %w[spec/source.cr]
config.update_rules [name], excluded: excluded
rule = config.rules.find!(&.name.== DummyRule.rule_name)
rule.excluded.should eq excluded

View file

@ -59,7 +59,7 @@ module Ameba
source = Source.new "a = 42", "source.cr"
output = explanation(source)
output.should contain "DETAILED DESCRIPTION"
output.should contain "TO BE DONE..."
output.should contain "Rule extended description"
end
it "writes nothing if location not found" do

View file

@ -1,43 +1,54 @@
require "../../spec_helper"
require "file_utils"
CONFIG_PATH = Path[Dir.tempdir] / Ameba::Config::FILENAME
module Ameba
private def with_formatter(&)
io = IO::Memory.new
formatter = Formatter::TODOFormatter.new(io, CONFIG_PATH)
yield formatter, io
end
private def create_todo
formatter = Formatter::TODOFormatter.new IO::Memory.new
with_formatter do |formatter|
s = Source.new "a = 1", "source.cr"
s.add_issue DummyRule.new, {1, 2}, "message"
file = formatter.finished([s])
file ? File.read(file.path) : ""
end
end
describe Formatter::TODOFormatter do
::Spec.after_each do
FileUtils.rm(Ameba::Config::PATH) if File.exists?(Ameba::Config::PATH)
FileUtils.rm_rf(CONFIG_PATH)
end
context "problems not found" do
it "does not create file" do
formatter = Formatter::TODOFormatter.new IO::Memory.new
with_formatter do |formatter|
file = formatter.finished [Source.new ""]
file.should be_nil
end
end
it "reports a message saying file is not created" do
io = IO::Memory.new
formatter = Formatter::TODOFormatter.new io
with_formatter do |formatter, io|
formatter.finished [Source.new ""]
io.to_s.should contain "No issues found. File is not generated"
end
end
end
context "problems found" do
it "prints a message saying file is created" do
io = IO::Memory.new
formatter = Formatter::TODOFormatter.new io
with_formatter do |formatter, io|
s = Source.new "a = 1", "source.cr"
s.add_issue DummyRule.new, {1, 2}, "message"
formatter.finished([s])
io.to_s.should contain "Created .ameba.yml"
io.to_s.should contain "Created #{CONFIG_PATH}"
end
end
it "creates a valid YAML document" do
@ -77,8 +88,8 @@ module Ameba
end
context "with multiple issues" do
formatter = Formatter::TODOFormatter.new IO::Memory.new
it "does generate todo file" do
with_formatter do |formatter|
s1 = Source.new "a = 1", "source1.cr"
s2 = Source.new "a = 1", "source2.cr"
s1.add_issue DummyRule.new, {1, 2}, "message1"
@ -93,6 +104,7 @@ module Ameba
# Run `ameba --only Ameba/DummyRule` for details
Ameba/DummyRule:
Description: Dummy rule that does nothing.
Dummy: true
Excluded:
- source1.cr
- source2.cr
@ -100,20 +112,22 @@ module Ameba
Severity: Convention
CONTENT
end
end
end
context "when invalid syntax" do
it "does generate todo file" do
formatter = Formatter::TODOFormatter.new IO::Memory.new
with_formatter do |formatter|
s = Source.new "def invalid_syntax"
s.add_issue Rule::Lint::Syntax.new, {1, 2}, "message"
file = formatter.finished [s]
file.should be_nil
end
end
it "prints an error message" do
io = IO::Memory.new
formatter = Formatter::TODOFormatter.new io
with_formatter do |formatter, io|
s = Source.new "def invalid_syntax"
s.add_issue Rule::Lint::Syntax.new, {1, 2}, "message"
@ -125,3 +139,4 @@ module Ameba
end
end
end
end

View file

@ -39,7 +39,7 @@ module Ameba::Formatter
describe "#context" do
it "returns correct pre/post context lines" do
source = Source.new <<-EOF
source = Source.new <<-CRYSTAL
# pre:1
# pre:2
# pre:3
@ -51,7 +51,7 @@ module Ameba::Formatter
# post:3
# post:4
# post:5
EOF
CRYSTAL
subject.context(source.lines, lineno: 6, context_lines: 3)
.should eq({<<-PRE.lines, <<-POST.lines
@ -69,24 +69,24 @@ module Ameba::Formatter
describe "#affected_code" do
it "returns nil if there is no such a line number" do
code = <<-EOF
code = <<-CRYSTAL
a = 1
EOF
CRYSTAL
location = Crystal::Location.new("filename", 2, 1)
subject.affected_code(code, location).should be_nil
end
it "returns correct line if it is found" do
code = <<-EOF
code = <<-CRYSTAL
a = 1
EOF
CRYSTAL
location = Crystal::Location.new("filename", 1, 1)
subject.deansify(subject.affected_code(code, location))
.should eq "> a = 1\n ^\n"
end
it "returns correct line if it is found" do
code = <<-EOF
code = <<-CRYSTAL
# pre:1
# pre:2
# pre:3
@ -98,7 +98,7 @@ module Ameba::Formatter
# post:3
# post:4
# post:5
EOF
CRYSTAL
location = Crystal::Location.new("filename", 6, 1)
subject.deansify(subject.affected_code(code, location, context_lines: 3))

View file

@ -1,11 +1,7 @@
require "../spec_helper"
module Ameba
struct GlobUtilsClass
include GlobUtils
end
subject = GlobUtilsClass.new
subject = GlobUtils
current_file_basename = File.basename(__FILE__)
current_file_path = "spec/ameba/#{current_file_basename}"
@ -45,6 +41,12 @@ module Ameba
subject.expand(["**/#{current_file_basename}", "**/#{current_file_basename}"])
.should eq [current_file_path]
end
it "does not list folders" do
subject.expand(["**/*"]).each do |path|
fail "#{path.inspect} should be a file" unless File.file?(path)
end
end
end
end
end

View file

@ -26,135 +26,135 @@ module Ameba
end
it "disables a rule with a comment directive" do
s = Source.new %Q(
source = Source.new <<-CRYSTAL
# ameba:disable #{NamedRule.name}
Time.epoch(1483859302)
)
s.add_issue(NamedRule.new, location: {1, 12}, message: "Error!")
s.should be_valid
CRYSTAL
source.add_issue(NamedRule.new, location: {1, 12}, message: "Error!")
source.should be_valid
end
it "disables a rule with a line that ends with a comment directive" do
s = Source.new %Q(
source = Source.new <<-CRYSTAL
Time.epoch(1483859302) # ameba:disable #{NamedRule.name}
)
s.add_issue(NamedRule.new, location: {1, 12}, message: "Error!")
s.should be_valid
CRYSTAL
source.add_issue(NamedRule.new, location: {1, 12}, message: "Error!")
source.should be_valid
end
it "does not disable a rule of a different name" do
s = Source.new %Q(
source = Source.new <<-CRYSTAL
# ameba:disable WrongName
Time.epoch(1483859302)
)
s.add_issue(NamedRule.new, location: {2, 12}, message: "Error!")
s.should_not be_valid
CRYSTAL
source.add_issue(NamedRule.new, location: {2, 12}, message: "Error!")
source.should_not be_valid
end
it "disables a rule if multiple rule names provided" do
s = Source.new %Q(
source = Source.new <<-CRYSTAL
# ameba:disable SomeRule LargeNumbers #{NamedRule.name} SomeOtherRule
Time.epoch(1483859302)
)
s.add_issue(NamedRule.new, location: {2, 12}, message: "")
s.should be_valid
CRYSTAL
source.add_issue(NamedRule.new, location: {2, 12}, message: "")
source.should be_valid
end
it "disables a rule if multiple rule names are separated by comma" do
s = Source.new %Q(
source = Source.new <<-CRYSTAL
# ameba:disable SomeRule, LargeNumbers, #{NamedRule.name}, SomeOtherRule
Time.epoch(1483859302)
)
s.add_issue(NamedRule.new, location: {2, 12}, message: "")
s.should be_valid
CRYSTAL
source.add_issue(NamedRule.new, location: {2, 12}, message: "")
source.should be_valid
end
it "does not disable if multiple rule names used without required one" do
s = Source.new %(
source = Source.new <<-CRYSTAL
# ameba:disable SomeRule, SomeOtherRule LargeNumbers
Time.epoch(1483859302)
)
s.add_issue(NamedRule.new, location: {2, 12}, message: "")
s.should_not be_valid
CRYSTAL
source.add_issue(NamedRule.new, location: {2, 12}, message: "")
source.should_not be_valid
end
it "does not disable if comment directive has wrong place" do
s = Source.new %Q(
source = Source.new <<-CRYSTAL
# ameba:disable #{NamedRule.name}
#
Time.epoch(1483859302)
)
s.add_issue(NamedRule.new, location: {3, 12}, message: "")
s.should_not be_valid
CRYSTAL
source.add_issue(NamedRule.new, location: {3, 12}, message: "")
source.should_not be_valid
end
it "does not disable if comment directive added to the wrong line" do
s = Source.new %Q(
source = Source.new <<-CRYSTAL
if use_epoch? # ameba:disable #{NamedRule.name}
Time.epoch(1483859302)
end
)
s.add_issue(NamedRule.new, location: {3, 12}, message: "")
s.should_not be_valid
CRYSTAL
source.add_issue(NamedRule.new, location: {3, 12}, message: "")
source.should_not be_valid
end
it "does not disable if that is not a comment directive" do
s = Source.new %Q(
source = Source.new <<-CRYSTAL
"ameba:disable #{NamedRule.name}"
Time.epoch(1483859302)
)
s.add_issue(NamedRule.new, location: {3, 12}, message: "")
s.should_not be_valid
CRYSTAL
source.add_issue(NamedRule.new, location: {3, 12}, message: "")
source.should_not be_valid
end
it "does not disable if that is a commented out directive" do
s = Source.new %Q(
source = Source.new <<-CRYSTAL
# # ameba:disable #{NamedRule.name}
Time.epoch(1483859302)
)
s.add_issue(NamedRule.new, location: {3, 12}, message: "")
s.should_not be_valid
CRYSTAL
source.add_issue(NamedRule.new, location: {3, 12}, message: "")
source.should_not be_valid
end
it "does not disable if that is an inline commented out directive" do
s = Source.new %Q(
source = Source.new <<-CRYSTAL
a = 1 # Disable it: # ameba:disable #{NamedRule.name}
)
s.add_issue(NamedRule.new, location: {2, 12}, message: "")
s.should_not be_valid
CRYSTAL
source.add_issue(NamedRule.new, location: {2, 12}, message: "")
source.should_not be_valid
end
context "with group name" do
it "disables one rule with a group" do
s = Source.new %Q(
source = Source.new <<-CRYSTAL
a = 1 # ameba:disable #{DummyRule.rule_name}
)
s.add_issue(DummyRule.new, location: {1, 12}, message: "")
s.should be_valid
CRYSTAL
source.add_issue(DummyRule.new, location: {1, 12}, message: "")
source.should be_valid
end
it "doesn't disable others rules" do
s = Source.new %Q(
source = Source.new <<-CRYSTAL
a = 1 # ameba:disable #{DummyRule.rule_name}
)
s.add_issue(NamedRule.new, location: {2, 12}, message: "")
s.should_not be_valid
CRYSTAL
source.add_issue(NamedRule.new, location: {2, 12}, message: "")
source.should_not be_valid
end
it "disables a hole group of rules" do
s = Source.new %Q(
source = Source.new <<-CRYSTAL
a = 1 # ameba:disable #{DummyRule.group_name}
)
s.add_issue(DummyRule.new, location: {1, 12}, message: "")
s.should be_valid
CRYSTAL
source.add_issue(DummyRule.new, location: {1, 12}, message: "")
source.should be_valid
end
it "does not disable rules which do not belong to the group" do
s = Source.new %Q(
source = Source.new <<-CRYSTAL
a = 1 # ameba:disable Lint
)
s.add_issue(DummyRule.new, location: {2, 12}, message: "")
s.should_not be_valid
CRYSTAL
source.add_issue(DummyRule.new, location: {2, 12}, message: "")
source.should_not be_valid
end
end
end

View file

@ -0,0 +1,32 @@
require "../../spec_helper"
module Ameba
private def with_rule_collection_presenter(&)
with_presenter(Presenter::RuleCollectionPresenter) do |presenter, io|
rules = Config.load.rules
presenter.run(rules)
output = io.to_s
output = Formatter::Util.deansify(output).to_s
yield rules, output, presenter
end
end
describe Presenter::RuleCollectionPresenter do
it "outputs rule collection details" do
with_rule_collection_presenter do |rules, output|
rules.each do |rule|
output.should contain rule.name
output.should contain rule.severity.symbol
if description = rule.description
output.should contain description
end
end
output.should contain "Total rules: #{rules.size}"
output.should match /\d+ enabled/
end
end
end
end

View file

@ -0,0 +1,30 @@
require "../../spec_helper"
module Ameba
private def rule_presenter_each_rule(&)
with_presenter(Presenter::RulePresenter) do |presenter, io|
rules = Config.load.rules
rules.each do |rule|
presenter.run(rule)
output = io.to_s
output = Formatter::Util.deansify(output).to_s
yield rule, output, presenter
end
end
end
describe Presenter::RulePresenter do
it "outputs rule details" do
rule_presenter_each_rule do |rule, output|
output.should contain rule.name
output.should contain rule.severity.to_s
if description = rule.description
output.should contain description
end
end
end
end
end

View file

@ -0,0 +1,113 @@
require "../../../spec_helper"
module Ameba::Rule::Documentation
subject = DocumentationAdmonition.new
describe DocumentationAdmonition do
it "passes for comments with admonition mid-word/sentence" do
subject.admonitions.each do |admonition|
expect_no_issues subject, <<-CRYSTAL
# Mentioning #{admonition} mid-sentence
# x#{admonition}x
# x#{admonition}
# #{admonition}x
CRYSTAL
end
end
it "fails for comments with admonition" do
subject.admonitions.each do |admonition|
expect_issue subject, <<-CRYSTAL
# #{admonition}: Single-line comment
# ^{} error: Found a #{admonition} admonition in a comment
CRYSTAL
expect_issue subject, <<-CRYSTAL
# Text before ...
# #{admonition}(some context): Part of multi-line comment
# ^{} error: Found a #{admonition} admonition in a comment
# Text after ...
CRYSTAL
expect_issue subject, <<-CRYSTAL
# #{admonition}
# ^{} error: Found a #{admonition} admonition in a comment
if rand > 0.5
end
CRYSTAL
end
end
context "with date" do
it "passes for admonitions with future date" do
subject.admonitions.each do |admonition|
future_date = (Time.utc + 21.days).to_s(format: "%F")
expect_no_issues subject, <<-CRYSTAL
# #{admonition}(#{future_date}): sth in the future
CRYSTAL
end
end
it "fails for admonitions with past date" do
subject.admonitions.each do |admonition|
past_date = (Time.utc - 21.days).to_s(format: "%F")
expect_issue subject, <<-CRYSTAL
# #{admonition}(#{past_date}): sth in the past
# ^{} error: Found a #{admonition} admonition in a comment (21 days past)
CRYSTAL
end
end
it "fails for admonitions with yesterday's date" do
subject.admonitions.each do |admonition|
yesterday_date = (Time.utc - 1.day).to_s(format: "%F")
expect_issue subject, <<-CRYSTAL
# #{admonition}(#{yesterday_date}): sth in the past
# ^{} error: Found a #{admonition} admonition in a comment (1 day past)
CRYSTAL
end
end
it "fails for admonitions with today's date" do
subject.admonitions.each do |admonition|
today_date = Time.utc.to_s(format: "%F")
expect_issue subject, <<-CRYSTAL
# #{admonition}(#{today_date}): sth in the past
# ^{} error: Found a #{admonition} admonition in a comment (today is the day!)
CRYSTAL
end
end
it "fails for admonitions with invalid date" do
subject.admonitions.each do |admonition|
expect_issue subject, <<-CRYSTAL
# #{admonition}(0000-00-00): sth wrong
# ^{} error: #{admonition} admonition error: Invalid time: "0000-00-00"
CRYSTAL
end
end
end
context "properties" do
describe "#admonitions" do
it "lets setting custom admonitions" do
rule = DocumentationAdmonition.new
rule.admonitions = %w[FOO BAR]
rule.admonitions.each do |admonition|
expect_issue rule, <<-CRYSTAL
# #{admonition}
# ^{} error: Found a #{admonition} admonition in a comment
CRYSTAL
end
subject.admonitions.each do |admonition|
expect_no_issues rule, <<-CRYSTAL
# #{admonition}
CRYSTAL
end
end
end
end
end
end

View file

@ -0,0 +1,151 @@
require "../../../spec_helper"
module Ameba::Rule::Documentation
subject = Documentation.new
.tap(&.ignore_classes = false)
.tap(&.ignore_modules = false)
.tap(&.ignore_enums = false)
.tap(&.ignore_defs = false)
.tap(&.ignore_macros = false)
describe Documentation do
it "passes for undocumented private types" do
expect_no_issues subject, <<-CRYSTAL
private class Foo
def foo
end
end
private module Bar
def bar
end
end
private enum Baz
end
private def bat
end
private macro bag
end
CRYSTAL
end
it "passes for documented public types" do
expect_no_issues subject, <<-CRYSTAL
# Foo
class Foo
# foo
def foo
end
end
# Bar
module Bar
# bar
def bar
end
end
# Baz
enum Baz
end
# bat
def bat
end
# bag
macro bag
end
CRYSTAL
end
it "fails if there is an undocumented public type" do
expect_issue subject, <<-CRYSTAL
class Foo
# ^^^^^^^^^ error: Missing documentation
end
module Bar
# ^^^^^^^^^^ error: Missing documentation
end
enum Baz
# ^^^^^^^^ error: Missing documentation
end
def bat
# ^^^^^^^ error: Missing documentation
end
macro bag
# ^^^^^^^^^ error: Missing documentation
end
CRYSTAL
end
context "properties" do
describe "#ignore_classes" do
it "lets the rule to ignore method definitions if true" do
rule = Documentation.new
rule.ignore_classes = true
expect_no_issues rule, <<-CRYSTAL
class Foo
end
CRYSTAL
end
end
describe "#ignore_modules" do
it "lets the rule to ignore method definitions if true" do
rule = Documentation.new
rule.ignore_modules = true
expect_no_issues rule, <<-CRYSTAL
module Bar
end
CRYSTAL
end
end
describe "#ignore_enums" do
it "lets the rule to ignore method definitions if true" do
rule = Documentation.new
rule.ignore_enums = true
expect_no_issues rule, <<-CRYSTAL
enum Baz
end
CRYSTAL
end
end
describe "#ignore_defs" do
it "lets the rule to ignore method definitions if true" do
rule = Documentation.new
rule.ignore_defs = true
expect_no_issues rule, <<-CRYSTAL
def bat
end
CRYSTAL
end
end
describe "#ignore_macros" do
it "lets the rule to ignore macros if true" do
rule = Documentation.new
rule.ignore_macros = true
expect_no_issues rule, <<-CRYSTAL
macro bag
end
CRYSTAL
end
end
end
end
end

View file

@ -34,7 +34,7 @@ module Ameba::Rule::Layout
end
context "properties" do
it "allows to configure max length of the line" do
it "#max_length" do
rule = LineLength.new
rule.max_length = long_line.size

View file

@ -5,7 +5,7 @@ module Ameba::Rule::Layout
describe TrailingWhitespace do
it "passes if all lines do not have trailing whitespace" do
expect_no_issues subject, "no-whispace"
expect_no_issues subject, "no-whitespace"
end
it "fails if there is a line with trailing whitespace" do
@ -15,16 +15,5 @@ module Ameba::Rule::Layout
expect_correction source, "whitespace at the end"
end
it "reports rule, pos and message" do
source = Source.new "a = 1\n b = 2 ", "source.cr"
subject.catch(source).should_not be_valid
issue = source.issues.first
issue.rule.should_not be_nil
issue.location.to_s.should eq "source.cr:2:7"
issue.end_location.to_s.should eq "source.cr:2:7"
issue.message.should eq "Trailing whitespace detected"
end
end
end

View file

@ -94,16 +94,6 @@ module Ameba::Rule::Lint
a
CRYSTAL
end
it "reports rule, pos and message" do
source = Source.new "a != true", "source.cr"
subject.catch(source)
issue = source.issues.first
issue.rule.should_not be_nil
issue.location.to_s.should eq "source.cr:1:1"
issue.message.should eq "Comparison to a boolean is pointless"
end
end
context "boolean on the left" do
@ -165,17 +155,6 @@ module Ameba::Rule::Lint
a
CRYSTAL
end
it "reports rule, pos and message" do
source = Source.new "true != a", "source.cr"
subject.catch(source).should_not be_valid
issue = source.issues.first
issue.rule.should_not be_nil
issue.location.to_s.should eq "source.cr:1:1"
issue.end_location.to_s.should eq "source.cr:1:9"
issue.message.should eq "Comparison to a boolean is pointless"
end
end
end
end

View file

@ -28,16 +28,5 @@ module Ameba::Rule::Lint
CRYSTAL
end
end
it "reports rule, pos and message" do
s = Source.new "pp! :foo", "source.cr"
subject.catch(s).should_not be_valid
issue = s.issues.first
issue.rule.should_not be_nil
issue.location.to_s.should eq "source.cr:1:1"
issue.end_location.to_s.should eq "source.cr:1:8"
issue.message.should eq "Possibly forgotten debug-related `pp!` call detected"
end
end
end

View file

@ -31,16 +31,5 @@ module Ameba::Rule::Lint
expect_no_corrections source
end
it "reports rule, pos and message" do
s = Source.new "debugger", "source.cr"
subject.catch(s).should_not be_valid
issue = s.issues.first
issue.rule.should_not be_nil
issue.location.to_s.should eq "source.cr:1:1"
issue.end_location.to_s.should eq "source.cr:1:8"
issue.message.should eq "Possible forgotten debugger statement detected"
end
end
end

View file

@ -17,33 +17,10 @@ module Ameba::Rule::Lint
require "big"
require "math"
require "big"
# ^{} error: Duplicated require of `big`
# ^^^^^^^^^^^ error: Duplicated require of `big`
CRYSTAL
expect_no_corrections source
end
it "reports rule, pos and message" do
source = Source.new %(
require "./thing"
require "./thing"
require "./another_thing"
require "./another_thing"
), "source.cr"
subject.catch(source).should_not be_valid
issue = source.issues.first
issue.rule.should_not be_nil
issue.location.to_s.should eq "source.cr:2:1"
issue.end_location.to_s.should eq ""
issue.message.should eq "Duplicated require of `./thing`"
issue = source.issues.last
issue.rule.should_not be_nil
issue.location.to_s.should eq "source.cr:4:1"
issue.end_location.to_s.should eq ""
issue.message.should eq "Duplicated require of `./another_thing`"
end
end
end

View file

@ -3,17 +3,17 @@ require "../../../spec_helper"
module Ameba
subject = Rule::Lint::EmptyExpression.new
def it_detects_empty_expression(code)
it "detects empty expression" do
s = Source.new code
private def it_detects_empty_expression(code, *, file = __FILE__, line = __LINE__)
it "detects empty expression #{code.inspect}", file, line do
source = Source.new code
rule = Rule::Lint::EmptyExpression.new
rule.catch(s).should_not be_valid
rule.catch(source).should_not be_valid, file: file, line: line
end
end
describe Rule::Lint::EmptyExpression do
it "passes if there is no empty expression" do
s = Source.new %(
expect_no_issues subject, <<-CRYSTAL
def method()
end
@ -30,8 +30,7 @@ module Ameba
begin "" end
[nil] << nil
)
subject.catch(s).should be_valid
CRYSTAL
end
it_detects_empty_expression %(())
@ -84,11 +83,6 @@ module Ameba
it_detects_empty_expression %(
begin; end
)
it_detects_empty_expression %(
begin
nil
end
)
it_detects_empty_expression %(
begin
()
@ -96,10 +90,10 @@ module Ameba
)
it "does not report empty expression in macro" do
s = Source.new %q(
expect_no_issues subject, <<-CRYSTAL
module MyModule
macro conditional_error_for_inline_callbacks
\{%
\\{%
raise ""
%}
end
@ -107,21 +101,7 @@ module Ameba
macro before_save(x = nil)
end
end
)
subject.catch(s).should be_valid
end
it "reports rule, location and message" do
s = Source.new %(
if ()
end
), "source.cr"
subject.catch(s).should_not be_valid
issue = s.issues.first
issue.rule.should_not be_nil
issue.location.to_s.should eq "source.cr:1:4"
issue.end_location.to_s.should eq "source.cr:1:5"
issue.message.should eq "Avoid empty expressions"
CRYSTAL
end
end
end

View file

@ -64,21 +64,5 @@ module Ameba::Rule::Lint
end
CRYSTAL
end
it "reports rule, message and location" do
s = Source.new %(
a = 1
loop do
# comment goes here
end
), "source.cr"
subject.catch(s).should_not be_valid
s.issues.size.should eq 1
issue = s.issues.first
issue.rule.should_not be_nil
issue.location.to_s.should eq "source.cr:2:1"
issue.end_location.to_s.should eq "source.cr:4:3"
issue.message.should eq EmptyLoop::MSG
end
end
end

View file

@ -0,0 +1,48 @@
require "../../../spec_helper"
module Ameba::Rule::Lint
describe Formatting do
subject = Formatting.new
it "passes if source is formatted" do
expect_no_issues subject, <<-CRYSTAL
def method(a, b)
a + b
end
CRYSTAL
end
it "reports if source is not formatted" do
expect_issue subject, <<-CRYSTAL
def method(a,b)
# ^{} error: Use built-in formatter to format this source
end
CRYSTAL
end
context "properties" do
context "#fail_on_error" do
it "passes on formatter errors by default" do
rule = Formatting.new
expect_no_issues rule, <<-CRYSTAL
def method(a, b)
a + b
CRYSTAL
end
it "reports on formatter errors when enabled" do
rule = Formatting.new
rule.fail_on_error = true
expect_issue rule, <<-CRYSTAL
def method(a, b)
a + b
# ^ error: Error while formatting: expecting identifier 'end', not 'EOF'
CRYSTAL
end
end
end
end
end

View file

@ -32,17 +32,5 @@ module Ameba::Rule::Lint
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: Duplicated keys in hash literal: "key1", "key2"
CRYSTAL
end
it "reports rule, location and message" do
s = Source.new %q(
h = {"a" => 1, "a" => 2}
), "source.cr"
subject.catch(s).should_not be_valid
issue = s.issues.first
issue.rule.should_not be_nil
issue.location.to_s.should eq "source.cr:1:5"
issue.end_location.to_s.should eq "source.cr:1:24"
issue.message.should eq %(Duplicated keys in hash literal: "a")
end
end
end

View file

@ -0,0 +1,47 @@
require "../../../spec_helper"
LITERAL_SAMPLES = {
nil, true, 42, 4.2, 'c', "foo", :foo, /foo/,
0..42, [1, 2, 3], {1, 2, 3},
{foo: :bar}, {:foo => :bar},
}
module Ameba::Rule::Lint
subject = LiteralAssignmentsInExpressions.new
describe LiteralAssignmentsInExpressions do
it "passes if the assignment value is not a literal" do
expect_no_issues subject, <<-CRYSTAL
if a = b
:ok
end
unless a = b.presence
:ok
end
:ok if a = b
:ok unless a = b
case {a, b}
when {0, 1} then :gt
when {1, 0} then :lt
end
CRYSTAL
end
{% for literal in LITERAL_SAMPLES %}
it %(reports if the assignment value is a {{ literal }} literal) do
expect_issue subject, <<-CRYSTAL, literal: {{ literal.stringify }}
raise "boo!" if foo = {{ literal }}
# ^{literal}^^^^^^ error: Detected assignment with a literal value in control expression
CRYSTAL
expect_issue subject, <<-CRYSTAL, literal: {{ literal.stringify }}
raise "boo!" unless foo = {{ literal }}
# ^{literal}^^^^^^ error: Detected assignment with a literal value in control expression
CRYSTAL
end
{% end %}
end
end

View file

@ -6,16 +6,20 @@ module Ameba::Rule::Lint
describe LiteralsComparison do
it "passes for valid cases" do
expect_no_issues subject, <<-CRYSTAL
{start.year, start.month} == {stop.year, stop.month}
["foo"] === [foo]
"foo" == foo
"foo" != foo
"foo" == FOO
FOO == "foo"
foo == "foo"
foo != "foo"
CRYSTAL
end
it "reports if there is a regex comparison possibly evaluating to the same" do
it "reports if there is a dynamic comparison possibly evaluating to the same" do
expect_issue subject, <<-CRYSTAL
/foo/ === "foo"
[foo] === [foo]
# ^^^^^^^^^^^^^ error: Comparison most likely evaluates to the same
CRYSTAL
end
@ -41,39 +45,22 @@ module Ameba::Rule::Lint
CRYSTAL
end
it "reports if there is a static path comparison evaluating to false" do
expect_issue subject, <<-CRYSTAL
String == Nil
# ^^^^^^^^^^^ error: Comparison always evaluates to false
CRYSTAL
end
context "macro" do
pending "reports in macro scope" do
it "reports in macro scope" do
expect_issue subject, <<-CRYSTAL
{{ "foo" == "foo" }}
# ^^^^^^^^^^^^^^ error: Comparison always evaluates to true
CRYSTAL
end
it "passes for free variables comparisons in macro scope" do
it "passes for valid cases" do
expect_no_issues subject, <<-CRYSTAL
{{ T == Nil }}
{{ "foo" == foo }}
{{ "foo" != foo }}
{% foo == "foo" %}
{% foo != "foo" %}
CRYSTAL
end
end
it "reports rule, pos and message" do
s = Source.new %(
"foo" == "foo"
), "source.cr"
subject.catch(s).should_not be_valid
issue = s.issues.first
issue.rule.should_not be_nil
issue.location.to_s.should eq "source.cr:1:1"
issue.end_location.to_s.should eq "source.cr:1:14"
issue.message.should eq "Comparison always evaluates to true"
end
end
end

View file

@ -0,0 +1,42 @@
require "../../../spec_helper"
module Ameba::Rule::Lint
subject = MissingBlockArgument.new
describe MissingBlockArgument do
it "passes if the block argument is defined" do
expect_no_issues subject, <<-CRYSTAL
def foo(&)
yield 42
end
def bar(&block)
yield 24
end
def baz(a, b, c, &block)
yield a, b, c
end
CRYSTAL
end
it "reports if the block argument is missing" do
expect_issue subject, <<-CRYSTAL
def foo
# ^^^ error: Missing anonymous block argument. Use `&` as an argument name to indicate yielding method.
yield 42
end
def bar
# ^^^ error: Missing anonymous block argument. Use `&` as an argument name to indicate yielding method.
yield 24
end
def baz(a, b, c)
# ^^^ error: Missing anonymous block argument. Use `&` as an argument name to indicate yielding method.
yield a, b, c
end
CRYSTAL
end
end
end

View file

@ -7,8 +7,11 @@ module Ameba::Rule::Lint
it "passes for valid cases" do
expect_no_issues subject, <<-CRYSTAL
(1..3).index(1).not_nil!(:foo)
(1..3).rindex(1).not_nil!(:foo)
(1..3).index { |i| i > 2 }.not_nil!(:foo)
(1..3).rindex { |i| i > 2 }.not_nil!(:foo)
(1..3).find { |i| i > 2 }.not_nil!(:foo)
/(.)(.)(.)/.match("abc", &.itself).not_nil!
CRYSTAL
end
@ -23,6 +26,28 @@ module Ameba::Rule::Lint
CRYSTAL
end
it "reports if there is an `rindex` call followed by `not_nil!`" do
source = expect_issue subject, <<-CRYSTAL
(1..3).rindex(1).not_nil!
# ^^^^^^^^^^^^^^^^^^ error: Use `rindex! {...}` instead of `rindex {...}.not_nil!`
CRYSTAL
expect_correction source, <<-CRYSTAL
(1..3).rindex!(1)
CRYSTAL
end
it "reports if there is an `match` call followed by `not_nil!`" do
source = expect_issue subject, <<-CRYSTAL
/(.)(.)(.)/.match("abc").not_nil![2]
# ^^^^^^^^^^^^^^^^^^^^^ error: Use `match! {...}` instead of `match {...}.not_nil!`
CRYSTAL
expect_correction source, <<-CRYSTAL
/(.)(.)(.)/.match!("abc")[2]
CRYSTAL
end
it "reports if there is an `index` call with block followed by `not_nil!`" do
source = expect_issue subject, <<-CRYSTAL
(1..3).index { |i| i > 2 }.not_nil!
@ -34,6 +59,17 @@ module Ameba::Rule::Lint
CRYSTAL
end
it "reports if there is an `rindex` call with block followed by `not_nil!`" do
source = expect_issue subject, <<-CRYSTAL
(1..3).rindex { |i| i > 2 }.not_nil!
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: Use `rindex! {...}` instead of `rindex {...}.not_nil!`
CRYSTAL
expect_correction source, <<-CRYSTAL
(1..3).rindex! { |i| i > 2 }
CRYSTAL
end
it "reports if there is a `find` call with block followed by `not_nil!`" do
source = expect_issue subject, <<-CRYSTAL
(1..3).find { |i| i > 2 }.not_nil!
@ -58,18 +94,5 @@ module Ameba::Rule::Lint
CRYSTAL
end
end
it "reports rule, pos and message" do
s = Source.new %(
(1..3).index(1).not_nil!
), "source.cr"
subject.catch(s).should_not be_valid
issue = s.issues.first
issue.rule.should_not be_nil
issue.location.to_s.should eq "source.cr:1:8"
issue.end_location.to_s.should eq "source.cr:1:24"
issue.message.should eq "Use `index! {...}` instead of `index {...}.not_nil!`"
end
end
end

View file

@ -18,6 +18,13 @@ module Ameba::Rule::Lint
CRYSTAL
end
it "reports if there is a `not_nil!` call in the middle of the call-chain" do
expect_issue subject, <<-CRYSTAL
(1..3).first?.not_nil!.to_s
# ^^^^^^^^ error: Avoid using `not_nil!`
CRYSTAL
end
context "macro" do
it "doesn't report in macro scope" do
expect_no_issues subject, <<-CRYSTAL
@ -25,18 +32,5 @@ module Ameba::Rule::Lint
CRYSTAL
end
end
it "reports rule, pos and message" do
s = Source.new %(
(1..3).first?.not_nil!
), "source.cr"
subject.catch(s).should_not be_valid
issue = s.issues.first
issue.rule.should_not be_nil
issue.location.to_s.should eq "source.cr:1:15"
issue.end_location.to_s.should eq "source.cr:1:22"
issue.message.should eq "Avoid using `not_nil!`"
end
end
end

View file

@ -6,41 +6,41 @@ module Ameba::Rule::Lint
it "passes if percent arrays are written correctly" do
s = Source.new %q(
%i(one two three)
%w(one two three)
%i[one two three]
%w[one two three]
%i(1 2 3)
%w(1 2 3)
%i[1 2 3]
%w[1 2 3]
%i()
%w()
%i[]
%w[]
)
subject.catch(s).should be_valid
end
it "fails if string percent array has commas" do
s = Source.new %( %w(one, two) )
s = Source.new %( %w[one, two] )
subject.catch(s).should_not be_valid
end
it "fails if string percent array has quotes" do
s = Source.new %( %w("one" "two") )
s = Source.new %( %w["one" "two"] )
subject.catch(s).should_not be_valid
end
it "fails if symbols percent array has commas" do
s = Source.new %( %i(one, two) )
s = Source.new %( %i[one, two] )
subject.catch(s).should_not be_valid
end
it "fails if symbols percent array has a colon" do
s = Source.new %( %i(:one :two) )
s = Source.new %( %i[:one :two] )
subject.catch(s).should_not be_valid
end
it "reports rule, location and message for %i" do
s = Source.new %(
%i(:one)
%i[:one]
), "source.cr"
subject.catch(s).should_not be_valid
@ -54,7 +54,7 @@ module Ameba::Rule::Lint
it "reports rule, location and message for %w" do
s = Source.new %(
%w("one")
%w["one"]
), "source.cr"
subject.catch(s).should_not be_valid
@ -68,17 +68,17 @@ module Ameba::Rule::Lint
end
context "properties" do
it "allows to configure string_array_unwanted_symbols" do
it "#string_array_unwanted_symbols" do
rule = PercentArrays.new
rule.string_array_unwanted_symbols = ","
s = Source.new %( %w("one") )
s = Source.new %( %w["one"] )
rule.catch(s).should be_valid
end
it "allows to configure symbol_array_unwanted_symbols" do
it "#symbol_array_unwanted_symbols" do
rule = PercentArrays.new
rule.symbol_array_unwanted_symbols = ","
s = Source.new %( %i(:one) )
s = Source.new %( %i[:one] )
rule.catch(s).should be_valid
end
end

View file

@ -25,16 +25,5 @@ module Ameba::Rule::Lint
# ^^^^^ error: rand(1) always returns 0
CRYSTAL
end
it "reports rule, location and a message" do
s = Source.new "rand(1)", "source.cr"
subject.catch(s).should_not be_valid
issue = s.issues.first
issue.rule.should_not be_nil
issue.location.to_s.should eq "source.cr:1:1"
issue.end_location.to_s.should eq "source.cr:1:7"
issue.message.should eq "rand(1) always returns 0"
end
end
end

View file

@ -4,21 +4,21 @@ module Ameba::Rule::Lint
describe RedundantStringCoercion do
subject = RedundantStringCoercion.new
it "does not report if there is no redundant string coersion" do
it "does not report if there is no redundant string coercion" do
s = Source.new %(
"Hello, #{name}"
)
subject.catch(s).should be_valid
end
it "reports if there is a redundant string coersion" do
it "reports if there is a redundant string coercion" do
s = Source.new %q(
"Hello, #{name.to_s}"
)
subject.catch(s).should_not be_valid
end
it "does not report if coersion is used in binary op" do
it "does not report if coercion is used in binary op" do
s = Source.new %q(
"Hello, #{3.to_s + 's'}"
)

View file

@ -77,7 +77,7 @@ module Ameba::Rule::Lint
issue = s.issues.first
issue.rule.should_not be_nil
issue.location.to_s.should eq "source.cr:2:19"
issue.end_location.to_s.should eq "source.cr:2:29"
issue.end_location.to_s.should eq "source.cr:2:28"
issue.message.should eq "Remove redundant with_index"
end
end
@ -155,7 +155,7 @@ module Ameba::Rule::Lint
issue = s.issues.first
issue.rule.should_not be_nil
issue.location.to_s.should eq "source.cr:2:14"
issue.end_location.to_s.should eq "source.cr:2:29"
issue.end_location.to_s.should eq "source.cr:2:28"
issue.message.should eq "Use each instead of each_with_index"
end
end

View file

@ -76,7 +76,7 @@ module Ameba::Rule::Lint
issue = s.issues.first
issue.rule.should_not be_nil
issue.location.to_s.should eq "source.cr:2:14"
issue.end_location.to_s.should eq "source.cr:2:30"
issue.end_location.to_s.should eq "source.cr:2:29"
issue.message.should eq "Use `each` instead of `each_with_object`"
end
end

View file

@ -31,6 +31,30 @@ module Ameba::Rule::Lint
CRYSTAL
end
pending "reports if there is a shadowing in an unpacked variable in a block" do
expect_issue subject, <<-CRYSTAL
def some_method
foo = 1
[{3}].each do |(foo)|
# ^ error: Shadowing outer local variable `foo`
end
end
CRYSTAL
end
pending "reports if there is a shadowing in an unpacked variable in a block (2)" do
expect_issue subject, <<-CRYSTAL
def some_method
foo = 1
[{[3]}].each do |((foo))|
# ^ error: Shadowing outer local variable `foo`
end
end
CRYSTAL
end
it "does not report outer vars declared below shadowed block" do
expect_no_issues subject, <<-CRYSTAL
methods = klass.methods.select { |m| m.annotation(MyAnn) }
@ -44,7 +68,7 @@ module Ameba::Rule::Lint
foo = 1
-> (foo : Int32) {}
# ^ error: Shadowing outer local variable `foo`
# ^^^^^^^^^^^ error: Shadowing outer local variable `foo`
end
CRYSTAL
end
@ -69,7 +93,7 @@ module Ameba::Rule::Lint
3.times do |foo|
# ^ error: Shadowing outer local variable `foo`
-> (foo : Int32) { foo + 1 }
# ^ error: Shadowing outer local variable `foo`
# ^^^^^^^^^^^ error: Shadowing outer local variable `foo`
end
CRYSTAL
end
@ -136,6 +160,19 @@ module Ameba::Rule::Lint
CRYSTAL
end
it "doesn't report if it shadows type declaration" do
expect_no_issues subject, <<-CRYSTAL
class FooBar
getter index : String
def bar
3.times do |index|
end
end
end
CRYSTAL
end
it "doesn't report if it shadows throwaway arguments" do
expect_no_issues subject, <<-CRYSTAL
data = [{1, "a"}, {2, "b"}, {3, "c"}]
@ -157,20 +194,6 @@ module Ameba::Rule::Lint
CRYSTAL
end
it "reports rule, location and message" do
source = Source.new %(
foo = 1
3.times { |foo| foo + 1 }
), "source.cr"
subject.catch(source).should_not be_valid
issue = source.issues.first
issue.rule.should_not be_nil
issue.location.to_s.should eq "source.cr:2:12"
issue.end_location.should be_nil
issue.message.should eq "Shadowing outer local variable `foo`"
end
context "macro" do
it "does not report shadowed vars in outer scope" do
expect_no_issues subject, <<-CRYSTAL

View file

@ -39,7 +39,7 @@ module Ameba::Rule::Lint
CRYSTAL
end
it "reports if there is a shared var in spawn" do
it "reports if there is a shared var in spawn (while)" do
source = expect_issue subject, <<-CRYSTAL
i = 0
while i < 10
@ -56,6 +56,24 @@ module Ameba::Rule::Lint
expect_no_corrections source
end
it "reports if there is a shared var in spawn (loop)" do
source = expect_issue subject, <<-CRYSTAL
i = 0
loop do
break if i >= 10
spawn do
puts(i)
# ^ error: Shared variable `i` is used in fiber
end
i += 1
end
Fiber.yield
CRYSTAL
expect_no_corrections source
end
it "reports reassigned reference to shared var in spawn" do
source = expect_issue subject, <<-CRYSTAL
channel = Channel(String).new
@ -194,24 +212,5 @@ module Ameba::Rule::Lint
end
CRYSTAL
end
it "reports rule, location and message" do
s = Source.new %(
i = 0
while true
i += 1
spawn { i }
end
), "source.cr"
subject.catch(s).should_not be_valid
s.issues.size.should eq 1
issue = s.issues.first
issue.rule.should_not be_nil
issue.location.to_s.should eq "source.cr:4:11"
issue.end_location.to_s.should eq "source.cr:4:11"
issue.message.should eq "Shared variable `i` is used in fiber"
end
end
end

View file

@ -0,0 +1,46 @@
require "../../../spec_helper"
module Ameba::Rule::Lint
subject = SpecFilename.new
describe SpecFilename do
it "passes if relative file path does not start with `spec/`" do
expect_no_issues subject, code: "", path: "src/spec/foo.cr"
expect_no_issues subject, code: "", path: "src/spec/foo/bar.cr"
end
it "passes if file extension is not `.cr`" do
expect_no_issues subject, code: "", path: "spec/foo.json"
expect_no_issues subject, code: "", path: "spec/foo/bar.json"
end
it "passes if filename is correct" do
expect_no_issues subject, code: "", path: "spec/foo_spec.cr"
expect_no_issues subject, code: "", path: "spec/foo/bar_spec.cr"
end
it "fails if filename is wrong" do
expect_issue subject, <<-CRYSTAL, path: "spec/foo.cr"
# ^{} error: Spec filename should have `_spec` suffix: foo_spec.cr, not foo.cr
CRYSTAL
end
context "properties" do
context "#ignored_dirs" do
it "provide sane defaults" do
expect_no_issues subject, code: "", path: "spec/support/foo.cr"
expect_no_issues subject, code: "", path: "spec/fixtures/foo.cr"
expect_no_issues subject, code: "", path: "spec/data/foo.cr"
end
end
context "#ignored_filenames" do
it "ignores spec_helper by default" do
expect_no_issues subject, code: "", path: "spec/spec_helper.cr"
expect_no_issues subject, code: "", path: "spec/foo/spec_helper.cr"
end
end
end
end
end

View file

@ -115,12 +115,12 @@ module Ameba::Rule::Lint
first.rule.should_not be_nil
first.location.to_s.should eq "source_spec.cr:1:11"
first.end_location.to_s.should eq ""
first.end_location.to_s.should eq "source_spec.cr:1:21"
first.message.should eq "Focused spec item detected"
second.rule.should_not be_nil
second.location.to_s.should eq "source_spec.cr:2:13"
second.end_location.to_s.should eq ""
second.end_location.to_s.should eq "source_spec.cr:2:23"
second.message.should eq "Focused spec item detected"
end
end

View file

@ -23,16 +23,6 @@ module Ameba::Rule::Lint
CRYSTAL
end
it "reports rule, location and message" do
s = Source.new "def hello end", "source.cr"
subject.catch(s).should_not be_valid
issue = s.issues.first
issue.rule.should_not be_nil
issue.location.to_s.should eq "source.cr:1:11"
issue.message.should match /unexpected token: "?end"? \(expected ["'];["'] or newline\)/
end
it "has highest severity" do
subject.severity.should eq Severity::Error
end

View file

@ -0,0 +1,35 @@
require "../../../spec_helper"
private def check_typos_bin!
unless Ameba::Rule::Lint::Typos::BIN_PATH
pending! "`typos` executable is not available"
end
end
module Ameba::Rule::Lint
subject = Typos.new
.tap(&.fail_on_error = true)
describe Typos do
it "reports typos" do
check_typos_bin!
source = expect_issue subject, <<-CRYSTAL
# method with no arugments
# ^^^^^^^^^ error: Typo found: arugments -> arguments
def tpos
# ^^^^ error: Typo found: tpos -> typos
:otput
# ^^^^^ error: Typo found: otput -> output
end
CRYSTAL
expect_correction source, <<-CRYSTAL
# method with no arguments
def typos
:output
end
CRYSTAL
end
end
end

View file

@ -6,7 +6,7 @@ module Ameba::Rule::Lint
describe UnusedArgument do
it "doesn't report if arguments are used" do
s = Source.new %(
expect_no_issues subject, <<-CRYSTAL
def method(a, b, c)
a + b + c
end
@ -16,146 +16,158 @@ module Ameba::Rule::Lint
end
->(i : Int32) { i + 1 }
)
subject.catch(s).should be_valid
CRYSTAL
end
it "reports if method argument is unused" do
s = Source.new %(
source = expect_issue subject, <<-CRYSTAL
def method(a, b, c)
# ^ error: Unused argument `c`. If it's necessary, use `_c` as an argument name to indicate that it won't be used.
a + b
end
)
subject.catch(s).should_not be_valid
s.issues.first.message.should eq "Unused argument `c`. If it's necessary, use `_c` " \
"as an argument name to indicate that it won't be used."
CRYSTAL
expect_correction source, <<-CRYSTAL
def method(a, b, _c)
a + b
end
CRYSTAL
end
it "reports if block argument is unused" do
s = Source.new %(
source = expect_issue subject, <<-CRYSTAL
[1, 2].each_with_index do |a, i|
# ^ error: Unused argument `i`. [...]
a
end
)
subject.catch(s).should_not be_valid
s.issues.first.message.should eq "Unused argument `i`. If it's necessary, use `_` " \
"as an argument name to indicate that it won't be used."
CRYSTAL
expect_correction source, <<-CRYSTAL
[1, 2].each_with_index do |a, _|
a
end
CRYSTAL
end
it "reports if proc argument is unused" do
s = Source.new %(
source = expect_issue subject, <<-CRYSTAL
-> (a : Int32, b : String) do
# ^^^^^^^^^^ error: Unused argument `b`. If it's necessary, use `_b` as an argument name to indicate that it won't be used.
a = a + 1
end
)
subject.catch(s).should_not be_valid
s.issues.first.message.should eq "Unused argument `b`. If it's necessary, use `_b` " \
"as an argument name to indicate that it won't be used."
CRYSTAL
expect_correction source, <<-CRYSTAL
-> (a : Int32, _b : String) do
a = a + 1
end
CRYSTAL
end
it "reports multiple unused args" do
s = Source.new %(
source = expect_issue subject, <<-CRYSTAL
def method(a, b, c)
# ^ error: Unused argument `a`. If it's necessary, use `_a` as an argument name to indicate that it won't be used.
# ^ error: Unused argument `b`. If it's necessary, use `_b` as an argument name to indicate that it won't be used.
# ^ error: Unused argument `c`. If it's necessary, use `_c` as an argument name to indicate that it won't be used.
nil
end
)
subject.catch(s).should_not be_valid
s.issues[0].message.should eq "Unused argument `a`. If it's necessary, use `_a` " \
"as an argument name to indicate that it won't be used."
s.issues[1].message.should eq "Unused argument `b`. If it's necessary, use `_b` " \
"as an argument name to indicate that it won't be used."
s.issues[2].message.should eq "Unused argument `c`. If it's necessary, use `_c` " \
"as an argument name to indicate that it won't be used."
CRYSTAL
expect_correction source, <<-CRYSTAL
def method(_a, _b, _c)
nil
end
CRYSTAL
end
it "doesn't report if it is an instance var argument" do
s = Source.new %(
expect_no_issues subject, <<-CRYSTAL
class A
def method(@name)
end
end
)
subject.catch(s).should be_valid
CRYSTAL
end
it "doesn't report if a typed argument is used" do
s = Source.new %(
expect_no_issues subject, <<-CRYSTAL
def method(x : Int32)
3.times do
puts x
end
end
)
subject.catch(s).should be_valid
CRYSTAL
end
it "doesn't report if an argument with default value is used" do
s = Source.new %(
expect_no_issues subject, <<-CRYSTAL
def method(x = 1)
puts x
end
)
subject.catch(s).should be_valid
CRYSTAL
end
it "doesn't report if argument starts with a _" do
s = Source.new %(
expect_no_issues subject, <<-CRYSTAL
def method(_x)
end
)
subject.catch(s).should be_valid
CRYSTAL
end
it "doesn't report if it is a block and used" do
s = Source.new %(
expect_no_issues subject, <<-CRYSTAL
def method(&block)
block.call
end
)
subject.catch(s).should be_valid
CRYSTAL
end
it "reports if block arg is not used" do
s = Source.new %(
it "doesn't report if block arg is not used" do
expect_no_issues subject, <<-CRYSTAL
def method(&block)
end
)
subject.catch(s).should_not be_valid
CRYSTAL
end
it "reports if unused and there is yield" do
s = Source.new %(
it "doesn't report if unused and there is yield" do
expect_no_issues subject, <<-CRYSTAL
def method(&block)
yield 1
end
)
subject.catch(s).should_not be_valid
CRYSTAL
end
it "doesn't report if it's an anonymous block" do
expect_no_issues subject, <<-CRYSTAL
def method(&)
yield 1
end
CRYSTAL
end
it "doesn't report if variable is referenced implicitly" do
s = Source.new %(
expect_no_issues subject, <<-CRYSTAL
class Bar < Foo
def method(a, b)
super
end
end
)
subject.catch(s).should be_valid
CRYSTAL
end
it "doesn't report if arg if referenced in case" do
s = Source.new %(
expect_no_issues subject, <<-CRYSTAL
def foo(a)
case a
when /foo/
end
end
)
subject.catch(s).should be_valid
CRYSTAL
end
it "doesn't report if enum in a record" do
s = Source.new %(
expect_no_issues subject, <<-CRYSTAL
class Class
record Record do
enum Enum
@ -163,59 +175,49 @@ module Ameba::Rule::Lint
end
end
end
)
subject.catch(s).should be_valid
CRYSTAL
end
context "super" do
it "reports if variable is not referenced implicitly by super" do
s = Source.new %(
source = expect_issue subject, <<-CRYSTAL
class Bar < Foo
def method(a, b)
# ^ error: Unused argument `b`. If it's necessary, use `_b` as an argument name to indicate that it won't be used.
super a
end
end
)
subject.catch(s).should_not be_valid
s.issues.first.message.should eq "Unused argument `b`. If it's necessary, use `_b` " \
"as an argument name to indicate that it won't be used."
end
CRYSTAL
it "reports rule, location and message" do
s = Source.new %(
def method(a)
expect_correction source, <<-CRYSTAL
class Bar < Foo
def method(a, _b)
super a
end
), "source.cr"
subject.catch(s).should_not be_valid
issue = s.issues.first
issue.rule.should_not be_nil
issue.message.should eq "Unused argument `a`. If it's necessary, use `_a` " \
"as an argument name to indicate that it won't be used."
issue.location.to_s.should eq "source.cr:1:12"
end
CRYSTAL
end
end
context "macro" do
it "doesn't report if it is a used macro argument" do
s = Source.new %(
expect_no_issues subject, <<-CRYSTAL
macro my_macro(arg)
{% arg %}
end
)
subject.catch(s).should be_valid
CRYSTAL
end
it "doesn't report if it is a used macro block argument" do
s = Source.new %(
expect_no_issues subject, <<-CRYSTAL
macro my_macro(&block)
{% block %}
end
)
subject.catch(s).should be_valid
CRYSTAL
end
it "doesn't report used macro args with equal names in record" do
s = Source.new %(
expect_no_issues subject, <<-CRYSTAL
record X do
macro foo(a, b)
{{ a }} + {{ b }}
@ -225,12 +227,11 @@ module Ameba::Rule::Lint
{{ a }} + {{ b }} + {{ c }}
end
end
)
subject.catch(s).should be_valid
CRYSTAL
end
it "doesn't report used args in macro literals" do
s = Source.new %(
expect_no_issues subject, <<-CRYSTAL
def print(f : Array(U)) forall U
f.size.times do |i|
{% if U == Float64 %}
@ -240,65 +241,73 @@ module Ameba::Rule::Lint
{% end %}
end
end
)
subject.catch(s).should be_valid
CRYSTAL
end
end
context "properties" do
describe "#ignore_defs" do
it "lets the rule to ignore def scopes if true" do
subject.ignore_defs = true
s = Source.new %(
rule = UnusedArgument.new
rule.ignore_defs = true
expect_no_issues rule, <<-CRYSTAL
def method(a)
end
)
subject.catch(s).should be_valid
CRYSTAL
end
it "lets the rule not to ignore def scopes if false" do
subject.ignore_defs = false
s = Source.new %(
rule = UnusedArgument.new
rule.ignore_defs = false
expect_issue rule, <<-CRYSTAL
def method(a)
# ^ error: Unused argument `a`. If it's necessary, use `_a` as an argument name to indicate that it won't be used.
end
)
subject.catch(s).should_not be_valid
CRYSTAL
end
end
context "#ignore_blocks" do
it "lets the rule to ignore block scopes if true" do
subject.ignore_blocks = true
s = Source.new %(
rule = UnusedArgument.new
rule.ignore_blocks = true
expect_no_issues rule, <<-CRYSTAL
3.times { |i| puts "yo!" }
)
subject.catch(s).should be_valid
CRYSTAL
end
it "lets the rule not to ignore block scopes if false" do
subject.ignore_blocks = false
s = Source.new %(
rule = UnusedArgument.new
rule.ignore_blocks = false
expect_issue rule, <<-CRYSTAL
3.times { |i| puts "yo!" }
)
subject.catch(s).should_not be_valid
# ^ error: Unused argument `i`. If it's necessary, use `_` as an argument name to indicate that it won't be used.
CRYSTAL
end
end
context "#ignore_procs" do
it "lets the rule to ignore proc scopes if true" do
subject.ignore_procs = true
s = Source.new %(
rule = UnusedArgument.new
rule.ignore_procs = true
expect_no_issues rule, <<-CRYSTAL
->(a : Int32) {}
)
subject.catch(s).should be_valid
CRYSTAL
end
it "lets the rule not to ignore proc scopes if false" do
subject.ignore_procs = false
s = Source.new %(
rule = UnusedArgument.new
rule.ignore_procs = false
expect_issue rule, <<-CRYSTAL
->(a : Int32) {}
)
subject.catch(s).should_not be_valid
# ^^^^^^^^^ error: Unused argument `a`. If it's necessary, use `_a` as an argument name to indicate that it won't be used.
CRYSTAL
end
end
end

View file

@ -0,0 +1,130 @@
require "../../../spec_helper"
module Ameba::Rule::Lint
subject = UnusedBlockArgument.new
describe UnusedBlockArgument do
it "doesn't report if it is an instance var argument" do
expect_no_issues subject, <<-CRYSTAL
class A
def initialize(&@callback)
end
end
CRYSTAL
end
it "doesn't report if anonymous" do
expect_no_issues subject, <<-CRYSTAL
def method(a, b, c, &)
end
CRYSTAL
end
it "doesn't report if argument name starts with a `_`" do
expect_no_issues subject, <<-CRYSTAL
def method(a, b, c, &_block)
end
CRYSTAL
end
it "doesn't report if it is a block and used" do
expect_no_issues subject, <<-CRYSTAL
def method(a, b, c, &block)
block.call
end
CRYSTAL
end
it "reports if block arg is not used" do
source = expect_issue subject, <<-CRYSTAL
def method(a, b, c, &block)
# ^^^^^ error: Unused block argument `block`. [...]
end
CRYSTAL
expect_correction source, <<-CRYSTAL
def method(a, b, c, &_block)
end
CRYSTAL
end
it "reports if unused and there is yield" do
source = expect_issue subject, <<-CRYSTAL
def method(a, b, c, &block)
# ^^^^^ error: Use `&` as an argument name to indicate that it won't be referenced.
3.times do |i|
i.try do
yield i
end
end
end
CRYSTAL
expect_correction source, <<-CRYSTAL
def method(a, b, c, &)
3.times do |i|
i.try do
yield i
end
end
end
CRYSTAL
end
it "doesn't report if anonymous and there is yield" do
expect_no_issues subject, <<-CRYSTAL
def method(a, b, c, &)
yield 1
end
CRYSTAL
end
it "doesn't report if variable is referenced implicitly" do
expect_no_issues subject, <<-CRYSTAL
class Bar < Foo
def method(a, b, c, &block)
super
end
end
CRYSTAL
end
it "doesn't report if used in abstract def" do
expect_no_issues subject, <<-CRYSTAL
abstract def debug(id : String, &on_message: Callback)
abstract def info(&on_message: Callback)
CRYSTAL
end
context "super" do
it "reports if variable is not referenced implicitly by super" do
source = expect_issue subject, <<-CRYSTAL
class Bar < Foo
def method(a, b, c, &block)
# ^^^^^ error: Unused block argument `block`. [...]
super a, b, c
end
end
CRYSTAL
expect_correction source, <<-CRYSTAL
class Bar < Foo
def method(a, b, c, &_block)
super a, b, c
end
end
CRYSTAL
end
end
context "macro" do
it "doesn't report if it is a used macro block argument" do
expect_no_issues subject, <<-CRYSTAL
macro my_macro(&block)
{% block %}
end
CRYSTAL
end
end
end
end

File diff suppressed because it is too large Load diff

View file

@ -24,22 +24,5 @@ module Ameba::Rule::Lint
end
CRYSTAL
end
it "reports rule, location and message" do
s = Source.new %(
case
when String
puts "hello"
when can_generate?
generate if can_generate?
end
), "source.cr"
subject.catch(s).should_not be_valid
issue = s.issues.first
issue.rule.should_not be_nil
issue.location.to_s.should eq "source.cr:5:15"
issue.end_location.to_s.should eq "source.cr:5:27"
issue.message.should eq "Useless condition in when detected"
end
end
end

View file

@ -2,7 +2,7 @@ require "../../../spec_helper"
module Ameba::Rule::Metrics
subject = CyclomaticComplexity.new
complex_method = <<-CODE
complex_method = <<-CRYSTAL
def hello(a, b, c)
if a && b && c
begin
@ -15,7 +15,7 @@ module Ameba::Rule::Metrics
end
end
end
CODE
CRYSTAL
describe CyclomaticComplexity do
it "passes for empty methods" do

View file

@ -0,0 +1,93 @@
require "../../../spec_helper"
module Ameba::Rule::Naming
subject = AccessorMethodName.new
describe AccessorMethodName do
it "passes if accessor method name is correct" do
expect_no_issues subject, <<-CRYSTAL
class Foo
def self.instance
end
def self.instance=(value)
end
def user
end
def user=(user)
end
end
CRYSTAL
end
it "passes if accessor method is defined in top-level scope" do
expect_no_issues subject, <<-CRYSTAL
def get_user
end
def set_user(user)
end
CRYSTAL
end
it "fails if accessor method is defined with receiver in top-level scope" do
expect_issue subject, <<-CRYSTAL
def Foo.get_user
# ^^^^^^^^ error: Favour method name 'user' over 'get_user'
end
def Foo.set_user(user)
# ^^^^^^^^ error: Favour method name 'user=' over 'set_user'
end
CRYSTAL
end
it "fails if accessor method name is wrong" do
expect_issue subject, <<-CRYSTAL
class Foo
def self.get_instance
# ^^^^^^^^^^^^ error: Favour method name 'instance' over 'get_instance'
end
def self.set_instance(value)
# ^^^^^^^^^^^^ error: Favour method name 'instance=' over 'set_instance'
end
def get_user
# ^^^^^^^^ error: Favour method name 'user' over 'get_user'
end
def set_user(user)
# ^^^^^^^^ error: Favour method name 'user=' over 'set_user'
end
end
CRYSTAL
end
it "ignores if alternative name isn't valid syntax" do
expect_no_issues subject, <<-CRYSTAL
class Foo
def get_404
end
def set_404(value)
end
end
CRYSTAL
end
it "ignores if the method has unexpected arity" do
expect_no_issues subject, <<-CRYSTAL
class Foo
def get_user(type)
end
def set_user(user, type)
end
end
CRYSTAL
end
end
end

View file

@ -0,0 +1,151 @@
require "../../../spec_helper"
module Ameba::Rule::Naming
subject = AsciiIdentifiers.new
describe AsciiIdentifiers do
it "reports classes with names containing non-ascii characters" do
expect_issue subject, <<-CRYSTAL
class BigAwesome🐺
# ^^^^^^^^^^^ error: Identifier contains non-ascii characters
@🐺_name : String
# ^^^^^^^ error: Identifier contains non-ascii characters
end
CRYSTAL
end
it "reports modules with names containing non-ascii characters" do
expect_issue subject, <<-CRYSTAL
module Bąk
# ^^^ error: Identifier contains non-ascii characters
@@bąk_name : String
# ^^^^^^^^^^ error: Identifier contains non-ascii characters
end
CRYSTAL
end
it "reports enums with names containing non-ascii characters" do
expect_issue subject, <<-CRYSTAL
enum TypeOf🔥
# ^^^^^^^ error: Identifier contains non-ascii characters
end
CRYSTAL
end
it "reports defs with names containing non-ascii characters" do
expect_issue subject, <<-CRYSTAL
def łó
# ^^^^ error: Identifier contains non-ascii characters
end
CRYSTAL
end
it "reports defs with parameter names containing non-ascii characters" do
expect_issue subject, <<-CRYSTAL
def forest_adventure(include_🐺 = true, include_🐿 = true)
# ^ error: Identifier contains non-ascii characters
# ^ error: Identifier contains non-ascii characters
end
CRYSTAL
end
it "reports defs with parameter default values containing non-ascii characters" do
expect_issue subject, <<-CRYSTAL
def forest_adventure(animal_type = :🐺)
# ^^ error: Identifier contains non-ascii characters
end
CRYSTAL
end
it "reports argument names containing non-ascii characters" do
expect_issue subject, <<-CRYSTAL
%w[wensleydale cheddar brie].each { |🧀| nil }
# ^ error: Identifier contains non-ascii characters
CRYSTAL
end
it "reports calls with arguments containing non-ascii characters" do
expect_issue subject, <<-CRYSTAL
%i[🐺 🐿].index!(:🐺)
# ^^ error: Identifier contains non-ascii characters
CRYSTAL
end
it "reports calls with named arguments containing non-ascii characters" do
expect_issue subject, <<-CRYSTAL
%i[🐺 🐿].index!(obj: :🐺)
# ^^ error: Identifier contains non-ascii characters
CRYSTAL
end
it "reports aliases with names containing non-ascii characters" do
expect_issue subject, <<-CRYSTAL
alias JSON🧀 = JSON::Any
# ^^^^^ error: Identifier contains non-ascii characters
CRYSTAL
end
it "reports constants with names containing non-ascii characters" do
expect_issue subject, <<-CRYSTAL
I_LOVE_🍣 = true
# ^^^^^^ error: Identifier contains non-ascii characters
CRYSTAL
end
it "reports assignments with variable names containing non-ascii characters" do
expect_issue subject, <<-CRYSTAL
space_👾 = true
# ^^^^^ error: Identifier contains non-ascii characters
CRYSTAL
end
it "reports multiple assignments with variable names containing non-ascii characters" do
expect_issue subject, <<-CRYSTAL
foo, space_👾 = true, true
# ^^^^^^^ error: Identifier contains non-ascii characters
CRYSTAL
end
it "reports assignments with symbol literals containing non-ascii characters" do
expect_issue subject, <<-CRYSTAL
foo = :
# ^^^ error: Identifier contains non-ascii characters
CRYSTAL
end
it "reports multiple assignments with symbol literals containing non-ascii characters" do
expect_issue subject, <<-CRYSTAL
foo, bar = :, true
# ^^^ error: Identifier contains non-ascii characters
CRYSTAL
end
it "passes for strings with non-ascii characters" do
expect_no_issues subject, <<-CRYSTAL
space = "👾"
space = :invader # 👾
CRYSTAL
end
context "properties" do
context "#ignore_symbols" do
it "returns `false` by default" do
rule = AsciiIdentifiers.new
rule.ignore_symbols?.should be_false
end
it "stops reporting symbol literals if set to `true`" do
rule = AsciiIdentifiers.new
rule.ignore_symbols = true
expect_no_issues rule, <<-CRYSTAL
def forest_adventure(animal_type = :🐺); end
%i[🐺 🐿].index!(:🐺)
foo, bar = :, true
foo = :
CRYSTAL
end
end
end
end
end

View file

@ -0,0 +1,50 @@
require "../../../spec_helper"
module Ameba::Rule::Naming
subject = BinaryOperatorParameterName.new
describe BinaryOperatorParameterName do
it "ignores `other` parameter name in binary method definitions" do
expect_no_issues subject, <<-CRYSTAL
def +(other); end
def -(other); end
def *(other); end
CRYSTAL
end
it "ignores binary method definitions with arity other than 1" do
expect_no_issues subject, <<-CRYSTAL
def +; end
def +(foo, bar); end
def -; end
def -(foo, bar); end
CRYSTAL
end
it "ignores non-binary method definitions" do
expect_no_issues subject, <<-CRYSTAL
def foo(bar); end
def bąk(genus); end
CRYSTAL
end
it "reports binary methods definitions with incorrectly named parameter" do
expect_issue subject, <<-CRYSTAL
def +(foo); end
# ^ error: When defining the `+` operator, name its argument `other`
def -(foo); end
# ^ error: When defining the `-` operator, name its argument `other`
def *(foo); end
# ^ error: When defining the `*` operator, name its argument `other`
CRYSTAL
end
it "ignores methods from #excluded_operators" do
subject.excluded_operators.each do |op|
expect_no_issues subject, <<-CRYSTAL
def #{op}(foo); end
CRYSTAL
end
end
end
end

View file

@ -0,0 +1,100 @@
require "../../../spec_helper"
module Ameba::Rule::Naming
subject = BlockParameterName.new
.tap(&.min_name_length = 3)
.tap(&.allowed_names = %w[_ e i j k v])
describe BlockParameterName do
it "passes if block parameter name matches #allowed_names" do
subject.allowed_names.each do |name|
expect_no_issues subject, <<-CRYSTAL
%w[].each { |#{name}| }
CRYSTAL
end
end
it "fails if block parameter name doesn't match #allowed_names" do
expect_issue subject, <<-CRYSTAL
%w[].each { |x| }
# ^ error: Disallowed block parameter name found
CRYSTAL
end
context "properties" do
context "#min_name_length" do
it "allows setting custom values" do
rule = BlockParameterName.new
rule.allowed_names = %w[a b c]
rule.min_name_length = 3
expect_issue rule, <<-CRYSTAL
%w[].each { |x| }
# ^ error: Disallowed block parameter name found
CRYSTAL
rule.min_name_length = 1
expect_no_issues rule, <<-CRYSTAL
%w[].each { |x| }
CRYSTAL
end
end
context "#allow_names_ending_in_numbers" do
it "allows setting custom values" do
rule = BlockParameterName.new
rule.min_name_length = 1
rule.allowed_names = %w[]
rule.allow_names_ending_in_numbers = false
expect_issue rule, <<-CRYSTAL
%w[].each { |x1| }
# ^ error: Disallowed block parameter name found
CRYSTAL
rule.allow_names_ending_in_numbers = true
expect_no_issues rule, <<-CRYSTAL
%w[].each { |x1| }
CRYSTAL
end
end
context "#allowed_names" do
it "allows setting custom names" do
rule = BlockParameterName.new
rule.min_name_length = 3
rule.allowed_names = %w[a b c]
expect_issue rule, <<-CRYSTAL
%w[].each { |x| }
# ^ error: Disallowed block parameter name found
CRYSTAL
rule.allowed_names = %w[x y z]
expect_no_issues rule, <<-CRYSTAL
%w[].each { |x| }
CRYSTAL
end
end
context "#forbidden_names" do
it "allows setting custom names" do
rule = BlockParameterName.new
rule.min_name_length = 1
rule.allowed_names = %w[]
rule.forbidden_names = %w[x y z]
expect_issue rule, <<-CRYSTAL
%w[].each { |x| }
# ^ error: Disallowed block parameter name found
CRYSTAL
rule.forbidden_names = %w[a b c]
expect_no_issues rule, <<-CRYSTAL
%w[].each { |x| }
CRYSTAL
end
end
end
end
end

View file

@ -0,0 +1,41 @@
require "../../../spec_helper"
module Ameba
subject = Rule::Naming::ConstantNames.new
private def it_reports_constant(name, value, expected, *, file = __FILE__, line = __LINE__)
it "reports constant name #{expected}", file, line do
rule = Rule::Naming::ConstantNames.new
expect_issue rule, <<-CRYSTAL, name: name, file: file, line: line
%{name} = #{value}
# ^{name} error: Constant name should be screaming-cased: #{expected}, not #{name}
CRYSTAL
end
end
describe Rule::Naming::ConstantNames do
it "passes if type names are screaming-cased" do
expect_no_issues subject, <<-CRYSTAL
LUCKY_NUMBERS = [3, 7, 11]
DOCUMENTATION_URL = "https://crystal-lang.org/docs"
Int32
s : String = "str"
def works(n : Int32)
end
Log = ::Log.for("db")
a = 1
myVar = 2
m_var = 3
CRYSTAL
end
# it_reports_constant "MyBadConstant", "1", "MYBADCONSTANT"
it_reports_constant "Wrong_NAME", "2", "WRONG_NAME"
it_reports_constant "Wrong_Name", "3", "WRONG_NAME"
end
end

View file

@ -0,0 +1,19 @@
require "../../../spec_helper"
module Ameba::Rule::Naming
subject = Filename.new
describe Filename do
it "passes if filename is correct" do
expect_no_issues subject, code: "", path: "src/foo.cr"
expect_no_issues subject, code: "", path: "src/foo_bar.cr"
end
it "fails if filename is wrong" do
expect_issue subject, <<-CRYSTAL, path: "src/fooBar.cr"
# ^{} error: Filename should be underscore-cased: foo_bar.cr, not fooBar.cr
CRYSTAL
end
end
end

View file

@ -0,0 +1,42 @@
require "../../../spec_helper"
module Ameba
subject = Rule::Naming::MethodNames.new
private def it_reports_method_name(name, expected, *, file = __FILE__, line = __LINE__)
it "reports method name #{expected}", file, line do
rule = Rule::Naming::MethodNames.new
expect_issue rule, <<-CRYSTAL, name: name, file: file, line: line
def %{name}; end
# ^{name} error: Method name should be underscore-cased: #{expected}, not %{name}
CRYSTAL
end
end
describe Rule::Naming::MethodNames do
it "passes if method names are underscore-cased" do
expect_no_issues subject, <<-CRYSTAL
class Person
def first_name
end
def date_of_birth
end
def homepage_url
end
def valid?
end
def name
end
end
CRYSTAL
end
it_reports_method_name "firstName", "first_name"
it_reports_method_name "date_of_Birth", "date_of_birth"
it_reports_method_name "homepageURL", "homepage_url"
end
end

View file

@ -1,6 +1,6 @@
require "../../../spec_helper"
module Ameba::Rule::Style
module Ameba::Rule::Naming
subject = PredicateName.new
describe PredicateName do
@ -21,30 +21,22 @@ module Ameba::Rule::Style
it "fails if predicate name is wrong" do
expect_issue subject, <<-CRYSTAL
class Image
def self.is_valid?(x)
# ^^^^^^^^^ error: Favour method name 'valid?' over 'is_valid?'
end
end
def is_valid?(x)
# ^^^^^^^^^^^^^^ error: Favour method name 'valid?' over 'is_valid?'
# ^^^^^^^^^ error: Favour method name 'valid?' over 'is_valid?'
end
def is_valid(x)
# ^^^^^^^^ error: Favour method name 'valid?' over 'is_valid'
end
CRYSTAL
end
it "reports rule, pos and message" do
s = Source.new %q(
class Image
def is_valid?(x)
true
end
end
), "source.cr"
subject.catch(s).should_not be_valid
issue = s.issues.first
issue.rule.should_not be_nil
issue.location.to_s.should eq "source.cr:2:3"
issue.end_location.to_s.should eq "source.cr:4:5"
issue.message.should eq(
"Favour method name 'valid?' over 'is_valid?'")
end
it "ignores if alternative name isn't valid syntax" do
expect_no_issues subject, <<-CRYSTAL
class Image

View file

@ -0,0 +1,72 @@
require "../../../spec_helper"
module Ameba::Rule::Naming
subject = QueryBoolMethods.new
describe QueryBoolMethods do
it "passes for valid cases" do
expect_no_issues subject, <<-CRYSTAL
class Foo
class_property? foo = true
property? foo = true
property foo2 : Bool? = true
setter panda = true
end
module Bar
class_getter? bar : Bool = true
getter? bar : Bool
getter bar2 : Bool? = true
setter panda : Bool = true
def initialize(@bar = true)
end
end
CRYSTAL
end
it "reports only valid properties" do
expect_issue subject, <<-CRYSTAL
class Foo
class_property? foo = true
class_property bar = true
# ^^^ error: Consider using 'class_property?' for 'bar'
class_property baz = true
# ^^^ error: Consider using 'class_property?' for 'baz'
end
CRYSTAL
end
{% for call in %w[getter class_getter property class_property] %}
it "reports `{{ call.id }}` assign with Bool" do
expect_issue subject, <<-CRYSTAL, call: {{ call }}
class Foo
%{call} foo = true
_{call} # ^^^ error: Consider using '%{call}?' for 'foo'
end
CRYSTAL
end
it "reports `{{ call.id }}` type declaration assign with Bool" do
expect_issue subject, <<-CRYSTAL, call: {{ call }}
class Foo
%{call} foo : Bool = true
_{call} # ^^^ error: Consider using '%{call}?' for 'foo'
end
CRYSTAL
end
it "reports `{{ call.id }}` type declaration with Bool" do
expect_issue subject, <<-CRYSTAL, call: {{ call }}
class Foo
%{call} foo : Bool
_{call} # ^^^ error: Consider using '%{call}?' for 'foo'
def initialize(@foo = true)
end
end
CRYSTAL
end
{% end %}
end
end

View file

@ -0,0 +1,53 @@
require "../../../spec_helper"
module Ameba::Rule::Naming
subject = RescuedExceptionsVariableName.new
describe RescuedExceptionsVariableName do
it "passes if exception handler variable name matches #allowed_names" do
subject.allowed_names.each do |name|
expect_no_issues subject, <<-CRYSTAL
def foo
raise "foo"
rescue #{name}
nil
end
CRYSTAL
end
end
it "fails if exception handler variable name doesn't match #allowed_names" do
expect_issue subject, <<-CRYSTAL
def foo
raise "foo"
rescue wtf
# ^^^^^^^^ error: Disallowed variable name, use one of these instead: 'e', 'ex', 'exception', 'error'
nil
end
CRYSTAL
end
context "properties" do
context "#allowed_names" do
it "returns sensible defaults" do
rule = RescuedExceptionsVariableName.new
rule.allowed_names.should eq %w[e ex exception error]
end
it "allows setting custom names" do
rule = RescuedExceptionsVariableName.new
rule.allowed_names = %w[foo]
expect_issue rule, <<-CRYSTAL
def foo
raise "foo"
rescue e
# ^^^^^^ error: Disallowed variable name, use 'foo' instead
nil
end
CRYSTAL
end
end
end
end
end

View file

@ -0,0 +1,53 @@
require "../../../spec_helper"
module Ameba
subject = Rule::Naming::TypeNames.new
private def it_reports_name(type, name, expected, *, file = __FILE__, line = __LINE__)
it "reports type name #{expected}", file, line do
rule = Rule::Naming::TypeNames.new
expect_issue rule, <<-CRYSTAL, type: type, name: name, file: file, line: line
%{type} %{name}; end
_{type} # ^{name} error: Type name should be camelcased: #{expected}, but it was %{name}
CRYSTAL
end
end
describe Rule::Naming::TypeNames do
it "passes if type names are camelcased" do
expect_no_issues subject, <<-CRYSTAL
class ParseError < Exception
end
module HTTP
class RequestHandler
end
end
alias NumericValue = Float32 | Float64 | Int32 | Int64
lib LibYAML
end
struct TagDirective
end
enum Time::DayOfWeek
end
CRYSTAL
end
it_reports_name "class", "My_class", "MyClass"
it_reports_name "module", "HTT_p", "HTTP"
it_reports_name "lib", "Lib_YAML", "LibYAML"
it_reports_name "struct", "Tag_directive", "TagDirective"
it_reports_name "enum", "Time_enum::Day_of_week", "TimeEnum::DayOfWeek"
it "reports alias name" do
expect_issue subject, <<-CRYSTAL
alias Numeric_value = Int32
# ^^^^^^^^^^^^^ error: Type name should be camelcased: NumericValue, but it was Numeric_value
CRYSTAL
end
end
end

View file

@ -1,19 +1,19 @@
require "../../../spec_helper"
module Ameba
subject = Rule::Style::VariableNames.new
subject = Rule::Naming::VariableNames.new
private def it_reports_var_name(name, value, expected)
it "reports variable name #{expected}" do
rule = Rule::Style::VariableNames.new
expect_issue rule, <<-CRYSTAL, name: name
private def it_reports_var_name(name, value, expected, *, file = __FILE__, line = __LINE__)
it "reports variable name #{expected}", file, line do
rule = Rule::Naming::VariableNames.new
expect_issue rule, <<-CRYSTAL, name: name, file: file, line: line
%{name} = #{value}
# ^{name} error: Var name should be underscore-cased: #{expected}, not %{name}
CRYSTAL
end
end
describe Rule::Style::VariableNames do
describe Rule::Naming::VariableNames do
it "passes if var names are underscore-cased" do
expect_no_issues subject, <<-CRYSTAL
class Greeting
@ -62,19 +62,5 @@ module Ameba
end
CRYSTAL
end
it "reports rule, pos and message" do
s = Source.new %(
badName = "Yeah"
), "source.cr"
subject.catch(s).should_not be_valid
issue = s.issues.first
issue.rule.should_not be_nil
issue.location.to_s.should eq "source.cr:1:1"
issue.end_location.to_s.should eq "source.cr:1:7"
issue.message.should eq(
"Var name should be underscore-cased: bad_name, not badName"
)
end
end
end

View file

@ -17,7 +17,7 @@ module Ameba::Rule::Performance
it "reports if there is select followed by any? without a block" do
source = expect_issue subject, <<-CRYSTAL
[1, 2, 3].select { |e| e > 2 }.any?
# ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: Use `any? {...}` instead of `select {...}.any?`
# ^^^^^^^^^^^^^^^^^^^^^^^^^ error: Use `any? {...}` instead of `select {...}.any?`
CRYSTAL
expect_no_corrections source
@ -32,7 +32,7 @@ module Ameba::Rule::Performance
it "reports if there is reject followed by any? without a block" do
source = expect_issue subject, <<-CRYSTAL
[1, 2, 3].reject { |e| e > 2 }.any?
# ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: Use `any? {...}` instead of `reject {...}.any?`
# ^^^^^^^^^^^^^^^^^^^^^^^^^ error: Use `any? {...}` instead of `reject {...}.any?`
CRYSTAL
expect_no_corrections source
@ -46,9 +46,9 @@ module Ameba::Rule::Performance
end
context "properties" do
it "allows to configure object_call_names" do
rule = Rule::Performance::AnyAfterFilter.new
rule.filter_names = %w(select)
it "#filter_names" do
rule = AnyAfterFilter.new
rule.filter_names = %w[select]
expect_no_issues rule, <<-CRYSTAL
[1, 2, 3].reject { |e| e > 2 }.any?
@ -60,16 +60,6 @@ module Ameba::Rule::Performance
it "reports in macro scope" do
source = expect_issue subject, <<-CRYSTAL
{{ [1, 2, 3].reject { |e| e > 2 }.any? }}
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: Use `any? {...}` instead of `reject {...}.any?`
CRYSTAL
expect_no_corrections source
end
end
it "reports rule, pos and message" do
source = expect_issue subject, <<-CRYSTAL
[1, 2, 3].reject { |e| e > 2 }.any?
# ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: Use `any? {...}` instead of `reject {...}.any?`
CRYSTAL
@ -77,3 +67,4 @@ module Ameba::Rule::Performance
end
end
end
end

View file

@ -14,14 +14,10 @@ module Ameba::Rule::Performance
end
it "reports if there is any? call without a block nor argument" do
source = expect_issue subject, <<-CRYSTAL
expect_issue subject, <<-CRYSTAL
[1, 2, 3].any?
# ^^^^ error: Use `!{...}.empty?` instead of `{...}.any?`
CRYSTAL
expect_correction source, <<-CRYSTAL
![1, 2, 3].empty?
CRYSTAL
end
it "does not report if source is a spec" do
@ -32,28 +28,11 @@ module Ameba::Rule::Performance
context "macro" do
it "reports in macro scope" do
source = expect_issue subject, <<-CRYSTAL
expect_issue subject, <<-CRYSTAL
{{ [1, 2, 3].any? }}
# ^^^^ error: Use `!{...}.empty?` instead of `{...}.any?`
CRYSTAL
expect_correction source, <<-CRYSTAL
{{ ![1, 2, 3].empty? }}
CRYSTAL
end
end
it "reports rule, pos and message" do
source = Source.new path: "source.cr", code: %(
[1, 2, 3].any?
)
subject.catch(source).should_not be_valid
issue = source.issues.first
issue.rule.should_not be_nil
issue.location.to_s.should eq "source.cr:1:11"
issue.end_location.to_s.should eq "source.cr:1:14"
issue.message.should eq "Use `!{...}.empty?` instead of `{...}.any?`"
end
end
end
end

View file

@ -44,9 +44,9 @@ module Ameba::Rule::Performance
end
context "properties" do
it "allows to configure `call_names`" do
it "#call_names" do
rule = ChainedCallWithNoBang.new
rule.call_names = %w(uniq)
rule.call_names = %w[uniq]
expect_no_issues rule, <<-CRYSTAL
[1, 2, 3].select { |e| e > 2 }.reverse
@ -54,22 +54,6 @@ module Ameba::Rule::Performance
end
end
it "reports rule, pos and message" do
source = Source.new path: "source.cr", code: <<-CODE
[1, 2, 3].select { |e| e > 1 }.reverse
CODE
subject.catch(source).should_not be_valid
source.issues.size.should eq 1
issue = source.issues.first
issue.rule.should_not be_nil
issue.location.to_s.should eq "source.cr:1:32"
issue.end_location.to_s.should eq "source.cr:1:38"
issue.message.should eq "Use bang method variant `reverse!` after chained `select` call"
end
context "macro" do
it "doesn't report in macro scope" do
expect_no_issues subject, <<-CRYSTAL

View file

@ -19,7 +19,7 @@ module Ameba::Rule::Performance
it "reports if there is map followed by compact call" do
expect_issue subject, <<-CRYSTAL
(1..3).map(&.itself).compact
# ^^^^^^^^^^^^^^^^^^^^^^ error: Use `compact_map {...}` instead of `map {...}.compact`
# ^^^^^^^^^^^^^^^^^^^^^ error: Use `compact_map {...}` instead of `map {...}.compact`
CRYSTAL
end
@ -36,18 +36,5 @@ module Ameba::Rule::Performance
CRYSTAL
end
end
it "reports rule, pos and message" do
s = Source.new %(
(1..3).map(&.itself).compact
), "source.cr"
subject.catch(s).should_not be_valid
issue = s.issues.first
issue.rule.should_not be_nil
issue.location.to_s.should eq "source.cr:1:8"
issue.end_location.to_s.should eq "source.cr:1:29"
issue.message.should eq "Use `compact_map {...}` instead of `map {...}.compact`"
end
end
end

View file

@ -0,0 +1,57 @@
require "../../../spec_helper"
module Ameba::Rule::Performance
subject = ExcessiveAllocations.new
describe ExcessiveAllocations do
it "passes if there is no potential performance improvements" do
expect_no_issues subject, <<-CRYSTAL
"Alice".chars.each(arg) { |c| puts c }
"Alice".chars(arg).each { |c| puts c }
"Alice\nBob".lines.each(arg) { |l| puts l }
"Alice\nBob".lines(arg).each { |l| puts l }
CRYSTAL
end
it "reports if there is a collection method followed by each" do
source = expect_issue subject, <<-CRYSTAL
"Alice".chars.each { |c| puts c }
# ^^^^^^^^^^ error: Use `each_char {...}` instead of `chars.each {...}` to avoid excessive allocation
"Alice\nBob".lines.each { |l| puts l }
# ^^^^^^^^^^ error: Use `each_line {...}` instead of `lines.each {...}` to avoid excessive allocation
CRYSTAL
expect_correction source, <<-CRYSTAL
"Alice".each_char { |c| puts c }
"Alice\nBob".each_line { |l| puts l }
CRYSTAL
end
it "does not report if source is a spec" do
expect_no_issues subject, <<-CRYSTAL, "source_spec.cr"
"Alice".chars.each { |c| puts c }
CRYSTAL
end
context "properties" do
it "#call_names" do
rule = ExcessiveAllocations.new
rule.call_names = {
"children" => "each_child",
}
expect_no_issues rule, <<-CRYSTAL
"Alice".chars.each { |c| puts c }
CRYSTAL
end
end
context "macro" do
it "doesn't report in macro scope" do
expect_no_issues subject, <<-CRYSTAL
{{ "Alice".chars.each { |c| puts c } }}
CRYSTAL
end
end
end
end

View file

@ -17,7 +17,7 @@ module Ameba::Rule::Performance
it "reports if there is select followed by last" do
expect_issue subject, <<-CRYSTAL
[1, 2, 3].select { |e| e > 2 }.last
# ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: Use `reverse_each.find {...}` instead of `select {...}.last`
# ^^^^^^^^^^^^^^^^^^^^^^^^^ error: Use `reverse_each.find {...}` instead of `select {...}.last`
CRYSTAL
end
@ -30,14 +30,14 @@ module Ameba::Rule::Performance
it "reports if there is select followed by last?" do
expect_issue subject, <<-CRYSTAL
[1, 2, 3].select { |e| e > 2 }.last?
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: Use `reverse_each.find {...}` instead of `select {...}.last?`
# ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: Use `reverse_each.find {...}` instead of `select {...}.last?`
CRYSTAL
end
it "reports if there is select followed by first" do
expect_issue subject, <<-CRYSTAL
[1, 2, 3].select { |e| e > 2 }.first
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: Use `find {...}` instead of `select {...}.first`
# ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: Use `find {...}` instead of `select {...}.first`
CRYSTAL
end
@ -50,7 +50,7 @@ module Ameba::Rule::Performance
it "reports if there is select followed by first?" do
expect_issue subject, <<-CRYSTAL
[1, 2, 3].select { |e| e > 2 }.first?
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: Use `find {...}` instead of `select {...}.first?`
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: Use `find {...}` instead of `select {...}.first?`
CRYSTAL
end
@ -62,9 +62,9 @@ module Ameba::Rule::Performance
end
context "properties" do
it "allows to configure object_call_names" do
rule = Rule::Performance::FirstLastAfterFilter.new
rule.filter_names = %w(reject)
it "#filter_names" do
rule = FirstLastAfterFilter.new
rule.filter_names = %w[reject]
expect_no_issues rule, <<-CRYSTAL
[1, 2, 3].select { |e| e > 2 }.first
@ -72,21 +72,6 @@ module Ameba::Rule::Performance
end
end
it "reports rule, pos and message" do
s = Source.new %(
[1, 2, 3].select { |e| e > 2 }.first
), "source.cr"
subject.catch(s).should_not be_valid
s.issues.size.should eq 1
issue = s.issues.first
issue.rule.should_not be_nil
issue.location.to_s.should eq "source.cr:1:11"
issue.end_location.to_s.should eq "source.cr:1:37"
issue.message.should eq "Use `find {...}` instead of `select {...}.first`"
end
context "macro" do
it "doesn't report in macro scope" do
expect_no_issues subject, <<-CRYSTAL

View file

@ -13,7 +13,7 @@ module Ameba::Rule::Performance
it "reports if there is map followed by flatten call" do
expect_issue subject, <<-CRYSTAL
%w[Alice Bob].map(&.chars).flatten
# ^^^^^^^^^^^^^^^^^^^^^ error: Use `flat_map {...}` instead of `map {...}.flatten`
# ^^^^^^^^^^^^^^^^^^^^ error: Use `flat_map {...}` instead of `map {...}.flatten`
CRYSTAL
end
@ -30,18 +30,5 @@ module Ameba::Rule::Performance
CRYSTAL
end
end
it "reports rule, pos and message" do
s = Source.new %(
%w[Alice Bob].map(&.chars).flatten
), "source.cr"
subject.catch(s).should_not be_valid
issue = s.issues.first
issue.rule.should_not be_nil
issue.location.to_s.should eq "source.cr:1:15"
issue.end_location.to_s.should eq "source.cr:1:35"
issue.message.should eq "Use `flat_map {...}` instead of `map {...}.flatten`"
end
end
end

View file

@ -14,7 +14,7 @@ module Ameba::Rule::Performance
it "reports if there is map followed by sum without a block" do
expect_issue subject, <<-CRYSTAL
(1..3).map(&.to_u64).sum
# ^^^^^^^^^^^^^^^^^^ error: Use `sum {...}` instead of `map {...}.sum`
# ^^^^^^^^^^^^^^^^^ error: Use `sum {...}` instead of `map {...}.sum`
CRYSTAL
end
@ -27,14 +27,14 @@ module Ameba::Rule::Performance
it "reports if there is map followed by sum without a block (with argument)" do
expect_issue subject, <<-CRYSTAL
(1..3).map(&.to_u64).sum(0)
# ^^^^^^^^^^^^^^^^^^ error: Use `sum {...}` instead of `map {...}.sum`
# ^^^^^^^^^^^^^^^^^ error: Use `sum {...}` instead of `map {...}.sum`
CRYSTAL
end
it "reports if there is map followed by sum with a block" do
expect_issue subject, <<-CRYSTAL
(1..3).map(&.to_u64).sum(&.itself)
# ^^^^^^^^^^^^^^^^^^ error: Use `sum {...}` instead of `map {...}.sum`
# ^^^^^^^^^^^^^^^^^ error: Use `sum {...}` instead of `map {...}.sum`
CRYSTAL
end
@ -45,18 +45,5 @@ module Ameba::Rule::Performance
CRYSTAL
end
end
it "reports rule, pos and message" do
s = Source.new %(
(1..3).map(&.to_u64).sum
), "source.cr"
subject.catch(s).should_not be_valid
issue = s.issues.first
issue.rule.should_not be_nil
issue.location.to_s.should eq "source.cr:1:8"
issue.end_location.to_s.should eq "source.cr:1:25"
issue.message.should eq "Use `sum {...}` instead of `map {...}.sum`"
end
end
end

View file

@ -0,0 +1,45 @@
require "../../../spec_helper"
module Ameba::Rule::Performance
subject = MinMaxAfterMap.new
describe MinMaxAfterMap do
it "passes if there are no potential performance improvements" do
expect_no_issues subject, <<-CRYSTAL
%w[Alice Bob].map { |name| name.size }.min(2)
%w[Alice Bob].map { |name| name.size }.max(2)
CRYSTAL
end
it "reports if there is a `min/max/minmax` call followed by `map`" do
source = expect_issue subject, <<-CRYSTAL
%w[Alice Bob].map { |name| name.size }.min
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: Use `min_of {...}` instead of `map {...}.min`.
%w[Alice Bob].map(&.size).max.zero?
# ^^^^^^^^^^^^^^^ error: Use `max_of {...}` instead of `map {...}.max`.
%w[Alice Bob].map(&.size).minmax?
# ^^^^^^^^^^^^^^^^^^^ error: Use `minmax_of? {...}` instead of `map {...}.minmax?`.
CRYSTAL
expect_correction source, <<-CRYSTAL
%w[Alice Bob].min_of { |name| name.size }
%w[Alice Bob].max_of(&.size).zero?
%w[Alice Bob].minmax_of?(&.size)
CRYSTAL
end
it "does not report if source is a spec" do
expect_no_issues subject, path: "source_spec.cr", code: <<-CRYSTAL
%w[Alice Bob].map(&.size).min
CRYSTAL
end
context "macro" do
it "doesn't report in macro scope" do
expect_no_issues subject, <<-CRYSTAL
{{ %w[Alice Bob].map(&.size).min }}
CRYSTAL
end
end
end
end

View file

@ -19,7 +19,7 @@ module Ameba::Rule::Performance
it "reports if there is a select followed by size" do
expect_issue subject, <<-CRYSTAL
[1, 2, 3].select { |e| e > 2 }.size
# ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: Use `count {...}` instead of `select {...}.size`.
# ^^^^^^^^^^^^^^^^^^^^^^^^^ error: Use `count {...}` instead of `select {...}.size`.
CRYSTAL
end
@ -32,21 +32,21 @@ module Ameba::Rule::Performance
it "reports if there is a reject followed by size" do
expect_issue subject, <<-CRYSTAL
[1, 2, 3].reject { |e| e < 2 }.size
# ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: Use `count {...}` instead of `reject {...}.size`.
# ^^^^^^^^^^^^^^^^^^^^^^^^^ error: Use `count {...}` instead of `reject {...}.size`.
CRYSTAL
end
it "reports if a block shorthand used" do
expect_issue subject, <<-CRYSTAL
[1, 2, 3].reject(&.empty?).size
# ^^^^^^^^^^^^^^^^^^^^^^ error: Use `count {...}` instead of `reject {...}.size`.
# ^^^^^^^^^^^^^^^^^^^^^ error: Use `count {...}` instead of `reject {...}.size`.
CRYSTAL
end
context "properties" do
it "allows to configure object caller names" do
rule = Rule::Performance::SizeAfterFilter.new
rule.filter_names = %w(select)
it "#filter_names" do
rule = SizeAfterFilter.new
rule.filter_names = %w[select]
expect_no_issues rule, <<-CRYSTAL
[1, 2, 3].reject(&.empty?).size
@ -61,18 +61,5 @@ module Ameba::Rule::Performance
CRYSTAL
end
end
it "reports rule, pos and message" do
s = Source.new %(
lines.split("\n").reject(&.empty?).size
), "source.cr"
subject.catch(s).should_not be_valid
issue = s.issues.first
issue.rule.should_not be_nil
issue.location.to_s.should eq "source.cr:2:4"
issue.end_location.to_s.should eq "source.cr:2:25"
issue.message.should eq "Use `count {...}` instead of `reject {...}.size`."
end
end
end

View file

@ -1,55 +0,0 @@
require "../../../spec_helper"
module Ameba
subject = Rule::Style::ConstantNames.new
private def it_reports_constant(name, value, expected)
it "reports constant name #{expected}" do
rule = Rule::Style::ConstantNames.new
expect_issue rule, <<-CRYSTAL, name: name
%{name} = #{value}
# ^{name} error: Constant name should be screaming-cased: #{expected}, not #{name}
CRYSTAL
end
end
describe Rule::Style::ConstantNames do
it "passes if type names are screaming-cased" do
expect_no_issues subject, <<-CRYSTAL
LUCKY_NUMBERS = [3, 7, 11]
DOCUMENTATION_URL = "http://crystal-lang.org/docs"
Int32
s : String = "str"
def works(n : Int32)
end
Log = ::Log.for("db")
a = 1
myVar = 2
m_var = 3
CRYSTAL
end
# it_reports_constant "MyBadConstant", "1", "MYBADCONSTANT"
it_reports_constant "Wrong_NAME", "2", "WRONG_NAME"
it_reports_constant "Wrong_Name", "3", "WRONG_NAME"
it "reports rule, pos and message" do
s = Source.new %(
Const_Name = 1
), "source.cr"
subject.catch(s).should_not be_valid
issue = s.issues.first
issue.rule.should_not be_nil
issue.location.to_s.should eq "source.cr:1:1"
issue.end_location.to_s.should eq "source.cr:1:10"
issue.message.should eq(
"Constant name should be screaming-cased: CONST_NAME, not Const_Name"
)
end
end
end

View file

@ -3,11 +3,11 @@ require "../../../spec_helper"
module Ameba
subject = Rule::Style::GuardClause.new
def it_reports_body(body, *, line = __LINE__)
private def it_reports_body(body, *, file = __FILE__, line = __LINE__)
rule = Rule::Style::GuardClause.new
it "reports an issue if method body is if / unless without else" do
source = expect_issue rule, <<-CRYSTAL, line: line
it "reports an issue if method body is if / unless without else", file, line do
source = expect_issue rule, <<-CRYSTAL, file: file, line: line
def func
if something
# ^^ error: Use a guard clause (`return unless something`) instead of wrapping the code inside a conditional expression.
@ -23,7 +23,7 @@ module Ameba
end
CRYSTAL
expect_correction source, <<-CRYSTAL, line: line
expect_correction source, <<-CRYSTAL, file: file, line: line
def func
return unless something
#{body}
@ -38,8 +38,8 @@ module Ameba
CRYSTAL
end
it "reports an issue if method body ends with if / unless without else" do
source = expect_issue rule, <<-CRYSTAL, line: line
it "reports an issue if method body ends with if / unless without else", file, line do
source = expect_issue rule, <<-CRYSTAL, file: file, line: line
def func
test
if something
@ -57,7 +57,7 @@ module Ameba
end
CRYSTAL
expect_correction source, <<-CRYSTAL, line: line
expect_correction source, <<-CRYSTAL, file: file, line: line
def func
test
return unless something
@ -75,11 +75,11 @@ module Ameba
end
end
def it_reports_control_expression(kw, *, line = __LINE__)
private def it_reports_control_expression(kw, *, file = __FILE__, line = __LINE__)
rule = Rule::Style::GuardClause.new
it "reports an issue with #{kw} in the if branch" do
source = expect_issue rule, <<-CRYSTAL, line: line
it "reports an issue with #{kw} in the if branch", file, line do
source = expect_issue rule, <<-CRYSTAL, file: file, line: line
def func
if something
# ^^ error: Use a guard clause (`#{kw} if something`) instead of wrapping the code inside a conditional expression.
@ -90,11 +90,11 @@ module Ameba
end
CRYSTAL
expect_no_corrections source, line: line
expect_no_corrections source, file: file, line: line
end
it "reports an issue with #{kw} in the else branch" do
source = expect_issue rule, <<-CRYSTAL, line: line
it "reports an issue with #{kw} in the else branch", file, line do
source = expect_issue rule, <<-CRYSTAL, file: file, line: line
def func
if something
# ^^ error: Use a guard clause (`#{kw} unless something`) instead of wrapping the code inside a conditional expression.
@ -105,11 +105,11 @@ module Ameba
end
CRYSTAL
expect_no_corrections source, line: line
expect_no_corrections source, file: file, line: line
end
it "doesn't report an issue if condition has multiple lines" do
expect_no_issues rule, <<-CRYSTAL, line: line
it "doesn't report an issue if condition has multiple lines", file, line do
expect_no_issues rule, <<-CRYSTAL, file: file, line: line
def func
if something &&
something_else
@ -121,8 +121,8 @@ module Ameba
CRYSTAL
end
it "does not report an issue if #{kw} is inside elsif" do
expect_no_issues rule, <<-CRYSTAL, line: line
it "does not report an issue if #{kw} is inside elsif", file, line do
expect_no_issues rule, <<-CRYSTAL, file: file, line: line
def func
if something
a
@ -133,8 +133,8 @@ module Ameba
CRYSTAL
end
it "does not report an issue if #{kw} is inside if..elsif..else..end" do
expect_no_issues rule, <<-CRYSTAL, line: line
it "does not report an issue if #{kw} is inside if..elsif..else..end", file, line do
expect_no_issues rule, <<-CRYSTAL, file: file, line: line
def func
if something
a
@ -147,8 +147,8 @@ module Ameba
CRYSTAL
end
it "doesn't report an issue if control flow expr has multiple lines" do
expect_no_issues rule, <<-CRYSTAL, line: line
it "doesn't report an issue if control flow expr has multiple lines", file, line do
expect_no_issues rule, <<-CRYSTAL, file: file, line: line
def func
if something
#{kw} \\
@ -161,8 +161,8 @@ module Ameba
CRYSTAL
end
it "reports an issue if non-control-flow branch has multiple lines" do
source = expect_issue rule, <<-CRYSTAL, line: line
it "reports an issue if non-control-flow branch has multiple lines", file, line do
source = expect_issue rule, <<-CRYSTAL, file: file, line: line
def func
if something
# ^^ error: Use a guard clause (`#{kw} if something`) instead of wrapping the code inside a conditional expression.
@ -174,7 +174,7 @@ module Ameba
end
CRYSTAL
expect_no_corrections source, line: line
expect_no_corrections source, file: file, line: line
end
end

View file

@ -42,9 +42,10 @@ module Ameba::Rule::Style
end
context "properties" do
it "allows to configure filter_names" do
it "#filter_names" do
rule = IsAFilter.new
rule.filter_names = %w(select)
rule.filter_names = %w[select]
expect_no_issues rule, <<-CRYSTAL
[1, 2, nil].reject(&.nil?)
CRYSTAL
@ -58,20 +59,5 @@ module Ameba::Rule::Style
CRYSTAL
end
end
it "reports rule, pos and message" do
source = Source.new path: "source.cr", code: %(
[1, 2, nil].reject(&.nil?)
)
subject.catch(source).should_not be_valid
source.issues.size.should eq 1
issue = source.issues.first
issue.rule.should_not be_nil
issue.location.to_s.should eq "source.cr:1:13"
issue.end_location.to_s.should eq "source.cr:1:26"
issue.message.should eq "Use `reject(Nil)` instead of `reject {...}`"
end
end
end

View file

@ -14,31 +14,25 @@ module Ameba::Rule::Style
end
it "reports if there is a call to is_a?(Nil) without receiver" do
expect_issue subject, <<-CRYSTAL
source = expect_issue subject, <<-CRYSTAL
a = is_a?(Nil)
# ^^^ error: Use `nil?` instead of `is_a?(Nil)`
CRYSTAL
expect_correction source, <<-CRYSTAL
a = self.nil?
CRYSTAL
end
it "reports if there is a call to is_a?(Nil) with receiver" do
expect_issue subject, <<-CRYSTAL
source = expect_issue subject, <<-CRYSTAL
a.is_a?(Nil)
# ^^^ error: Use `nil?` instead of `is_a?(Nil)`
CRYSTAL
end
it "reports rule, location and message" do
s = Source.new %(
nil.is_a? Nil
), "source.cr"
subject.catch(s).should_not be_valid
s.issues.size.should eq 1
issue = s.issues.first
issue.rule.should_not be_nil
issue.location.to_s.should eq "source.cr:1:11"
issue.end_location.to_s.should eq "source.cr:1:13"
issue.message.should eq IsANil::MSG
expect_correction source, <<-CRYSTAL
a.nil?
CRYSTAL
end
end
end

View file

@ -3,12 +3,12 @@ require "../../../spec_helper"
module Ameba
subject = Rule::Style::LargeNumbers.new
private def it_transforms(number, expected)
it "transforms large number #{number}" do
private def it_transforms(number, expected, *, file = __FILE__, line = __LINE__)
it "transforms large number #{number}", file, line do
rule = Rule::Style::LargeNumbers.new
rule.int_min_digits = 5
source = expect_issue rule, <<-CRYSTAL, number: number
source = expect_issue rule, <<-CRYSTAL, number: number, file: file, line: line
number = %{number}
# ^{number} error: Large numbers should be written with underscores: #{expected}
CRYSTAL
@ -97,10 +97,12 @@ module Ameba
it_transforms "10000_i16", "10_000_i16"
it_transforms "10000_i32", "10_000_i32"
it_transforms "10000_i64", "10_000_i64"
it_transforms "10000_i128", "10_000_i128"
it_transforms "10000_u16", "10_000_u16"
it_transforms "10000_u32", "10_000_u32"
it_transforms "10000_u64", "10_000_u64"
it_transforms "10000_u128", "10_000_u128"
it_transforms "123456_f32", "123_456_f32"
it_transforms "123456_f64", "123_456_f64"
@ -117,23 +119,11 @@ module Ameba
it_transforms "3.001234", "3.001_234"
it_transforms "3.0012345", "3.001_234_5"
it "reports rule, pos and message" do
s = Source.new %q(
1200000
), "source.cr"
subject.catch(s).should_not be_valid
issue = s.issues.first
issue.rule.should_not be_nil
issue.location.to_s.should eq "source.cr:1:1"
issue.end_location.to_s.should eq "source.cr:1:7"
issue.message.should match /1_200_000/
end
context "properties" do
it "allows to configure integer min digits" do
it "#int_min_digits" do
rule = Rule::Style::LargeNumbers.new
rule.int_min_digits = 10
expect_no_issues rule, %q(1200000)
expect_no_issues rule, "1200000"
end
end
end

View file

@ -1,57 +0,0 @@
require "../../../spec_helper"
module Ameba
subject = Rule::Style::MethodNames.new
private def it_reports_method_name(name, expected)
it "reports method name #{expected}" do
rule = Rule::Style::MethodNames.new
expect_issue rule, <<-CRYSTAL, name: name
def %{name}; end
# ^{name} error: Method name should be underscore-cased: #{expected}, not %{name}
CRYSTAL
end
end
describe Rule::Style::MethodNames do
it "passes if method names are underscore-cased" do
expect_no_issues subject, <<-CRYSTAL
class Person
def first_name
end
def date_of_birth
end
def homepage_url
end
def valid?
end
def name
end
end
CRYSTAL
end
it_reports_method_name "firstName", "first_name"
it_reports_method_name "date_of_Birth", "date_of_birth"
it_reports_method_name "homepageURL", "homepage_url"
it "reports rule, pos and message" do
s = Source.new %(
def bad_Name(a)
end
), "source.cr"
subject.catch(s).should_not be_valid
issue = s.issues.first
issue.rule.should_not be_nil
issue.location.to_s.should eq "source.cr:1:5"
issue.end_location.to_s.should eq "source.cr:1:12"
issue.message.should eq(
"Method name should be underscore-cased: bad_name, not bad_Name"
)
end
end
end

View file

@ -53,16 +53,5 @@ module Ameba::Rule::Style
end
CRYSTAL
end
it "reports rule, pos and message" do
s = Source.new ":nok unless !s.empty?", "source.cr"
subject.catch(s).should_not be_valid
issue = s.issues.first
issue.rule.should_not be_nil
issue.location.to_s.should eq "source.cr:1:1"
issue.end_location.to_s.should eq "source.cr:1:21"
issue.message.should eq "Avoid negated conditions in unless blocks"
end
end
end

View file

@ -0,0 +1,120 @@
require "../../../spec_helper"
module Ameba::Rule::Style
subject = ParenthesesAroundCondition.new
describe ParenthesesAroundCondition do
{% for keyword in %w[if unless while until] %}
context "{{ keyword.id }}" do
it "reports if redundant parentheses are found" do
source = expect_issue subject, <<-CRYSTAL, keyword: {{ keyword }}
%{keyword} (foo > 10)
_{keyword} # ^^^^^^^^^^ error: Redundant parentheses
foo
end
CRYSTAL
expect_correction source, <<-CRYSTAL
{{ keyword.id }} foo > 10
foo
end
CRYSTAL
end
end
{% end %}
context "case" do
it "reports if redundant parentheses are found" do
source = expect_issue subject, <<-CRYSTAL
case (foo = @foo)
# ^^^^^^^^^^^^ error: Redundant parentheses
when String then "string"
when Symbol then "symbol"
end
CRYSTAL
expect_correction source, <<-CRYSTAL
case foo = @foo
when String then "string"
when Symbol then "symbol"
end
CRYSTAL
end
end
context "properties" do
context "#exclude_ternary" do
it "skips ternary control expressions by default" do
expect_no_issues subject, <<-CRYSTAL
(foo > bar) ? true : false
CRYSTAL
end
it "allows to configure assignments" do
rule = ParenthesesAroundCondition.new
rule.exclude_ternary = false
expect_issue rule, <<-CRYSTAL
(foo.empty?) ? true : false
# ^^^^^^^^^^ error: Redundant parentheses
CRYSTAL
expect_no_issues subject, <<-CRYSTAL
(foo && bar) ? true : false
(foo || bar) ? true : false
(foo = @foo) ? true : false
foo == 42 ? true : false
(foo = 42) ? true : false
(foo > 42) ? true : false
(foo >= 42) ? true : false
(3 >= foo >= 42) ? true : false
(3.in? 0..42) ? true : false
(yield 42) ? true : false
(foo rescue 42) ? true : false
CRYSTAL
end
end
context "#allow_safe_assignment" do
it "reports assignments by default" do
expect_issue subject, <<-CRYSTAL
if (foo = @foo)
# ^^^^^^^^^^^^ error: Redundant parentheses
foo
end
CRYSTAL
expect_no_issues subject, <<-CRYSTAL
if !(foo = @foo)
foo
end
CRYSTAL
expect_no_issues subject, <<-CRYSTAL
if foo = @foo
foo
end
CRYSTAL
end
it "allows to configure assignments" do
rule = ParenthesesAroundCondition.new
rule.allow_safe_assignment = true
expect_issue rule, <<-CRYSTAL
if foo = @foo
# ^^^^^^^^^^ error: Missing parentheses
foo
end
CRYSTAL
expect_no_issues rule, <<-CRYSTAL
if (foo = @foo)
foo
end
CRYSTAL
end
end
end
end
end

View file

@ -294,24 +294,5 @@ module Ameba::Rule::Style
}
CRYSTAL
end
it "reports rule, pos and message" do
s = Source.new %q(
def method
begin
open_connection
ensure
close_connection
end
end
), "source.cr"
subject.catch(s).should_not be_valid
issue = s.issues.first
issue.rule.should_not be_nil
issue.location.to_s.should eq "source.cr:2:3"
issue.end_location.to_s.should eq "source.cr:2:7"
issue.message.should eq "Redundant `begin` block detected"
end
end
end

View file

@ -146,7 +146,7 @@ module Ameba::Rule::Style
end
end
context "expception handler" do
context "exception handler" do
it "doesn't report if there is no redundant next in exception handler" do
expect_no_issues subject, <<-CRYSTAL
block do |v|
@ -201,7 +201,7 @@ module Ameba::Rule::Style
end
context "properties" do
context "#allow_multi_next=" do
context "#allow_multi_next" do
it "allows multi next statements by default" do
expect_no_issues subject, <<-CRYSTAL
block do |a, b|
@ -211,7 +211,7 @@ module Ameba::Rule::Style
end
it "allows to configure multi next statements" do
rule = Rule::Style::RedundantNext.new
rule = RedundantNext.new
rule.allow_multi_next = false
source = expect_issue rule, <<-CRYSTAL
block do |a, b|
@ -238,7 +238,7 @@ module Ameba::Rule::Style
end
it "allows to configure empty next statements" do
rule = Rule::Style::RedundantNext.new
rule = RedundantNext.new
rule.allow_empty_next = false
source = expect_issue rule, <<-CRYSTAL
block do

View file

@ -284,7 +284,7 @@ module Ameba::Rule::Style
end
context "properties" do
context "#allow_multi_return=" do
context "#allow_multi_return" do
it "allows multi returns by default" do
expect_no_issues subject, <<-CRYSTAL
def method(a, b)
@ -294,7 +294,7 @@ module Ameba::Rule::Style
end
it "allows to configure multi returns" do
rule = Rule::Style::RedundantReturn.new
rule = RedundantReturn.new
rule.allow_multi_return = false
source = expect_issue rule, <<-CRYSTAL
def method(a, b)
@ -321,7 +321,7 @@ module Ameba::Rule::Style
end
it "allows to configure empty returns" do
rule = Rule::Style::RedundantReturn.new
rule = RedundantReturn.new
rule.allow_empty_return = false
source = expect_issue rule, <<-CRYSTAL
def method

View file

@ -1,68 +0,0 @@
require "../../../spec_helper"
module Ameba
subject = Rule::Style::TypeNames.new
private def it_reports_name(type, name, expected)
it "reports type name #{expected}" do
rule = Rule::Style::TypeNames.new
expect_issue rule, <<-CRYSTAL, type: type, name: name
%{type} %{name}; end
# ^{type}^{name}^^^^ error: Type name should be camelcased: #{expected}, but it was %{name}
CRYSTAL
end
end
describe Rule::Style::TypeNames do
it "passes if type names are camelcased" do
expect_no_issues subject, <<-CRYSTAL
class ParseError < Exception
end
module HTTP
class RequestHandler
end
end
alias NumericValue = Float32 | Float64 | Int32 | Int64
lib LibYAML
end
struct TagDirective
end
enum Time::DayOfWeek
end
CRYSTAL
end
it_reports_name "class", "My_class", "MyClass"
it_reports_name "module", "HTT_p", "HTTP"
it_reports_name "lib", "Lib_YAML", "LibYAML"
it_reports_name "struct", "Tag_directive", "TagDirective"
it_reports_name "enum", "Time_enum::Day_of_week", "TimeEnum::DayOfWeek"
it "reports alias name" do
expect_issue subject, <<-CRYSTAL
alias Numeric_value = Int32
# ^{} error: Type name should be camelcased: NumericValue, but it was Numeric_value
CRYSTAL
end
it "reports rule, pos and message" do
s = Source.new %(
class My_class
end
), "source.cr"
subject.catch(s).should_not be_valid
issue = s.issues.first
issue.rule.should_not be_nil
issue.location.to_s.should eq "source.cr:1:1"
issue.end_location.to_s.should eq "source.cr:2:3"
issue.message.should eq(
"Type name should be camelcased: MyClass, but it was My_class"
)
end
end
end

Some files were not shown because too many files have changed in this diff Show more