Skip to content

Commit 1e6cb14

Browse files
committed
Merge remote-tracking branch 'upstream/master' into typst-fixes
2 parents 89e1951 + 1a2e72a commit 1e6cb14

File tree

27 files changed

+238
-92
lines changed

27 files changed

+238
-92
lines changed

.github/workflows/fuzz.yml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
name: Fuzz
2+
3+
on:
4+
schedule:
5+
- cron: "*/10 * * * *"
6+
7+
env:
8+
CARGO_TERM_COLOR: always
9+
# Run for 100 times the default
10+
QUICKCHECK_TESTS: 10000
11+
12+
jobs:
13+
precommit:
14+
runs-on: ubuntu-latest
15+
steps:
16+
- uses: actions/checkout@v4
17+
with:
18+
ref: harden-title-case
19+
- uses: extractions/setup-just@v2
20+
- name: Test
21+
run: cargo test

.github/workflows/precommit.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ on:
88

99
env:
1010
CARGO_TERM_COLOR: always
11+
# Run for 100 times the default
12+
QUICKCHECK_TESTS: 10000
1113

1214
jobs:
1315
precommit:

harper-cli/src/main.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ use harper_core::parsers::{Markdown, MarkdownOptions};
1111
use harper_core::{remove_overlaps, Dictionary, Document, FstDictionary, TokenKind};
1212
use harper_literate_haskell::LiterateHaskellParser;
1313

14+
/// A debugging tool for the Harper grammar checker.
1415
#[derive(Debug, Parser)]
16+
#[command(version, about)]
1517
enum Args {
1618
/// Lint a provided document.
1719
Lint {

harper-comments/src/comment_parsers/jsdoc.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ pub(super) fn mark_inline_tags(tokens: &mut [Token]) {
121121
}
122122
}
123123

124-
/// Checks if the provided token slice begins with an inline tag, returning it's
124+
/// Checks if the provided token slice begins with an inline tag, returning its
125125
/// end if so.
126126
fn parse_inline_tag(tokens: &[Token]) -> Option<usize> {
127127
if !matches!(

harper-core/dictionary.dict

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49771,3 +49771,4 @@ Harper/SM
4977149771
a8c/SM
4977249772
a11n/1
4977349773
a12s/9
49774+
intergenerational

harper-core/src/lexing/email_address.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ pub fn lex_email_address(source: &[char]) -> Option<FoundToken> {
1616

1717
let domain_part_len = lex_hostname(&source[at_loc + 1..])?;
1818

19+
if domain_part_len == 0 {
20+
return None;
21+
}
22+
1923
Some(FoundToken {
2024
next_index: at_loc + 1 + domain_part_len,
2125
token: TokenKind::EmailAddress,
@@ -155,6 +159,18 @@ mod tests {
155159
}
156160
}
157161

162+
#[test]
163+
fn does_not_allow_empty_domain() {
164+
for local in example_local_parts() {
165+
// Generate invalid email address
166+
let mut address = local.clone();
167+
address.push('@');
168+
address.push(' ');
169+
170+
assert!(lex_email_address(&address).is_none());
171+
}
172+
}
173+
158174
/// Tests that the email parser will not throw a panic under some random
159175
/// situations.
160176
#[test]

harper-core/src/lexing/hostname.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,13 @@ pub fn lex_hostname_token(source: &[char]) -> Option<FoundToken> {
2828
pub fn lex_hostname(source: &[char]) -> Option<usize> {
2929
let mut passed_chars = 0;
3030

31+
// The beginning has different requirements from the rest of the hostname.
32+
let first = source.first()?;
33+
34+
if !matches!(first, 'A'..='Z' | 'a'..='z' | '0'..='9' ) {
35+
return None;
36+
}
37+
3138
for label in source.split(|c| *c == '.') {
3239
for c in label {
3340
passed_chars += 1;
@@ -78,4 +85,10 @@ pub mod tests {
7885
assert_eq!(lex_hostname(&domain), Some(domain.len()));
7986
}
8087
}
88+
89+
#[test]
90+
fn hyphen_cannot_open_hostname() {
91+
let host: Vec<_> = "-something.com".chars().collect();
92+
assert!(lex_hostname(&host).is_none())
93+
}
8194
}

harper-core/src/linting/pronoun_contraction/avoid_contraction.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,14 @@ impl PatternLinter for AvoidContraction {
3434
vec!['y', 'o', 'u', 'r'],
3535
word,
3636
)],
37-
message: "I appears you intended to use the possessive version of this word".to_owned(),
37+
message: "It appears you intended to use the possessive version of this word"
38+
.to_owned(),
3839
priority: 63,
3940
}
4041
}
4142

4243
fn description(&self) -> &'static str {
43-
"This rule looks for situations where a contraction was used where it shouldn't."
44+
"This rule looks for situations where a contraction was used where it shouldn't have been."
4445
}
4546
}
4647

harper-core/src/linting/spelled_numbers.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ impl Linter for SpelledNumbers {
3535
}
3636
}
3737

38-
/// Converts a number to it's spelled-out variant.
38+
/// Converts a number to its spelled-out variant.
3939
///
4040
/// For example: 100 -> one hundred.
4141
///

harper-core/src/parsers/markdown.rs

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ impl Markdown {
3737

3838
/// Remove hidden Wikilink target text.
3939
///
40-
/// As in, the stuff to the left of the pipe operator:
40+
/// As in the stuff to the left of the pipe operator:
4141
///
4242
/// ```markdown
4343
/// [[Target text|Display Text]]
@@ -46,6 +46,10 @@ impl Markdown {
4646
let mut to_remove = VecDeque::new();
4747

4848
for pipe_idx in tokens.iter_pipe_indices() {
49+
if pipe_idx < 2 {
50+
continue;
51+
}
52+
4953
// Locate preceding `[[`
5054
let mut cursor = pipe_idx - 2;
5155
let mut open_bracket = None;
@@ -351,6 +355,35 @@ mod tests {
351355
))
352356
}
353357

358+
#[test]
359+
fn just_pipe() {
360+
let source = r"|";
361+
362+
let tokens = Markdown::default().parse_str(source);
363+
364+
let token_kinds = tokens.iter().map(|t| t.kind).collect::<Vec<_>>();
365+
366+
dbg!(&token_kinds);
367+
368+
assert!(matches!(
369+
token_kinds.as_slice(),
370+
&[TokenKind::Punctuation(Punctuation::Pipe)]
371+
))
372+
}
373+
374+
#[test]
375+
fn empty_wikilink_text() {
376+
let source = r"[[|]]";
377+
378+
let tokens = Markdown::default().parse_str(source);
379+
380+
let token_kinds = tokens.iter().map(|t| t.kind).collect::<Vec<_>>();
381+
382+
dbg!(&token_kinds);
383+
384+
assert!(matches!(token_kinds.as_slice(), &[]))
385+
}
386+
354387
#[test]
355388
fn improper_wikilink_text() {
356389
let source = r"this is shown|this is also shown]]";

harper-core/src/parsers/mask.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ where
3535
let mut last_allowed: Option<Span> = None;
3636

3737
for (span, content) in mask.iter_allowed(source) {
38-
// Check if there was a line break between the last chunk.
38+
// Check for a line break separating the current chunk from the preceding one.
3939
if let Some(last_allowed) = last_allowed {
4040
let intervening = Span::new(last_allowed.end, span.start);
4141

harper-core/src/patterns/is_not_title_case.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,7 @@ impl<D: Dictionary> Pattern for IsNotTitleCase<D> {
2424
}
2525

2626
let matched_chars = tokens[0..inner_match].span().unwrap().get_content(source);
27-
28-
if make_title_case(tokens, source, &self.dict) != matched_chars {
27+
if make_title_case(&tokens[0..inner_match], source, &self.dict) != matched_chars {
2928
inner_match
3029
} else {
3130
0

0 commit comments

Comments
 (0)