near-miss-clone
Blocks of code are alike but for a few names or literals.
- Group
- Opportunities
- Runs
- Runs under ur report:
ur report --rule near-miss-clone - Level
- note
- Reads
- Every file
- Fix
- None: the shared function's name, parameters, and home are a design choice
Why
These runs of statements, or match arms, are the same once a few names, fields, or literals are holes, and the finding lists the values each hole takes. A name each block binds for itself is a renaming, not a hole. One function that takes the holes as parameters, or a table of their values, would replace them. Only classes of at most three holes are reported. It reads every language whose grammar states its shapes in a Shapes.rsc; an advisory rule that ur report runs.
The fix, as Ur suggests it: write one function that takes the values that differ as parameters, or a table of them, and use it at each site.
For agents
Before copying a block and changing a few names or literals, make the block a function of those values, or a loop over a table of them.
Example
accounts/forms.pydef validate_signup(form):
errors = {}
value = form.data.get("email", "").strip()
if not value:
errors["email"] = "This field is required."
elif len(value) > 254:
errors["email"] = "Email addresses are at most 254 characters."
value = form.data.get("username", "").strip()
if not value:
errors["username"] = "This field is required."
elif len(value) > 32:
errors["username"] = "Usernames are at most 32 characters."
value = form.data.get("display_name", "").strip()
if not value:
errors["display_name"] = "This field is required."
elif len(value) > 64:
errors["display_name"] = "Display names are at most 64 characters."
return errors
$ ur report --rule near-miss-clone
opportunity[near-miss-clone]: 3 runs of 2 statements are alike but for 3 values: `"display_name"` / `"email"` / `"username"`; `254` / `32` / `64`; `"Display names are at most 64 characters."` / `"Email addresses are at most 254 characters."` / `"Usernames are at most 32 characters."` (saves about 6 lines)
--> ./accounts/forms.py:4:5-8 in `validate_signup`
--> ./accounts/forms.py:10:5-14 in `validate_signup`
--> ./accounts/forms.py:16:5-20 in `validate_signup`
= hint: write one function that takes the values that differ as parameters, or a table of them, and use it at each site
1 opportunity saving about 6 lines in all