java-functional-lsp 0.3.2__tar.gz → 0.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {java_functional_lsp-0.3.2 → java_functional_lsp-0.4.0}/.claude-plugin/plugin.json +1 -1
- {java_functional_lsp-0.3.2 → java_functional_lsp-0.4.0}/PKG-INFO +9 -3
- {java_functional_lsp-0.3.2 → java_functional_lsp-0.4.0}/README.md +8 -2
- {java_functional_lsp-0.3.2 → java_functional_lsp-0.4.0}/SKILL.md +5 -1
- {java_functional_lsp-0.3.2 → java_functional_lsp-0.4.0}/editors/vscode/package.json +1 -1
- {java_functional_lsp-0.3.2 → java_functional_lsp-0.4.0}/pyproject.toml +1 -1
- {java_functional_lsp-0.3.2 → java_functional_lsp-0.4.0}/src/java_functional_lsp/__init__.py +1 -1
- {java_functional_lsp-0.3.2 → java_functional_lsp-0.4.0}/src/java_functional_lsp/analyzers/base.py +24 -0
- {java_functional_lsp-0.3.2 → java_functional_lsp-0.4.0}/src/java_functional_lsp/analyzers/exception_checker.py +18 -2
- {java_functional_lsp-0.3.2 → java_functional_lsp-0.4.0}/src/java_functional_lsp/analyzers/mutation_checker.py +10 -3
- {java_functional_lsp-0.3.2 → java_functional_lsp-0.4.0}/src/java_functional_lsp/cli.py +4 -1
- {java_functional_lsp-0.3.2 → java_functional_lsp-0.4.0}/src/java_functional_lsp/server.py +8 -1
- {java_functional_lsp-0.3.2 → java_functional_lsp-0.4.0}/tests/test_base.py +35 -0
- {java_functional_lsp-0.3.2 → java_functional_lsp-0.4.0}/tests/test_exception_checker.py +42 -0
- {java_functional_lsp-0.3.2 → java_functional_lsp-0.4.0}/tests/test_mutation_checker.py +14 -0
- {java_functional_lsp-0.3.2 → java_functional_lsp-0.4.0}/.github/CODEOWNERS +0 -0
- {java_functional_lsp-0.3.2 → java_functional_lsp-0.4.0}/.github/ISSUE_TEMPLATE/bug-report.md +0 -0
- {java_functional_lsp-0.3.2 → java_functional_lsp-0.4.0}/.github/ISSUE_TEMPLATE/feature-request.md +0 -0
- {java_functional_lsp-0.3.2 → java_functional_lsp-0.4.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {java_functional_lsp-0.3.2 → java_functional_lsp-0.4.0}/.github/SECURITY.md +0 -0
- {java_functional_lsp-0.3.2 → java_functional_lsp-0.4.0}/.github/dependabot.yml +0 -0
- {java_functional_lsp-0.3.2 → java_functional_lsp-0.4.0}/.github/release-drafter.yml +0 -0
- {java_functional_lsp-0.3.2 → java_functional_lsp-0.4.0}/.github/workflows/publish.yml +0 -0
- {java_functional_lsp-0.3.2 → java_functional_lsp-0.4.0}/.github/workflows/release-drafter.yml +0 -0
- {java_functional_lsp-0.3.2 → java_functional_lsp-0.4.0}/.github/workflows/stale.yml +0 -0
- {java_functional_lsp-0.3.2 → java_functional_lsp-0.4.0}/.github/workflows/test.yml +0 -0
- {java_functional_lsp-0.3.2 → java_functional_lsp-0.4.0}/.github/workflows/update-homebrew.yml +0 -0
- {java_functional_lsp-0.3.2 → java_functional_lsp-0.4.0}/.github/workflows/vscode-ext.yml +0 -0
- {java_functional_lsp-0.3.2 → java_functional_lsp-0.4.0}/.gitignore +0 -0
- {java_functional_lsp-0.3.2 → java_functional_lsp-0.4.0}/CONTRIBUTING.md +0 -0
- {java_functional_lsp-0.3.2 → java_functional_lsp-0.4.0}/LICENSE +0 -0
- {java_functional_lsp-0.3.2 → java_functional_lsp-0.4.0}/commands/lint-java.md +0 -0
- {java_functional_lsp-0.3.2 → java_functional_lsp-0.4.0}/editors/intellij/README.md +0 -0
- {java_functional_lsp-0.3.2 → java_functional_lsp-0.4.0}/editors/intellij/lsp4ij-template.json +0 -0
- {java_functional_lsp-0.3.2 → java_functional_lsp-0.4.0}/editors/vscode/.vscodeignore +0 -0
- {java_functional_lsp-0.3.2 → java_functional_lsp-0.4.0}/editors/vscode/README.md +0 -0
- {java_functional_lsp-0.3.2 → java_functional_lsp-0.4.0}/editors/vscode/package-lock.json +0 -0
- {java_functional_lsp-0.3.2 → java_functional_lsp-0.4.0}/editors/vscode/src/extension.ts +0 -0
- {java_functional_lsp-0.3.2 → java_functional_lsp-0.4.0}/editors/vscode/tsconfig.json +0 -0
- {java_functional_lsp-0.3.2 → java_functional_lsp-0.4.0}/hooks/hooks.json +0 -0
- {java_functional_lsp-0.3.2 → java_functional_lsp-0.4.0}/hooks/java_linter_reminder.py +0 -0
- {java_functional_lsp-0.3.2 → java_functional_lsp-0.4.0}/scripts/ensure-lsp.sh +0 -0
- {java_functional_lsp-0.3.2 → java_functional_lsp-0.4.0}/scripts/generate-formula.py +0 -0
- {java_functional_lsp-0.3.2 → java_functional_lsp-0.4.0}/src/java_functional_lsp/analyzers/__init__.py +0 -0
- {java_functional_lsp-0.3.2 → java_functional_lsp-0.4.0}/src/java_functional_lsp/analyzers/null_checker.py +0 -0
- {java_functional_lsp-0.3.2 → java_functional_lsp-0.4.0}/src/java_functional_lsp/analyzers/spring_checker.py +0 -0
- {java_functional_lsp-0.3.2 → java_functional_lsp-0.4.0}/src/java_functional_lsp/proxy.py +0 -0
- {java_functional_lsp-0.3.2 → java_functional_lsp-0.4.0}/tests/__init__.py +0 -0
- {java_functional_lsp-0.3.2 → java_functional_lsp-0.4.0}/tests/conftest.py +0 -0
- {java_functional_lsp-0.3.2 → java_functional_lsp-0.4.0}/tests/test_cli.py +0 -0
- {java_functional_lsp-0.3.2 → java_functional_lsp-0.4.0}/tests/test_config.py +0 -0
- {java_functional_lsp-0.3.2 → java_functional_lsp-0.4.0}/tests/test_null_checker.py +0 -0
- {java_functional_lsp-0.3.2 → java_functional_lsp-0.4.0}/tests/test_proxy.py +0 -0
- {java_functional_lsp-0.3.2 → java_functional_lsp-0.4.0}/tests/test_spring_checker.py +0 -0
- {java_functional_lsp-0.3.2 → java_functional_lsp-0.4.0}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: java-functional-lsp
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Java LSP server enforcing functional programming best practices — null safety, immutability, no exceptions
|
|
5
5
|
Project-URL: Homepage, https://github.com/aviadshiber/java-functional-lsp
|
|
6
6
|
Project-URL: Repository, https://github.com/aviadshiber/java-functional-lsp
|
|
@@ -146,6 +146,7 @@ Create `.java-functional-lsp.json` in your project root to customize rules:
|
|
|
146
146
|
|
|
147
147
|
```json
|
|
148
148
|
{
|
|
149
|
+
"excludes": ["**/generated/**", "**/vendor/**"],
|
|
149
150
|
"rules": {
|
|
150
151
|
"null-literal-arg": "warning",
|
|
151
152
|
"throw-statement": "info",
|
|
@@ -155,8 +156,13 @@ Create `.java-functional-lsp.json` in your project root to customize rules:
|
|
|
155
156
|
}
|
|
156
157
|
```
|
|
157
158
|
|
|
158
|
-
|
|
159
|
-
|
|
159
|
+
**Options:**
|
|
160
|
+
- `excludes` — glob patterns for files/directories to skip entirely (supports `**` for multi-segment wildcards)
|
|
161
|
+
- `rules` — per-rule severity: `error`, `warning` (default), `info`, `hint`, `off`
|
|
162
|
+
|
|
163
|
+
**Spring-aware behavior:**
|
|
164
|
+
- `throw-statement` and `catch-rethrow` are automatically suppressed inside `@Bean` methods
|
|
165
|
+
- `mutable-dto` suggests `@ConstructorBinding` instead of `@Value` when the class has `@ConfigurationProperties`
|
|
160
166
|
|
|
161
167
|
## How it works
|
|
162
168
|
|
|
@@ -118,6 +118,7 @@ Create `.java-functional-lsp.json` in your project root to customize rules:
|
|
|
118
118
|
|
|
119
119
|
```json
|
|
120
120
|
{
|
|
121
|
+
"excludes": ["**/generated/**", "**/vendor/**"],
|
|
121
122
|
"rules": {
|
|
122
123
|
"null-literal-arg": "warning",
|
|
123
124
|
"throw-statement": "info",
|
|
@@ -127,8 +128,13 @@ Create `.java-functional-lsp.json` in your project root to customize rules:
|
|
|
127
128
|
}
|
|
128
129
|
```
|
|
129
130
|
|
|
130
|
-
|
|
131
|
-
|
|
131
|
+
**Options:**
|
|
132
|
+
- `excludes` — glob patterns for files/directories to skip entirely (supports `**` for multi-segment wildcards)
|
|
133
|
+
- `rules` — per-rule severity: `error`, `warning` (default), `info`, `hint`, `off`
|
|
134
|
+
|
|
135
|
+
**Spring-aware behavior:**
|
|
136
|
+
- `throw-statement` and `catch-rethrow` are automatically suppressed inside `@Bean` methods
|
|
137
|
+
- `mutable-dto` suggests `@ConstructorBinding` instead of `@Value` when the class has `@ConfigurationProperties`
|
|
132
138
|
|
|
133
139
|
## How it works
|
|
134
140
|
|
|
@@ -45,6 +45,7 @@ Create `.java-functional-lsp.json` in your project root:
|
|
|
45
45
|
|
|
46
46
|
```json
|
|
47
47
|
{
|
|
48
|
+
"excludes": ["**/generated/**", "**/vendor/**"],
|
|
48
49
|
"rules": {
|
|
49
50
|
"imperative-loop": "hint",
|
|
50
51
|
"mutable-variable": "info",
|
|
@@ -53,7 +54,10 @@ Create `.java-functional-lsp.json` in your project root:
|
|
|
53
54
|
}
|
|
54
55
|
```
|
|
55
56
|
|
|
56
|
-
|
|
57
|
+
- `excludes` — glob patterns to skip files/directories entirely
|
|
58
|
+
- `rules` — per-rule severity: `error`, `warning` (default), `info`, `hint`, `off`
|
|
59
|
+
- `throw-statement`/`catch-rethrow` auto-suppressed in `@Bean` methods
|
|
60
|
+
- `mutable-dto` suggests `@ConstructorBinding` for `@ConfigurationProperties` classes
|
|
57
61
|
|
|
58
62
|
## On-Demand Linting
|
|
59
63
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "java-functional-lsp",
|
|
3
3
|
"displayName": "Java Functional LSP",
|
|
4
4
|
"description": "Java LSP server enforcing functional programming best practices — null safety, immutability, no exceptions",
|
|
5
|
-
"version": "0.
|
|
5
|
+
"version": "0.4.0",
|
|
6
6
|
"publisher": "aviadshiber",
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"engines": {
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "java-functional-lsp"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.4.0"
|
|
8
8
|
description = "Java LSP server enforcing functional programming best practices — null safety, immutability, no exceptions"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { text = "MIT" }
|
{java_functional_lsp-0.3.2 → java_functional_lsp-0.4.0}/src/java_functional_lsp/analyzers/base.py
RENAMED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import fnmatch
|
|
5
6
|
from collections.abc import Generator
|
|
6
7
|
from dataclasses import dataclass
|
|
7
8
|
from enum import IntEnum
|
|
@@ -147,3 +148,26 @@ def severity_from_config(config: dict[str, Any], rule_id: str, default: Severity
|
|
|
147
148
|
"info": Severity.INFO,
|
|
148
149
|
"hint": Severity.HINT,
|
|
149
150
|
}.get(level, default)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def is_excluded(path_str: str, patterns: list[str]) -> bool:
|
|
154
|
+
"""Return True if path matches any exclude glob pattern.
|
|
155
|
+
|
|
156
|
+
Patterns support ** for multi-segment wildcards (e.g. **/generated/**).
|
|
157
|
+
Uses fnmatch which handles ** correctly across Python 3.10+.
|
|
158
|
+
"""
|
|
159
|
+
normalized = path_str.replace("\\", "/")
|
|
160
|
+
return any(fnmatch.fnmatch(normalized, pattern) for pattern in patterns)
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def has_sibling_annotation(modifiers_node: Node, annotation_name: bytes) -> bool:
|
|
164
|
+
"""Check if a modifiers node contains an annotation with the given name.
|
|
165
|
+
|
|
166
|
+
Checks both marker_annotation (@Foo) and annotation (@Foo(...)) forms.
|
|
167
|
+
"""
|
|
168
|
+
for child in modifiers_node.named_children:
|
|
169
|
+
if child.type in ("marker_annotation", "annotation"):
|
|
170
|
+
name_node = child.child_by_field_name("name")
|
|
171
|
+
if name_node is not None and name_node.text == annotation_name:
|
|
172
|
+
return True
|
|
173
|
+
return False
|
|
@@ -4,7 +4,7 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
from typing import Any
|
|
6
6
|
|
|
7
|
-
from .base import Diagnostic, find_nodes, severity_from_config
|
|
7
|
+
from .base import Diagnostic, find_nodes, has_sibling_annotation, severity_from_config
|
|
8
8
|
|
|
9
9
|
_MESSAGES = {
|
|
10
10
|
"throw-statement": ("Avoid throwing exceptions. Use Either.left(error) or Try.of(() -> ...).toEither()."),
|
|
@@ -14,6 +14,19 @@ _MESSAGES = {
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
|
|
17
|
+
def _is_in_bean_method(node: Any) -> bool:
|
|
18
|
+
"""Check if node is inside a method annotated with @Bean."""
|
|
19
|
+
parent = node.parent
|
|
20
|
+
while parent:
|
|
21
|
+
if parent.type == "method_declaration":
|
|
22
|
+
modifiers = next((c for c in parent.children if c.type == "modifiers"), None)
|
|
23
|
+
if modifiers and has_sibling_annotation(modifiers, b"Bean"):
|
|
24
|
+
return True
|
|
25
|
+
return False
|
|
26
|
+
parent = parent.parent
|
|
27
|
+
return False
|
|
28
|
+
|
|
29
|
+
|
|
17
30
|
class ExceptionChecker:
|
|
18
31
|
"""Detects throw statements and catch-rethrow anti-patterns."""
|
|
19
32
|
|
|
@@ -24,6 +37,8 @@ class ExceptionChecker:
|
|
|
24
37
|
severity = severity_from_config(config, "throw-statement")
|
|
25
38
|
if severity is not None:
|
|
26
39
|
for node in find_nodes(tree.root_node, "throw_statement"):
|
|
40
|
+
if _is_in_bean_method(node):
|
|
41
|
+
continue
|
|
27
42
|
diagnostics.append(
|
|
28
43
|
Diagnostic(
|
|
29
44
|
line=node.start_point[0],
|
|
@@ -40,10 +55,11 @@ class ExceptionChecker:
|
|
|
40
55
|
severity = severity_from_config(config, "catch-rethrow")
|
|
41
56
|
if severity is not None:
|
|
42
57
|
for node in find_nodes(tree.root_node, "catch_clause"):
|
|
58
|
+
if _is_in_bean_method(node):
|
|
59
|
+
continue
|
|
43
60
|
body = node.child_by_field_name("body")
|
|
44
61
|
if body is None:
|
|
45
62
|
continue
|
|
46
|
-
# Check if the block has exactly one named statement and it's a throw
|
|
47
63
|
statements = [c for c in body.named_children if c.type not in ("line_comment", "block_comment")]
|
|
48
64
|
if len(statements) == 1 and statements[0].type == "throw_statement":
|
|
49
65
|
diagnostics.append(
|
|
@@ -4,7 +4,7 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
from typing import Any
|
|
6
6
|
|
|
7
|
-
from .base import Diagnostic, find_nodes, find_nodes_multi, has_ancestor, severity_from_config
|
|
7
|
+
from .base import Diagnostic, find_nodes, find_nodes_multi, has_ancestor, has_sibling_annotation, severity_from_config
|
|
8
8
|
|
|
9
9
|
_MESSAGES = {
|
|
10
10
|
"mutable-variable": "Avoid reassigning variables. Use final + functional transforms (map, flatMap, fold).",
|
|
@@ -45,8 +45,15 @@ class MutationChecker:
|
|
|
45
45
|
if ann_text in (b"Data", b"Setter"):
|
|
46
46
|
# Verify it's on a class declaration
|
|
47
47
|
if node.parent and node.parent.type == "modifiers":
|
|
48
|
-
|
|
48
|
+
modifiers = node.parent
|
|
49
|
+
grandparent = modifiers.parent
|
|
49
50
|
if grandparent and grandparent.type == "class_declaration":
|
|
51
|
+
if has_sibling_annotation(modifiers, b"ConfigurationProperties"):
|
|
52
|
+
message = (
|
|
53
|
+
"Use @ConstructorBinding instead of @Data/@Setter for @ConfigurationProperties classes."
|
|
54
|
+
)
|
|
55
|
+
else:
|
|
56
|
+
message = _MESSAGES["mutable-dto"]
|
|
50
57
|
diagnostics.append(
|
|
51
58
|
Diagnostic(
|
|
52
59
|
line=name_node.start_point[0],
|
|
@@ -55,7 +62,7 @@ class MutationChecker:
|
|
|
55
62
|
end_col=name_node.end_point[1],
|
|
56
63
|
severity=severity,
|
|
57
64
|
code="mutable-dto",
|
|
58
|
-
message=
|
|
65
|
+
message=message,
|
|
59
66
|
)
|
|
60
67
|
)
|
|
61
68
|
|
|
@@ -7,7 +7,7 @@ import sys
|
|
|
7
7
|
from pathlib import Path
|
|
8
8
|
from typing import Any
|
|
9
9
|
|
|
10
|
-
from .analyzers.base import Analyzer, Diagnostic, Severity, get_parser
|
|
10
|
+
from .analyzers.base import Analyzer, Diagnostic, Severity, get_parser, is_excluded
|
|
11
11
|
from .analyzers.exception_checker import ExceptionChecker
|
|
12
12
|
from .analyzers.mutation_checker import MutationChecker
|
|
13
13
|
from .analyzers.null_checker import NullChecker
|
|
@@ -107,7 +107,10 @@ def main() -> None:
|
|
|
107
107
|
config = load_config(files[0])
|
|
108
108
|
|
|
109
109
|
total_diags = 0
|
|
110
|
+
excludes: list[str] = config.get("excludes", [])
|
|
110
111
|
for path in files:
|
|
112
|
+
if excludes and is_excluded(path.as_posix(), excludes):
|
|
113
|
+
continue
|
|
111
114
|
diags = check_file(path, config)
|
|
112
115
|
for d in diags:
|
|
113
116
|
print(format_diagnostic(path, d))
|
|
@@ -16,7 +16,7 @@ import cattrs
|
|
|
16
16
|
from lsprotocol import types as lsp
|
|
17
17
|
from pygls.lsp.server import LanguageServer
|
|
18
18
|
|
|
19
|
-
from .analyzers.base import Analyzer, Severity, get_parser
|
|
19
|
+
from .analyzers.base import Analyzer, Severity, get_parser, is_excluded
|
|
20
20
|
from .analyzers.base import Diagnostic as LintDiagnostic
|
|
21
21
|
from .analyzers.exception_checker import ExceptionChecker
|
|
22
22
|
from .analyzers.mutation_checker import MutationChecker
|
|
@@ -96,6 +96,13 @@ def _to_lsp_diagnostic(diag: LintDiagnostic) -> lsp.Diagnostic:
|
|
|
96
96
|
|
|
97
97
|
def _analyze_document(source_text: str, uri: str = "") -> list[lsp.Diagnostic]:
|
|
98
98
|
"""Run all custom analyzers on the given source text. Uses incremental parsing when possible."""
|
|
99
|
+
# Check excludes before parsing
|
|
100
|
+
if uri:
|
|
101
|
+
excludes: list[str] = server._config.get("excludes", [])
|
|
102
|
+
if excludes:
|
|
103
|
+
path_str = _uri_to_path(uri)
|
|
104
|
+
if is_excluded(path_str, excludes):
|
|
105
|
+
return []
|
|
99
106
|
source_bytes = source_text.encode("utf-8")
|
|
100
107
|
old_tree = server._trees.get(uri) if uri else None
|
|
101
108
|
tree = server._parser.parse(source_bytes, old_tree) if old_tree else server._parser.parse(source_bytes)
|
|
@@ -7,6 +7,8 @@ from java_functional_lsp.analyzers.base import (
|
|
|
7
7
|
find_nodes_multi,
|
|
8
8
|
get_parser,
|
|
9
9
|
has_ancestor,
|
|
10
|
+
has_sibling_annotation,
|
|
11
|
+
is_excluded,
|
|
10
12
|
)
|
|
11
13
|
|
|
12
14
|
|
|
@@ -120,3 +122,36 @@ class TestCollectNodesByType:
|
|
|
120
122
|
buckets = collect_nodes_by_type(tree.root_node, {"null_literal", "throw_statement"})
|
|
121
123
|
assert len(buckets["null_literal"]) == 0
|
|
122
124
|
assert len(buckets["throw_statement"]) == 0
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
class TestIsExcluded:
|
|
128
|
+
def test_matches_double_star_pattern(self):
|
|
129
|
+
assert is_excluded("/home/user/project/nlu-trs-client-shaded/Foo.java", ["**/nlu-trs-client-shaded/**"])
|
|
130
|
+
|
|
131
|
+
def test_no_match(self):
|
|
132
|
+
assert not is_excluded("/home/user/project/src/Foo.java", ["**/nlu-trs-client-shaded/**"])
|
|
133
|
+
|
|
134
|
+
def test_empty_patterns(self):
|
|
135
|
+
assert not is_excluded("/any/path/Foo.java", [])
|
|
136
|
+
|
|
137
|
+
def test_multiple_patterns(self):
|
|
138
|
+
assert is_excluded("/project/generated/Model.java", ["**/shaded/**", "**/generated/**"])
|
|
139
|
+
|
|
140
|
+
def test_windows_backslashes_normalized(self):
|
|
141
|
+
assert is_excluded("C:\\project\\generated\\Model.java", ["**/generated/**"])
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
class TestHasSiblingAnnotation:
|
|
145
|
+
def test_finds_sibling_annotation(self):
|
|
146
|
+
tree = _parse("@ConfigurationProperties @Setter class Props { String name; }")
|
|
147
|
+
for node in find_nodes(tree.root_node, "marker_annotation"):
|
|
148
|
+
name = node.child_by_field_name("name")
|
|
149
|
+
if name and name.text == b"Setter" and node.parent:
|
|
150
|
+
assert has_sibling_annotation(node.parent, b"ConfigurationProperties")
|
|
151
|
+
|
|
152
|
+
def test_no_sibling(self):
|
|
153
|
+
tree = _parse("@Setter class Props { String name; }")
|
|
154
|
+
for node in find_nodes(tree.root_node, "marker_annotation"):
|
|
155
|
+
name = node.child_by_field_name("name")
|
|
156
|
+
if name and name.text == b"Setter" and node.parent:
|
|
157
|
+
assert not has_sibling_annotation(node.parent, b"ConfigurationProperties")
|
|
@@ -60,3 +60,45 @@ class TestCatchRethrow:
|
|
|
60
60
|
"""
|
|
61
61
|
diags = parse_and_analyze(ExceptionChecker(), source)
|
|
62
62
|
assert not any(d.code == "catch-rethrow" for d in diags)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class TestBeanSuppression:
|
|
66
|
+
def test_ignores_throw_in_bean_method(self) -> None:
|
|
67
|
+
source = b"""
|
|
68
|
+
class Config {
|
|
69
|
+
@Bean
|
|
70
|
+
DataSource dataSource() {
|
|
71
|
+
if (url == null) {
|
|
72
|
+
throw new IllegalStateException("url required");
|
|
73
|
+
}
|
|
74
|
+
return new DataSource(url);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
"""
|
|
78
|
+
diags = parse_and_analyze(ExceptionChecker(), source)
|
|
79
|
+
assert not any(d.code == "throw-statement" for d in diags)
|
|
80
|
+
|
|
81
|
+
def test_flags_throw_in_regular_method(self) -> None:
|
|
82
|
+
source = b"""
|
|
83
|
+
class Service {
|
|
84
|
+
void process() {
|
|
85
|
+
throw new RuntimeException("error");
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
"""
|
|
89
|
+
diags = parse_and_analyze(ExceptionChecker(), source)
|
|
90
|
+
assert any(d.code == "throw-statement" for d in diags)
|
|
91
|
+
|
|
92
|
+
def test_ignores_catch_rethrow_in_bean_method(self) -> None:
|
|
93
|
+
source = b"""
|
|
94
|
+
class Config {
|
|
95
|
+
@Bean
|
|
96
|
+
DataSource dataSource() {
|
|
97
|
+
try { return connect(); }
|
|
98
|
+
catch (Exception e) { throw new RuntimeException(e); }
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
"""
|
|
102
|
+
diags = parse_and_analyze(ExceptionChecker(), source)
|
|
103
|
+
assert not any(d.code == "catch-rethrow" for d in diags)
|
|
104
|
+
assert not any(d.code == "throw-statement" for d in diags)
|
|
@@ -58,6 +58,20 @@ class TestMutableDto:
|
|
|
58
58
|
diags = parse_and_analyze(MutationChecker(), source)
|
|
59
59
|
assert not any(d.code == "mutable-dto" for d in diags)
|
|
60
60
|
|
|
61
|
+
def test_config_properties_suggests_constructor_binding(self) -> None:
|
|
62
|
+
source = b"@ConfigurationProperties @Setter class Props { String name; }"
|
|
63
|
+
diags = parse_and_analyze(MutationChecker(), source)
|
|
64
|
+
dto_diags = [d for d in diags if d.code == "mutable-dto"]
|
|
65
|
+
assert len(dto_diags) == 1
|
|
66
|
+
assert "@ConstructorBinding" in dto_diags[0].message
|
|
67
|
+
|
|
68
|
+
def test_regular_setter_suggests_value(self) -> None:
|
|
69
|
+
source = b"@Setter class Foo { String name; }"
|
|
70
|
+
diags = parse_and_analyze(MutationChecker(), source)
|
|
71
|
+
dto_diags = [d for d in diags if d.code == "mutable-dto"]
|
|
72
|
+
assert len(dto_diags) == 1
|
|
73
|
+
assert "@Value" in dto_diags[0].message
|
|
74
|
+
|
|
61
75
|
|
|
62
76
|
class TestImperativeOptionUnwrap:
|
|
63
77
|
def test_detects_is_defined_get(self) -> None:
|
|
File without changes
|
{java_functional_lsp-0.3.2 → java_functional_lsp-0.4.0}/.github/ISSUE_TEMPLATE/bug-report.md
RENAMED
|
File without changes
|
{java_functional_lsp-0.3.2 → java_functional_lsp-0.4.0}/.github/ISSUE_TEMPLATE/feature-request.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{java_functional_lsp-0.3.2 → java_functional_lsp-0.4.0}/.github/workflows/release-drafter.yml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{java_functional_lsp-0.3.2 → java_functional_lsp-0.4.0}/.github/workflows/update-homebrew.yml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{java_functional_lsp-0.3.2 → java_functional_lsp-0.4.0}/editors/intellij/lsp4ij-template.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|