From daaa83d3a421a326cc5f1a24baff35afeee6e688 Mon Sep 17 00:00:00 2001 From: AmrDeveloper Date: Sun, 12 Jan 2025 15:01:43 +0100 Subject: [PATCH] Implement `m_oneof`, `m_allof` and `m_noneof` combine matchers functions --- .github/workflows/ci.yaml | 13 ++- docs/MatcherFunctions.md | 10 ++- src/clang_ql/functions/matchers/combine.rs | 96 ++++++++++++++++++++++ src/clang_ql/functions/matchers/mod.rs | 3 + src/clang_ql/matchers/combine.rs | 72 ++++++++++++++++ src/clang_ql/matchers/mod.rs | 1 + 6 files changed, 193 insertions(+), 2 deletions(-) create mode 100644 src/clang_ql/functions/matchers/combine.rs diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index be9469e..02bd2ba 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -18,4 +18,15 @@ jobs: steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable - - run: cargo fmt --all -- --check \ No newline at end of file + - run: cargo fmt --all -- --check + + clippy: + name: Clippy + runs-on: ubuntu-latest + steps: + - run: wget https://apt.llvm.org/llvm.sh + - run: chmod +x llvm.sh + - run: sudo ./llvm.sh 18 + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - run: cargo clippy -- -D warnings \ No newline at end of file diff --git a/docs/MatcherFunctions.md b/docs/MatcherFunctions.md index e644075..eb4ad40 100644 --- a/docs/MatcherFunctions.md +++ b/docs/MatcherFunctions.md @@ -16,4 +16,12 @@ | m_destructor | () | FunctionMatcher | Create Matcher to match function is destructor | | m_public | () | FunctionMatcher | Create Matcher to match public function | | m_protected | () | FunctionMatcher | Create Matcher to match protected function | -| m_private | () | FunctionMatcher | Create Matcher to match private function | \ No newline at end of file +| m_private | () | FunctionMatcher | Create Matcher to match private function | + +### Combine matchers functions + +| Function | Parameters | Return | Description | +| :------: | :----------------------: | :-------------: | :----------------------------------------------------------------: | +| m_oneof | (n : ...FunctionMatcher) | FunctionMatcher | Create a matcher that returns true if any sub matcher is true | +| m_allof | (n : ...FunctionMatcher) | FunctionMatcher | Create a matcher that returns true if all of sub matcher are true | +| m_noneof | (n : ...FunctionMatcher) | FunctionMatcher | Create a matcher that returns true if none of sub matcher are true | diff --git a/src/clang_ql/functions/matchers/combine.rs b/src/clang_ql/functions/matchers/combine.rs new file mode 100644 index 0000000..d7c0dc4 --- /dev/null +++ b/src/clang_ql/functions/matchers/combine.rs @@ -0,0 +1,96 @@ +use std::collections::HashMap; + +use gitql_ast::types::dynamic::DynamicType; +use gitql_ast::types::varargs::VarargsType; +use gitql_ast::types::variant::VariantType; +use gitql_core::signature::Signature; +use gitql_core::signature::StandardFunction; +use gitql_core::values::base::Value; +use gitql_std::meta_types::first_element_type; + +use crate::clang_ql::matchers::CombineMatcher; +use crate::clang_ql::matchers::Matcher; +use crate::clang_ql::types::FunctionMatcherType; +use crate::clang_ql::values::FunctionMatcherValue; +use crate::clang_ql::values::FunctionNode; + +#[inline(always)] +pub(crate) fn register_combine_matchers_functions( + map: &mut HashMap<&'static str, StandardFunction>, +) { + map.insert("m_oneof", matcher_combine_oneof); + map.insert("m_allof", matcher_combine_allof); + map.insert("m_noneof", matcher_combine_noneof); +} + +#[inline(always)] +pub(crate) fn register_combine_matchers_signatures(map: &mut HashMap<&'static str, Signature>) { + map.insert( + "m_oneof", + Signature::with_return(Box::new(DynamicType::new(first_element_type))) + .add_parameter(Box::new(VariantType::new(vec![Box::new( + FunctionMatcherType, + )]))) + .add_parameter(Box::new(VarargsType::new(Box::new(DynamicType::new( + first_element_type, + ))))), + ); + + map.insert( + "m_allof", + Signature::with_return(Box::new(DynamicType::new(first_element_type))) + .add_parameter(Box::new(VariantType::new(vec![Box::new( + FunctionMatcherType, + )]))) + .add_parameter(Box::new(VarargsType::new(Box::new(DynamicType::new( + first_element_type, + ))))), + ); + + map.insert( + "m_noneof", + Signature::with_return(Box::new(DynamicType::new(first_element_type))) + .add_parameter(Box::new(VariantType::new(vec![Box::new( + FunctionMatcherType, + )]))) + .add_parameter(Box::new(VarargsType::new(Box::new(DynamicType::new( + first_element_type, + ))))), + ); +} + +fn matcher_combine_oneof(values: &[Box]) -> Box { + let mut matchers: Vec>> = vec![]; + for value in values.iter() { + if let Some(matcher_value) = value.as_any().downcast_ref::() { + matchers.push(matcher_value.matcher.to_owned()); + } + } + + let combine_matcher = Box::new(CombineMatcher::create_one_of(matchers)); + Box::new(FunctionMatcherValue::new(combine_matcher)) +} + +fn matcher_combine_allof(values: &[Box]) -> Box { + let mut matchers: Vec>> = vec![]; + for value in values.iter() { + if let Some(matcher_value) = value.as_any().downcast_ref::() { + matchers.push(matcher_value.matcher.to_owned()); + } + } + + let combine_matcher = Box::new(CombineMatcher::create_all_of(matchers)); + Box::new(FunctionMatcherValue::new(combine_matcher)) +} + +fn matcher_combine_noneof(values: &[Box]) -> Box { + let mut matchers: Vec>> = vec![]; + for value in values.iter() { + if let Some(matcher_value) = value.as_any().downcast_ref::() { + matchers.push(matcher_value.matcher.to_owned()); + } + } + + let combine_matcher = Box::new(CombineMatcher::create_none_of(matchers)); + Box::new(FunctionMatcherValue::new(combine_matcher)) +} diff --git a/src/clang_ql/functions/matchers/mod.rs b/src/clang_ql/functions/matchers/mod.rs index 321db04..2103312 100644 --- a/src/clang_ql/functions/matchers/mod.rs +++ b/src/clang_ql/functions/matchers/mod.rs @@ -3,14 +3,17 @@ use std::collections::HashMap; use gitql_core::signature::Signature; use gitql_core::signature::StandardFunction; +mod combine; mod function; #[inline(always)] pub(crate) fn register_matchers_functions(map: &mut HashMap<&'static str, StandardFunction>) { function::register_function_matchers_functions(map); + combine::register_combine_matchers_functions(map); } #[inline(always)] pub(crate) fn register_matchers_signatures(map: &mut HashMap<&'static str, Signature>) { function::register_function_matchers_signatures(map); + combine::register_combine_matchers_signatures(map); } diff --git a/src/clang_ql/matchers/combine.rs b/src/clang_ql/matchers/combine.rs index 639dada..0650c26 100644 --- a/src/clang_ql/matchers/combine.rs +++ b/src/clang_ql/matchers/combine.rs @@ -1,5 +1,77 @@ use super::Matcher; +#[allow(clippy::enum_variant_names)] +#[derive(PartialEq, Clone)] +enum CombineMatcherKind { + OneOf, + AllOf, + NoneOf, +} + +#[derive(Clone)] +pub struct CombineMatcher { + matchers: Vec>>, + kind: CombineMatcherKind, +} + +impl CombineMatcher { + pub fn create_one_of(matchers: Vec>>) -> Self { + CombineMatcher { + matchers, + kind: CombineMatcherKind::OneOf, + } + } + + pub fn create_all_of(matchers: Vec>>) -> Self { + CombineMatcher { + matchers, + kind: CombineMatcherKind::AllOf, + } + } + + pub fn create_none_of(matchers: Vec>>) -> Self { + CombineMatcher { + matchers, + kind: CombineMatcherKind::AllOf, + } + } +} + +impl Matcher for CombineMatcher { + fn is_match(&self, node: &T) -> bool { + let mut matches_count = 0; + let matcher_kind = &self.kind; + for matcher in self.matchers.iter() { + let is_matches = matcher.is_match(node); + + // If kind is `oneOf` and one if matches, return true + if is_matches && CombineMatcherKind::OneOf.eq(matcher_kind) { + return true; + } + + // If kind is `allOf` and one is not matches, return false + if !is_matches && CombineMatcherKind::AllOf.eq(matcher_kind) { + return false; + } + + // If kind is `noneOf` and one is matches, return false + if is_matches && CombineMatcherKind::NoneOf.eq(matcher_kind) { + return false; + } + + if is_matches { + matches_count += 1; + } + } + + match self.kind { + CombineMatcherKind::OneOf => matches_count > 1, + CombineMatcherKind::AllOf => matches_count == self.matchers.len(), + CombineMatcherKind::NoneOf => matches_count == 0, + } + } +} + #[derive(Clone)] enum CombineUnaryMatcherKind { Not, diff --git a/src/clang_ql/matchers/mod.rs b/src/clang_ql/matchers/mod.rs index 7278d6c..d8ba01a 100644 --- a/src/clang_ql/matchers/mod.rs +++ b/src/clang_ql/matchers/mod.rs @@ -16,6 +16,7 @@ pub use function::IsStaticMethodMatcher; pub use function::IsVirtualMatcher; mod combine; +pub use combine::CombineMatcher; pub use combine::UnaryCombineMatcher; dyn_clone::clone_trait_object!( Matcher);