feat: add comprehensive formatting rules and type inference guidelines

This commit is contained in:
overtrue
2025-05-28 16:04:38 +08:00
parent f05a0b8e83
commit 8747a91df8
28 changed files with 792 additions and 383 deletions
+59 -25
View File
@@ -193,12 +193,18 @@ mod tests {
let valid_parts = ['a', 'A', '0', '9', '_', '#', '@', '$', 'α', '中'];
for start_char in valid_starts {
assert!(dialect.is_identifier_start(start_char),
"Character '{}' should be valid identifier start", start_char);
assert!(
dialect.is_identifier_start(start_char),
"Character '{}' should be valid identifier start",
start_char
);
for part_char in valid_parts {
assert!(dialect.is_identifier_part(part_char),
"Character '{}' should be valid identifier part", part_char);
assert!(
dialect.is_identifier_part(part_char),
"Character '{}' should be valid identifier part",
part_char
);
}
}
}
@@ -211,8 +217,14 @@ mod tests {
assert!(!dialect.is_identifier_start('\0'), "Null character should not be valid identifier start");
assert!(!dialect.is_identifier_part('\0'), "Null character should not be valid identifier part");
assert!(!dialect.is_identifier_start('\x01'), "Control character should not be valid identifier start");
assert!(!dialect.is_identifier_part('\x01'), "Control character should not be valid identifier part");
assert!(
!dialect.is_identifier_start('\x01'),
"Control character should not be valid identifier start"
);
assert!(
!dialect.is_identifier_part('\x01'),
"Control character should not be valid identifier part"
);
assert!(!dialect.is_identifier_start('\x7F'), "DEL character should not be valid identifier start");
assert!(!dialect.is_identifier_part('\x7F'), "DEL character should not be valid identifier part");
@@ -226,10 +238,12 @@ mod tests {
let unicode_letters = ['α', 'β', 'γ', 'Α', 'Β', 'Γ', '中', '文', '日', '本', 'ñ', 'ü', 'ç'];
for ch in unicode_letters {
assert!(dialect.is_identifier_start(ch),
"Unicode letter '{}' should be valid identifier start", ch);
assert!(dialect.is_identifier_part(ch),
"Unicode letter '{}' should be valid identifier part", ch);
assert!(
dialect.is_identifier_start(ch),
"Unicode letter '{}' should be valid identifier start",
ch
);
assert!(dialect.is_identifier_part(ch), "Unicode letter '{}' should be valid identifier part", ch);
}
}
@@ -239,10 +253,16 @@ mod tests {
// Test all ASCII digits
for digit in '0'..='9' {
assert!(!dialect.is_identifier_start(digit),
"ASCII digit '{}' should not be valid identifier start", digit);
assert!(dialect.is_identifier_part(digit),
"ASCII digit '{}' should be valid identifier part", digit);
assert!(
!dialect.is_identifier_start(digit),
"ASCII digit '{}' should not be valid identifier start",
digit
);
assert!(
dialect.is_identifier_part(digit),
"ASCII digit '{}' should be valid identifier part",
digit
);
}
}
@@ -252,14 +272,16 @@ mod tests {
// Test that all valid identifier starts are also valid identifier parts
let test_chars = [
'a', 'A', 'z', 'Z', '_', '#', '@', 'α', '中', 'ñ',
'0', '9', '$', ' ', '.', ',', ';', '(', ')', '=', '+', '-'
'a', 'A', 'z', 'Z', '_', '#', '@', 'α', '中', 'ñ', '0', '9', '$', ' ', '.', ',', ';', '(', ')', '=', '+', '-',
];
for ch in test_chars {
if dialect.is_identifier_start(ch) {
assert!(dialect.is_identifier_part(ch),
"Character '{}' that is valid identifier start should also be valid identifier part", ch);
assert!(
dialect.is_identifier_part(ch),
"Character '{}' that is valid identifier start should also be valid identifier part",
ch
);
}
}
}
@@ -285,7 +307,10 @@ mod tests {
assert!(!dialect_ref.is_identifier_start('0'), "Trait method should work for invalid start");
assert!(dialect_ref.is_identifier_part('a'), "Trait method should work for valid part");
assert!(dialect_ref.is_identifier_part('0'), "Trait method should work for digit part");
assert!(dialect_ref.supports_group_by_expr(), "Trait method should return true for GROUP BY support");
assert!(
dialect_ref.supports_group_by_expr(),
"Trait method should return true for GROUP BY support"
);
}
#[test]
@@ -297,13 +322,22 @@ mod tests {
let test_chars = ['a', 'A', '0', '_', '#', '@', '$', ' ', '.'];
for ch in test_chars {
assert_eq!(dialect1.is_identifier_start(ch), dialect2.is_identifier_start(ch),
"Different instances should behave the same for is_identifier_start");
assert_eq!(dialect1.is_identifier_part(ch), dialect2.is_identifier_part(ch),
"Different instances should behave the same for is_identifier_part");
assert_eq!(
dialect1.is_identifier_start(ch),
dialect2.is_identifier_start(ch),
"Different instances should behave the same for is_identifier_start"
);
assert_eq!(
dialect1.is_identifier_part(ch),
dialect2.is_identifier_part(ch),
"Different instances should behave the same for is_identifier_part"
);
}
assert_eq!(dialect1.supports_group_by_expr(), dialect2.supports_group_by_expr(),
"Different instances should behave the same for supports_group_by_expr");
assert_eq!(
dialect1.supports_group_by_expr(),
dialect2.supports_group_by_expr(),
"Different instances should behave the same for supports_group_by_expr"
);
}
}
+8 -2
View File
@@ -90,7 +90,10 @@ mod tests {
let _builder = CascadeOptimizerBuilder::default();
// Test that builder can be created successfully
assert!(std::mem::size_of::<CascadeOptimizerBuilder>() > 0, "Builder should be created successfully");
assert!(
std::mem::size_of::<CascadeOptimizerBuilder>() > 0,
"Builder should be created successfully"
);
}
#[test]
@@ -109,7 +112,10 @@ mod tests {
// Test that we can call builder methods (even if we don't have mock implementations)
// This tests the builder pattern itself
assert!(std::mem::size_of::<CascadeOptimizerBuilder>() > 0, "Builder should be created successfully");
assert!(
std::mem::size_of::<CascadeOptimizerBuilder>() > 0,
"Builder should be created successfully"
);
}
#[test]
+19 -19
View File
@@ -104,7 +104,7 @@ mod tests {
assert!(std::mem::size_of::<DefaultParser>() == 0, "Parser should be zero-sized");
}
#[test]
#[test]
fn test_default_parser_simple_select() {
let parser = DefaultParser::default();
let sql = "SELECT * FROM S3Object";
@@ -119,11 +119,11 @@ mod tests {
match &statements[0] {
ExtStatement::SqlStatement(_) => {
// Successfully parsed as SQL statement
},
}
}
}
#[test]
#[test]
fn test_default_parser_select_with_columns() {
let parser = DefaultParser::default();
let sql = "SELECT id, name, age FROM S3Object";
@@ -137,11 +137,11 @@ mod tests {
match &statements[0] {
ExtStatement::SqlStatement(_) => {
// Successfully parsed as SQL statement
},
}
}
}
#[test]
#[test]
fn test_default_parser_select_with_where() {
let parser = DefaultParser::default();
let sql = "SELECT * FROM S3Object WHERE age > 25";
@@ -155,7 +155,7 @@ mod tests {
match &statements[0] {
ExtStatement::SqlStatement(_) => {
// Successfully parsed as SQL statement
},
}
}
}
@@ -248,7 +248,7 @@ mod tests {
assert!(result.is_ok(), "ExtParser::new_with_dialect should work");
}
#[test]
#[test]
fn test_ext_parser_complex_query() {
let sql = "SELECT id, name, age FROM S3Object WHERE age > 25 AND department = 'IT' ORDER BY age DESC LIMIT 10";
@@ -261,11 +261,11 @@ mod tests {
match &statements[0] {
ExtStatement::SqlStatement(_) => {
// Successfully parsed as SQL statement
},
}
}
}
#[test]
#[test]
fn test_ext_parser_aggregate_functions() {
let sql = "SELECT COUNT(*), AVG(age), MAX(salary) FROM S3Object GROUP BY department";
@@ -278,7 +278,7 @@ mod tests {
match &statements[0] {
ExtStatement::SqlStatement(_) => {
// Successfully parsed as SQL statement
},
}
}
}
@@ -348,14 +348,14 @@ mod tests {
assert_eq!(statements.len(), 1, "Should have exactly one statement");
}
#[test]
#[test]
fn test_ext_parser_error_handling() {
let invalid_sqls = vec![
"SELECT FROM", // Missing column list
"SELECT * FROM", // Missing table name
"SELECT * FROM S3Object WHERE", // Incomplete WHERE clause
"SELECT * FROM S3Object GROUP", // Incomplete GROUP BY
"SELECT * FROM S3Object ORDER", // Incomplete ORDER BY
"SELECT FROM", // Missing column list
"SELECT * FROM", // Missing table name
"SELECT * FROM S3Object WHERE", // Incomplete WHERE clause
"SELECT * FROM S3Object GROUP", // Incomplete GROUP BY
"SELECT * FROM S3Object ORDER", // Incomplete ORDER BY
];
for sql in invalid_sqls {
@@ -402,7 +402,7 @@ mod tests {
assert_eq!(statements.len(), 1, "Should have exactly one statement");
}
#[test]
#[test]
fn test_parser_err_macro() {
let error: Result<()> = parser_err!("Test error message");
assert!(error.is_err(), "parser_err! macro should create error");
@@ -410,7 +410,7 @@ mod tests {
match error {
Err(ParserError::ParserError(msg)) => {
assert_eq!(msg, "Test error message", "Error message should match");
},
}
_ => panic!("Expected ParserError::ParserError"),
}
}
@@ -428,7 +428,7 @@ mod tests {
Err(ParserError::ParserError(msg)) => {
assert!(msg.contains("Expected test token"), "Error should contain expected message");
assert!(msg.contains("found: found token"), "Error should contain found message");
},
}
_ => panic!("Expected ParserError::ParserError"),
}
}