Skip to content

Commit 54c096a

Browse files
feat(core): many more Weir rules (#2742)
* feat(core): a ton of new Weir rules * chore(core): move to the proper folder
1 parent 7e517f2 commit 54c096a

37 files changed

Lines changed: 1265 additions & 0 deletions

harper-core/src/linting/closed_compounds.rs

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ pub fn lint_group() -> LintGroup {
2525
"Anyhow" => ("any how", "anyhow"),
2626
"Anywhere" => ("any where", "anywhere"),
2727
"Backplane" => ("back plane", "backplane"),
28+
"Bypass" => ("by pass", "bypass"),
29+
"Deadlift" => ("dead lift", "deadlift"),
2830
"Desktop" => ("desk top", "desktop"),
2931
"Devops" => ("dev ops", "devops"),
3032
"Everybody" => ("every body", "everybody"),
@@ -37,6 +39,8 @@ pub fn lint_group() -> LintGroup {
3739
"Instead" => ("in stead", "instead"),
3840
"Intact" => ("in tact", "intact"),
3941
"Itself" => ("it self", "itself"),
42+
"Keystroke" => ("key stoke", "keystroke"),
43+
"Keystrokes" => ("key stokes", "keystrokes"),
4044
"Laptop" => ("lap top", "laptop"),
4145
"Middleware" => ("middle ware", "middleware"),
4246
"Misunderstand" => ("miss understand", "misunderstand"),
@@ -59,17 +63,21 @@ pub fn lint_group() -> LintGroup {
5963
"Postpone" => ("post pone", "postpone"),
6064
"Proofread" => ("proof read", "proofread"),
6165
"Regardless" => ("regard less", "regardless"),
66+
"Shortcoming" => ("short coming", "shortcoming"),
67+
"Shortcomings" => ("short comings", "shortcomings"),
6268
"Somebody" => ("some body", "somebody"),
6369
"Somehow" => ("some how", "somehow"),
6470
"Someone" => ("some one", "someone"),
6571
"Somewhere" => ("some where", "somewhere"),
72+
"There" => ("the re", "there"),
6673
"Therefore" => ("there fore", "therefore"),
6774
"Thereupon" => ("there upon", "thereupon"),
6875
"Underclock" => ("under clock", "underclock"),
6976
"Upset" => ("up set", "upset"),
7077
"Upward" => ("up ward", "upward"),
7178
"Whereupon" => ("where upon", "whereupon"),
7279
"Widespread" => ("wide spread", "widespread"),
80+
"Without" => ("with out", "without"),
7381
"Worldwide" => ("world wide", "worldwide"),
7482
});
7583

@@ -223,4 +231,60 @@ mod tests {
223231
let expected = "They set off on their journey overnight.";
224232
assert_suggestion_result(test_sentence, lint_group(), expected);
225233
}
234+
235+
#[test]
236+
fn by_pass() {
237+
let test_sentence = "Please by pass this check for now.";
238+
let expected = "Please bypass this check for now.";
239+
assert_suggestion_result(test_sentence, lint_group(), expected);
240+
}
241+
242+
#[test]
243+
fn dead_lift() {
244+
let test_sentence = "I can dead lift 200 kg.";
245+
let expected = "I can deadlift 200 kg.";
246+
assert_suggestion_result(test_sentence, lint_group(), expected);
247+
}
248+
249+
#[test]
250+
fn key_stoke() {
251+
let test_sentence = "Use this key stoke to open search.";
252+
let expected = "Use this keystroke to open search.";
253+
assert_suggestion_result(test_sentence, lint_group(), expected);
254+
}
255+
256+
#[test]
257+
fn key_stokes() {
258+
let test_sentence = "These key stokes are hard to memorize.";
259+
let expected = "These keystrokes are hard to memorize.";
260+
assert_suggestion_result(test_sentence, lint_group(), expected);
261+
}
262+
263+
#[test]
264+
fn with_out() {
265+
let test_sentence = "We left with out a map.";
266+
let expected = "We left without a map.";
267+
assert_suggestion_result(test_sentence, lint_group(), expected);
268+
}
269+
270+
#[test]
271+
fn the_re() {
272+
let test_sentence = "The re are too many popups on this page.";
273+
let expected = "There are too many popups on this page.";
274+
assert_suggestion_result(test_sentence, lint_group(), expected);
275+
}
276+
277+
#[test]
278+
fn short_coming() {
279+
let test_sentence = "That bug is a short coming in the current release.";
280+
let expected = "That bug is a shortcoming in the current release.";
281+
assert_suggestion_result(test_sentence, lint_group(), expected);
282+
}
283+
284+
#[test]
285+
fn short_comings() {
286+
let test_sentence = "We listed three short comings in the postmortem.";
287+
let expected = "We listed three shortcomings in the postmortem.";
288+
assert_suggestion_result(test_sentence, lint_group(), expected);
289+
}
226290
}

weir_rules/AtLeasToLeast.weir

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
expr main <([(at leas)]), leas>
2+
3+
let message "Use the standard phrase with `least` here."
4+
let description "Fixes the frequent typo `at leas` when the intended expression is `at least`."
5+
let kind "Typo"
6+
let becomes "least"
7+
let strategy "MatchCase"
8+
9+
# True positives
10+
11+
test "At leas we shipped the patch." "At least we shipped the patch."
12+
test "at leas I remembered to push." "at least I remembered to push."
13+
test "We can, at leas, retry tomorrow." "We can, at least, retry tomorrow."
14+
test "AT LEAS TRY AGAIN." "AT LEAST TRY AGAIN."
15+
test "I guess at leas one test should pass." "I guess at least one test should pass."
16+
test "At leas, this error is reproducible." "At least, this error is reproducible."
17+
18+
# True negatives / false positives
19+
20+
test "At least we shipped the patch." "At least we shipped the patch."
21+
test "I have a leas on that apartment." "I have a leas on that apartment."
22+
test "Please at leisure review the draft." "Please at leisure review the draft."
23+
test "The least risky option won." "The least risky option won."
24+
test "They said: \"at leash\" as a joke." "They said: \"at leash\" as a joke."
25+
test "The release is at, leas, in notes." "The release is at, leas, in notes."
26+
27+
# Boundary and false-negative coverage
28+
29+
test "At leas? Maybe." "At least? Maybe."
30+
test "If not now, at leas tomorrow." "If not now, at least tomorrow."
31+
test "We are at leas halfway done." "We are at least halfway done."
32+
test "At leas!" "At least!"
33+
test "I wrote \"at leas\" twice: at leas." "I wrote \"at least\" twice: at least."
34+
test "Looking at least-cost models." "Looking at least-cost models."
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
expr main [(believe to)]
2+
3+
let message "This verb usually takes `in` here."
4+
let description "Replaces `believe to` with `believe in` in common object-taking usage."
5+
let kind "Grammar"
6+
let becomes "believe in"
7+
let strategy "MatchCase"
8+
9+
# True positives
10+
11+
test "I believe to this plan." "I believe in this plan."
12+
test "They believe to ghosts." "They believe in ghosts."
13+
test "Many teens believe to luck." "Many teens believe in luck."
14+
test "Do you believe to fate?" "Do you believe in fate?"
15+
test "We still believe to science." "We still believe in science."
16+
test "I believe to your potential." "I believe in your potential."
17+
test "BELIEVE TO YOURSELF FOR ONCE." "BELIEVE IN YOURSELF FOR ONCE."
18+
test "I wonder if you believe to miracles." "I wonder if you believe in miracles."
19+
20+
# True negatives / false positives
21+
22+
allows "I believe in this plan."
23+
allows "They believe that this plan works."
24+
allows "I choose to believe this story."
25+
allows "We believe in each other."
26+
allows "Do you believe in fate?"
27+
28+
# Known false positives (current boundary)
29+
30+
test "I believe to this day that we were right." "I believe in this day that we were right."
31+
test "I believe to this moment it is true." "I believe in this moment it is true."
32+
33+
# Potential false negatives (accepted gaps)
34+
35+
allows "She believes to her team."
36+
allows "We believed to that strategy for years."
37+
allows "He is believing to the mission again."

weir_rules/BetterOffPhrase.weir

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
expr main (better of)
2+
3+
let message "This expression usually takes `off` instead of `of`."
4+
let description "Rewrites `better of` to `better off` in common comparative phrasing."
5+
let kind "Grammar"
6+
let becomes "better off"
7+
let strategy "MatchCase"
8+
9+
# True positives
10+
11+
test "People who are better of can donate more." "People who are better off can donate more."
12+
test "You will be better of waiting until tomorrow." "You will be better off waiting until tomorrow."
13+
test "I feel better of without that app." "I feel better off without that app."
14+
test "We are better of focusing on quality." "We are better off focusing on quality."
15+
test "They were better of before the update." "They were better off before the update."
16+
test "She is far better of now." "She is far better off now."
17+
test "He'd be better of at home." "He'd be better off at home."
18+
test "The team is BETTER OF with clear goals." "The team is BETTER OFF with clear goals."
19+
test "Are we better of if we delay launch?" "Are we better off if we delay launch?"
20+
21+
# True negatives / false-positive checks
22+
23+
test "People who are better off can donate more." "People who are better off can donate more."
24+
test "This is a matter of better options." "This is a matter of better options."
25+
test "Of course this is better overall." "Of course this is better overall."
26+
test "He offered better outcomes, not quick fixes." "He offered better outcomes, not quick fixes."
27+
test "They got off the train early." "They got off the train early."
28+
test "A better offer arrived yesterday." "A better offer arrived yesterday."
29+
30+
# Boundary / accepted limits
31+
32+
allows "She chose the better option from the list."
33+
allows "Pick the best design from these three mockups."
34+
allows "They discussed old and new methods in detail."

weir_rules/BluRayHyphen.weir

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
expr main (blu ray)
2+
3+
let message "Use the hyphenated spelling for this media format."
4+
let description "Joins the two-word spelling of the optical disc format into the standard compound form."
5+
let kind "Punctuation"
6+
let becomes "blu-ray"
7+
let strategy "MatchCase"
8+
9+
# True positives
10+
11+
test "I ordered a Blu ray player." "I ordered a Blu-ray player."
12+
test "This movie ships on blu ray." "This movie ships on blu-ray."
13+
test "Do you still buy BLU RAY discs?" "Do you still buy BLU-RAY discs?"
14+
test "A Blu ray drive is installed." "A Blu-ray drive is installed."
15+
test "They found cheap blu ray movies." "They found cheap blu-ray movies."
16+
test "BLU RAY" "BLU-RAY"
17+
18+
# False positives / true negatives
19+
20+
test "I ordered a Blu-ray player." "I ordered a Blu-ray player."
21+
test "The ray looked blue in that photo." "The ray looked blue in that photo."
22+
test "We watched a DVD last night." "We watched a DVD last night."
23+
test "The Blu rays are on the shelf." "The Blu rays are on the shelf."
24+
test "A blur ay effect appeared in the lens." "A blur ay effect appeared in the lens."
25+
test "The brand name is Blue Ray Labs." "The brand name is Blue Ray Labs."
26+
27+
# False negatives and edge behavior checks
28+
29+
test "blu ray" "blu-ray"
30+
test "Blu ray, DVD, and VHS." "Blu-ray, DVD, and VHS."
31+
test "I keep one blu ray." "I keep one blu-ray."
32+
test "Many people said BLU ray still works." "Many people said BLU-ray still works."

weir_rules/CauseItIsBecause.weir

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
expr main (cause it is)
2+
3+
let message "Prefer `because it is` in this clause."
4+
let description "Normalizes informal `cause it is` to the standard subordinating form in explanatory clauses."
5+
let kind "Style"
6+
let becomes "because it is"
7+
let strategy "MatchCase"
8+
9+
# True positives
10+
11+
test "Cause it is late, we should leave." "Because it is late, we should leave."
12+
test "cause it is noisy, I use headphones." "because it is noisy, I use headphones."
13+
test "I stayed home cause it is raining." "I stayed home because it is raining."
14+
test "We paused deployment cause it is unstable." "We paused deployment because it is unstable."
15+
test "CAUSE IT IS CLEAR NOW." "BECAUSE IT IS CLEAR NOW."
16+
test "Cause it is simple, ship it." "Because it is simple, ship it."
17+
test "They agreed cause it is practical." "They agreed because it is practical."
18+
19+
# False negatives (intentionally out of scope)
20+
21+
test "Cause it was late, we should leave." "Cause it was late, we should leave."
22+
test "Cause it's late, we should leave." "Cause it's late, we should leave."
23+
test "Cause it has changed, reread docs." "Cause it has changed, reread docs."
24+
test "Cause it had failed, we rolled back." "Cause it had failed, we rolled back."
25+
26+
# True negatives / false positives
27+
28+
test "Because it is late, we should leave." "Because it is late, we should leave."
29+
test "Stress can cause it is not obvious." "Stress can because it is not obvious."
30+
test "The cause it is unclear." "The because it is unclear."
31+
test "They discuss cause and effect." "They discuss cause and effect."
32+
test "Because it was late, we should leave." "Because it was late, we should leave."
33+
test "No one said cause it is wrong." "No one said because it is wrong."

weir_rules/ColdModalTypo.weir

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
expr subjects [i, you, we, they, he, she, it]
2+
expr baseVerbs [be, have, do, go, get, make, take, see, say, know, think, find, use, work, help, start, stop, leave, feel, need, want]
3+
expr main <([(@subjects cold @baseVerbs), (@subjects cold ADV @baseVerbs), (@subjects cold not @baseVerbs), (@subjects cold never @baseVerbs), (@subjects cold ADV not @baseVerbs)]), cold>
4+
5+
let message "This looks like a modal typo."
6+
let description "Rewrites `cold` to `could` when it appears in common subject-plus-verb modal contexts."
7+
let kind "Typo"
8+
let becomes "could"
9+
let strategy "MatchCase"
10+
11+
# True positives
12+
13+
test "I cold go now." "I could go now."
14+
test "You cold make dinner tonight." "You could make dinner tonight."
15+
test "We cold take the train." "We could take the train."
16+
test "They cold see the issue." "They could see the issue."
17+
test "He cold feel the difference." "He could feel the difference."
18+
test "She cold help us later." "She could help us later."
19+
test "It cold work this time." "It could work this time."
20+
test "I cold not find the setting." "I could not find the setting."
21+
test "We cold never know the reason." "We could never know the reason."
22+
test "They cold really use a break." "They could really use a break."
23+
test "I COLD GO IF NEEDED." "I COULD GO IF NEEDED."
24+
25+
# False positives / true negatives
26+
27+
allows "It was cold outside all day."
28+
allows "A cold drink helped a lot."
29+
allows "He caught a cold last week."
30+
allows "The soup went cold quickly."
31+
allows "I enjoy cold brew coffee."
32+
allows "She gave me a cold stare."
33+
34+
# False negatives (accepted limits)
35+
36+
allows "I cold call prospects each morning."
37+
allows "You cold text me later."
38+
allows "They cold build a prototype fast."

weir_rules/DoubleCheckHyphen.weir

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
expr main (double check)
2+
3+
let message "Use the hyphenated form in this compound."
4+
let description "Normalizes the common two-word form `double check` to `double-check`."
5+
let kind "Punctuation"
6+
let becomes "double-check"
7+
let strategy "MatchCase"
8+
9+
# True positives
10+
11+
test "Please double check the report before sending." "Please double-check the report before sending."
12+
test "I always double check numbers in production." "I always double-check numbers in production."
13+
test "Can you double check this again?" "Can you double-check this again?"
14+
test "We should double check with legal." "We should double-check with legal."
15+
test "You need to double check every file." "You need to double-check every file."
16+
test "DOUBLE CHECK THE FINAL TOTAL." "DOUBLE-CHECK THE FINAL TOTAL."
17+
test "double check this quickly." "double-check this quickly."
18+
test "If unsure, double check." "If unsure, double-check."
19+
20+
# True negatives / false positives
21+
22+
test "Please double-click the button." "Please double-click the button."
23+
test "Please double click the button." "Please double click the button."
24+
test "They double checked the config after deploy." "They double checked the config after deploy."
25+
test "We are double checking every migration." "We are double checking every migration."
26+
test "She double checks each invoice manually." "She double checks each invoice manually."
27+
test "A careful check is still needed." "A careful check is still needed."
28+
test "The team did one more review." "The team did one more review."
29+
test "double-check the report before sending." "double-check the report before sending."
30+
test "Double-checking takes discipline." "Double-checking takes discipline."
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
expr main <((each others) [NOUN, PROPN, ADJ, DET]), (each others)>
2+
3+
let message "Use the reciprocal possessive form here."
4+
let description "Rewrites `each others` to `each other's` when it modifies a following noun phrase."
5+
let kind "Grammar"
6+
let becomes "each other's"
7+
let strategy "MatchCase"
8+
9+
# True positives
10+
11+
test "They reviewed each others code before release." "They reviewed each other's code before release."
12+
test "We checked each others notes after class." "We checked each other's notes after class."
13+
test "The two teams respected each others traditions." "The two teams respected each other's traditions."
14+
test "The siblings borrowed each others bikes." "The siblings borrowed each other's bikes."
15+
test "Please verify each others entries." "Please verify each other's entries."
16+
test "They corrected each others grammar in chat." "They corrected each other's grammar in chat."
17+
test "Each others schedules kept drifting." "Each other's schedules kept drifting."
18+
test "EACH OTHERS PROJECTS WERE MERGED." "EACH OTHER'S PROJECTS WERE MERGED."
19+
test "Writers should respect each others voices." "Writers should respect each other's voices."
20+
test "Designers critique each others drafts weekly." "Designers critique each other's drafts weekly."
21+
22+
# False positives / true negatives
23+
24+
test "They reviewed each other's code before release." "They reviewed each other's code before release."
25+
test "They reviewed each other during rehearsals." "They reviewed each other during rehearsals."
26+
test "Others' opinions were shared in the thread." "Others' opinions were shared in the thread."
27+
test "Each other was mentioned in the report." "Each other was mentioned in the report."
28+
test "They checked others code before release." "They checked others code before release."
29+
test "They checked others' code before release." "They checked others' code before release."
30+
31+
# False negatives (known limitation: does not target misspelled reciprocal phrases with extra apostrophes)
32+
33+
allows "They reviewed each others' code before release."
34+
allows "They corrected each others' grammar in chat."

0 commit comments

Comments
 (0)