Skip to content

Commit 431b32c

Browse files
committed
git merge conflict indicator error message
1 parent e8eb002 commit 431b32c

4 files changed

Lines changed: 362 additions & 95 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,10 @@
9999
when building large projects.
100100
([John Downey](https://github.com/jtdowney))
101101

102+
- The compiler now provides a more specific error message when encountering a
103+
merge conflict indicator
104+
([0xda157](https://github.com/0xda157))
105+
102106
### Language server
103107

104108
- The language server now supports go-to-definition, find-references and rename

compiler-core/src/parse/error.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ pub enum LexicalErrorType {
4646
tok: char,
4747
},
4848
InvalidTripleEqual,
49+
MergeConflictIndicator,
4950
/// A character was encountered that visually looks like a correct
5051
/// character, but in reality it's some other unicode characters.
5152
/// For example, a non-breaking-space instead of a regular space.
@@ -952,6 +953,10 @@ impl LexicalError {
952953
"See: https://tour.gleam.run/basics/equality".into(),
953954
],
954955
),
956+
LexicalErrorType::MergeConflictIndicator => (
957+
"I don't know how to handle this",
958+
vec!["Hint: resolve merge conflicts".into()],
959+
),
955960
LexicalErrorType::VisuallySimilarInvalidCharacter { name, correct } => (
956961
"Unexpected character",
957962
vec![wrap(&format!(

compiler-core/src/parse/lexer.rs

Lines changed: 197 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -196,24 +196,50 @@ where
196196
'=' => {
197197
let tok_start = self.get_pos();
198198
let _ = self.next_char();
199-
match self.chr0 {
200-
Some('=') => {
201-
let _ = self.next_char();
202-
let tok_end = self.get_pos();
203-
if let Some('=') = self.chr0 {
204-
return Err(LexicalError {
205-
error: LexicalErrorType::InvalidTripleEqual,
206-
location: SrcSpan {
207-
start: tok_start,
208-
end: tok_end + 1,
209-
},
210-
});
211-
}
212-
self.emit((tok_start, Token::EqualEqual, tok_end));
199+
200+
if self.chr0 != Some('=') {
201+
let tok_end = self.get_pos();
202+
self.emit((tok_start, Token::Equal, tok_end));
203+
return Ok(());
204+
}
205+
206+
let _ = self.next_char();
207+
208+
if self.chr0 != Some('=') {
209+
self.emit((tok_start, Token::EqualEqual, self.get_pos()));
210+
return Ok(());
211+
}
212+
213+
let _ = self.next_char();
214+
let mut seen_eq = 3;
215+
216+
loop {
217+
if seen_eq >= 7 {
218+
return Err(LexicalError {
219+
error: LexicalErrorType::MergeConflictIndicator,
220+
location: SrcSpan {
221+
start: tok_start,
222+
end: self.get_pos(),
223+
},
224+
});
213225
}
214-
_ => {
215-
let tok_end = self.get_pos();
216-
self.emit((tok_start, Token::Equal, tok_end));
226+
227+
if self.chr0 == Some('=') {
228+
let _ = self.next_char();
229+
seen_eq += 1;
230+
} else {
231+
// we've found at least three `=`s, but not
232+
// enough to be a merge conflict indicator,
233+
// so we just return the triple equals error
234+
//
235+
// TODO: provide a better error for 3 < '='s < 7
236+
return Err(LexicalError {
237+
error: LexicalErrorType::InvalidTripleEqual,
238+
location: SrcSpan {
239+
start: tok_start,
240+
end: tok_start + 3,
241+
},
242+
});
217243
}
218244
}
219245
}
@@ -353,84 +379,8 @@ where
353379
':' => {
354380
self.eat_single_char(Token::Colon);
355381
}
356-
'<' => {
357-
let tok_start = self.get_pos();
358-
let _ = self.next_char();
359-
match self.chr0 {
360-
Some('>') => {
361-
let _ = self.next_char();
362-
let tok_end = self.get_pos();
363-
self.emit((tok_start, Token::Concatenate, tok_end));
364-
}
365-
Some('<') => {
366-
let _ = self.next_char();
367-
let tok_end = self.get_pos();
368-
self.emit((tok_start, Token::LtLt, tok_end));
369-
}
370-
Some('.') => {
371-
let _ = self.next_char();
372-
let tok_end = self.get_pos();
373-
self.emit((tok_start, Token::LessDot, tok_end));
374-
}
375-
Some('-') => {
376-
let _ = self.next_char();
377-
let tok_end = self.get_pos();
378-
self.emit((tok_start, Token::LArrow, tok_end));
379-
}
380-
Some('=') => {
381-
let _ = self.next_char();
382-
match self.chr0 {
383-
Some('.') => {
384-
let _ = self.next_char();
385-
let tok_end = self.get_pos();
386-
self.emit((tok_start, Token::LessEqualDot, tok_end));
387-
}
388-
_ => {
389-
let tok_end = self.get_pos();
390-
self.emit((tok_start, Token::LessEqual, tok_end));
391-
}
392-
}
393-
}
394-
_ => {
395-
let tok_end = self.get_pos();
396-
self.emit((tok_start, Token::Less, tok_end));
397-
}
398-
}
399-
}
400-
'>' => {
401-
let tok_start = self.get_pos();
402-
let _ = self.next_char();
403-
match self.chr0 {
404-
Some('>') => {
405-
let _ = self.next_char();
406-
let tok_end = self.get_pos();
407-
self.emit((tok_start, Token::GtGt, tok_end));
408-
}
409-
Some('.') => {
410-
let _ = self.next_char();
411-
let tok_end = self.get_pos();
412-
self.emit((tok_start, Token::GreaterDot, tok_end));
413-
}
414-
Some('=') => {
415-
let _ = self.next_char();
416-
match self.chr0 {
417-
Some('.') => {
418-
let _ = self.next_char();
419-
let tok_end = self.get_pos();
420-
self.emit((tok_start, Token::GreaterEqualDot, tok_end));
421-
}
422-
_ => {
423-
let tok_end = self.get_pos();
424-
self.emit((tok_start, Token::GreaterEqual, tok_end));
425-
}
426-
}
427-
}
428-
_ => {
429-
let tok_end = self.get_pos();
430-
self.emit((tok_start, Token::Greater, tok_end));
431-
}
432-
}
433-
}
382+
'<' => return self.lex_lt(),
383+
'>' => return self.lex_gt(),
434384
',' => {
435385
self.eat_single_char(Token::Comma);
436386
}
@@ -838,6 +788,158 @@ where
838788
Ok(())
839789
}
840790

791+
// Once we've found a `<`, proceed from there
792+
fn lex_lt(&mut self) -> Result<(), LexicalError> {
793+
let mut tok_start = self.get_pos();
794+
let _ = self.next_char();
795+
match self.chr0 {
796+
Some('>') => {
797+
let _ = self.next_char();
798+
let tok_end = self.get_pos();
799+
self.emit((tok_start, Token::Concatenate, tok_end));
800+
}
801+
Some('<') => {
802+
let _ = self.next_char();
803+
804+
if self.chr0 != Some('<') {
805+
self.emit((tok_start, Token::LtLt, self.get_pos()));
806+
return Ok(());
807+
}
808+
809+
let _ = self.next_char();
810+
let mut seen_lt = 3;
811+
812+
loop {
813+
if seen_lt >= 7 {
814+
return Err(LexicalError {
815+
error: LexicalErrorType::MergeConflictIndicator,
816+
location: SrcSpan {
817+
start: tok_start,
818+
end: self.get_pos(),
819+
},
820+
});
821+
}
822+
823+
if self.chr0 == Some('<') {
824+
let _ = self.next_char();
825+
seen_lt += 1;
826+
} else {
827+
while seen_lt > 1 {
828+
self.emit((tok_start, Token::LtLt, tok_start + 2));
829+
tok_start += 2;
830+
seen_lt -= 2;
831+
}
832+
if seen_lt == 1 {
833+
return self.lex_lt();
834+
} else {
835+
return Ok(());
836+
}
837+
}
838+
}
839+
}
840+
Some('.') => {
841+
let _ = self.next_char();
842+
let tok_end = self.get_pos();
843+
self.emit((tok_start, Token::LessDot, tok_end));
844+
}
845+
Some('-') => {
846+
let _ = self.next_char();
847+
let tok_end = self.get_pos();
848+
self.emit((tok_start, Token::LArrow, tok_end));
849+
}
850+
Some('=') => {
851+
let _ = self.next_char();
852+
match self.chr0 {
853+
Some('.') => {
854+
let _ = self.next_char();
855+
let tok_end = self.get_pos();
856+
self.emit((tok_start, Token::LessEqualDot, tok_end));
857+
}
858+
_ => {
859+
let tok_end = self.get_pos();
860+
self.emit((tok_start, Token::LessEqual, tok_end));
861+
}
862+
}
863+
}
864+
_ => {
865+
let tok_end = self.get_pos();
866+
self.emit((tok_start, Token::Less, tok_end));
867+
}
868+
}
869+
Ok(())
870+
}
871+
872+
// once we've found a `>`
873+
fn lex_gt(&mut self) -> Result<(), LexicalError> {
874+
let mut tok_start = self.get_pos();
875+
let _ = self.next_char();
876+
match self.chr0 {
877+
Some('>') => {
878+
let _ = self.next_char();
879+
let tok_end = self.get_pos();
880+
881+
if self.chr0 != Some('>') {
882+
self.emit((tok_start, Token::GtGt, tok_end));
883+
return Ok(());
884+
}
885+
886+
let _ = self.next_char();
887+
let mut seen_gt = 3;
888+
889+
loop {
890+
if seen_gt >= 7 {
891+
return Err(LexicalError {
892+
error: LexicalErrorType::MergeConflictIndicator,
893+
location: SrcSpan {
894+
start: tok_start,
895+
end: self.get_pos(),
896+
},
897+
});
898+
}
899+
900+
if self.chr0 == Some('>') {
901+
let _ = self.next_char();
902+
seen_gt += 1;
903+
} else {
904+
while seen_gt > 1 {
905+
self.emit((tok_start, Token::GtGt, tok_start + 2));
906+
tok_start += 2;
907+
seen_gt -= 2;
908+
}
909+
if seen_gt > 0 {
910+
return self.lex_lt();
911+
} else {
912+
return Ok(());
913+
}
914+
}
915+
}
916+
}
917+
Some('.') => {
918+
let _ = self.next_char();
919+
let tok_end = self.get_pos();
920+
self.emit((tok_start, Token::GreaterDot, tok_end));
921+
}
922+
Some('=') => {
923+
let _ = self.next_char();
924+
match self.chr0 {
925+
Some('.') => {
926+
let _ = self.next_char();
927+
let tok_end = self.get_pos();
928+
self.emit((tok_start, Token::GreaterEqualDot, tok_end));
929+
}
930+
_ => {
931+
let tok_end = self.get_pos();
932+
self.emit((tok_start, Token::GreaterEqual, tok_end));
933+
}
934+
}
935+
}
936+
_ => {
937+
let tok_end = self.get_pos();
938+
self.emit((tok_start, Token::Greater, tok_end));
939+
}
940+
}
941+
return Ok(());
942+
}
841943
// Lexer helper functions:
842944
// this can be either a reserved word, or a name
843945
fn lex_name(&mut self) -> LexResult {

0 commit comments

Comments
 (0)