Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

C# Support #1388

Merged
merged 55 commits into from
Jan 8, 2025
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
7b3ad3a
Add c-sharp (with class query)
noahd1 Jan 2, 2025
06d945a
qlty fmt
noahd1 Jan 2, 2025
ed6f900
Add more (copying java's implementation for now)
noahd1 Jan 2, 2025
ba275b8
qlty fmt
noahd1 Jan 2, 2025
c5486ef
Update field query
noahd1 Jan 3, 2025
942f923
Add c sharp file extension
noahd1 Jan 3, 2025
887c062
More C-Sharp updates
noahd1 Jan 3, 2025
323cd92
Change language name
noahd1 Jan 3, 2025
500bcfe
Renames / integration tests
noahd1 Jan 3, 2025
6d4cb13
File and function complexity test
noahd1 Jan 3, 2025
b23edcc
Function complexity
noahd1 Jan 3, 2025
0858ecf
Add duplication test
noahd1 Jan 4, 2025
c5038dc
Add Lines.cs
noahd1 Jan 4, 2025
b7f0d51
Ignore buildid and analyzed at
noahd1 Jan 6, 2025
6acf02c
Nested control aka too many return statements
noahd1 Jan 6, 2025
ffbc80c
Too many parameters integration test
noahd1 Jan 6, 2025
a9630c3
Returns file for C#
noahd1 Jan 6, 2025
1b4a794
--
noahd1 Jan 6, 2025
ad652ed
Conditional assignment
noahd1 Jan 6, 2025
b95cd59
Count function with ternary
noahd1 Jan 6, 2025
2551060
Count function with ternary
noahd1 Jan 6, 2025
1c29a36
Count non sequential logical operators test
noahd1 Jan 6, 2025
24d71ff
Multiple conditionals
noahd1 Jan 7, 2025
3923057
Merge branch 'main' into nd-c-sharp
noahd1 Jan 7, 2025
7a6b0c8
Add Cyclo basic
noahd1 Jan 7, 2025
88da24e
CylcoIf
noahd1 Jan 7, 2025
dc88e68
cycloifelse
noahd1 Jan 7, 2025
386bf32
IfElseIf
noahd1 Jan 7, 2025
12179db
IfElseIfElse
noahd1 Jan 7, 2025
b857c45
Empty function
noahd1 Jan 7, 2025
536c508
Empty If
noahd1 Jan 7, 2025
9e437c1
IfWithBool
noahd1 Jan 7, 2025
5dd91df
IterativeForOf / Iterative Map
noahd1 Jan 7, 2025
59b15ac
IterativeMethods
noahd1 Jan 7, 2025
1efc874
While loop
noahd1 Jan 7, 2025
4847ab9
Unit tests for call_identifiers
noahd1 Jan 7, 2025
7fd3f1b
All call_identifier unit tests
noahd1 Jan 7, 2025
a7168b3
qlty fmt
noahd1 Jan 7, 2025
a4d7b4e
Field identifier tests
noahd1 Jan 8, 2025
bf2d575
Recursion
noahd1 Jan 8, 2025
d10ecb9
Fields in a class declaration
noahd1 Jan 8, 2025
5d5baff
Multiple test
noahd1 Jan 8, 2025
b527ec8
Other
noahd1 Jan 8, 2025
97c90f2
Read
noahd1 Jan 8, 2025
2e10986
Private Field Declaration
noahd1 Jan 8, 2025
55fbeff
Unique
noahd1 Jan 8, 2025
510b487
3 more field tests
noahd1 Jan 8, 2025
2bfb575
-
noahd1 Jan 8, 2025
4ddff3f
Add Methods tests
noahd1 Jan 8, 2025
d5b2622
Add lcom tests
noahd1 Jan 8, 2025
0cc3342
Add real pending test
noahd1 Jan 8, 2025
c7fb1e7
Count properties as fields
noahd1 Jan 8, 2025
5553630
Merge branch 'main' into nd-c-sharp
noahd1 Jan 8, 2025
c4216d7
More fully fleshed C# node types
noahd1 Jan 8, 2025
05c65b6
qlty fmt
noahd1 Jan 8, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ tracing-appender = "0.2.3"
tracing-subscriber = { version = "0.3.19", features = ["env-filter"] }
tracing-test = "0.2.5"
tree-sitter = "0.22.6"
tree-sitter-c-sharp = "0.21.3"
tree-sitter-go = "0.21.2"
tree-sitter-java = "0.21.0"
tree-sitter-javascript = "0.21.4"
Expand Down
1 change: 1 addition & 0 deletions qlty-analysis/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ tempfile.workspace = true
time.workspace = true
tracing.workspace = true
tree-sitter.workspace = true
tree-sitter-c-sharp.workspace = true
tree-sitter-go.workspace = true
tree-sitter-java.workspace = true
tree-sitter-javascript.workspace = true
Expand Down
6 changes: 4 additions & 2 deletions qlty-analysis/src/lang.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use core::fmt;
use std::sync::Arc;
use tree_sitter::{Node, Parser, Query};

mod c_sharp;
noahd1 marked this conversation as resolved.
Show resolved Hide resolved
mod go;
mod java;
mod javascript;
Expand All @@ -16,8 +17,8 @@ mod typescript;
mod typescript_common;

pub use {
go::*, java::*, javascript::*, kotlin::*, php::*, python::*, ruby::*, rust::*, tsx::*,
typescript::*,
c_sharp::*, go::*, java::*, javascript::*, kotlin::*, php::*, python::*, ruby::*, rust::*,
qltysh[bot] marked this conversation as resolved.
Show resolved Hide resolved
tsx::*, typescript::*,
};

#[allow(clippy::borrowed_box)]
Expand All @@ -30,6 +31,7 @@ use lazy_static::lazy_static;
lazy_static! {
pub static ref ALL_LANGS: Vec<Box<dyn Language + Sync>> = {
vec![
Box::<c_sharp::CSharp>::default(),
qltysh[bot] marked this conversation as resolved.
Show resolved Hide resolved
Box::<php::Php>::default(),
Box::<kotlin::Kotlin>::default(),
Box::<go::Go>::default(),
Expand Down
243 changes: 243 additions & 0 deletions qlty-analysis/src/lang/c_sharp.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
use crate::code::node_source;
use crate::code::File;
qltysh[bot] marked this conversation as resolved.
Show resolved Hide resolved
use crate::lang::Language;
qltysh[bot] marked this conversation as resolved.
Show resolved Hide resolved
use tree_sitter::Node;
qltysh[bot] marked this conversation as resolved.
Show resolved Hide resolved

const CLASS_QUERY: &str = r#"
[
(class_declaration
name: (identifier) @name)
(interface_declaration
name: (identifier) @name)
] @definition.class
"#;

const FUNCTION_DECLARATION_QUERY: &str = r#"
[
(method_declaration
name: (identifier) @name
parameters: (_) @parameters)
(constructor_declaration
name: (identifier) @name
parameters: (_) @parameters)
(local_function_statement
name: (identifier) @name
parameters: (_) @parameters)
] @definition.function
"#;

const FIELD_QUERY: &str = r#"
(field_declaration
(variable_declaration
(variable_declarator name: (identifier) @name)
)
) @field
"#;

pub struct CSharp {
pub class_query: tree_sitter::Query,
pub function_declaration_query: tree_sitter::Query,
pub field_query: tree_sitter::Query,
}

impl CSharp {
qltysh[bot] marked this conversation as resolved.
Show resolved Hide resolved
qltysh[bot] marked this conversation as resolved.
Show resolved Hide resolved
pub const BINARY: &'static str = "binary_expression";
pub const BLOCK: &'static str = "block";
pub const BREAK: &'static str = "break_statement";
pub const CATCH: &'static str = "catch_clause";
pub const CASE: &'static str = "switch_section";
pub const COMMENT: &'static str = "comment";
pub const COMPILATION_UNIT: &'static str = "complication_unit";
pub const CONTINUE: &'static str = "continue_statement";
pub const DO: &'static str = "do_statement";
pub const FIELD_DECLARATION: &'static str = "field_declaration";
pub const FIELD_ACCESS: &'static str = "member_access_expression";
pub const FOR: &'static str = "for_statement";
pub const METHOD_DECLARATION: &'static str = "method_declaration";
pub const METHOD_INVOCATION: &'static str = "invocation_expression";
pub const IDENTIFIER: &'static str = "identifier";
pub const IF: &'static str = "if_statement";
pub const LAMBDA: &'static str = "lambda_expression";
pub const RETURN: &'static str = "return_statement";
pub const SELF: &'static str = "this";
pub const STRING: &'static str = "string_literal";
pub const SWITCH: &'static str = "switch_expression";
pub const TERNARY: &'static str = "conditional_expression";
pub const TRY: &'static str = "try_statement";
pub const WHILE: &'static str = "while_statement";

pub const AND: &'static str = "&&";
pub const OR: &'static str = "||";
}

impl Default for CSharp {
qltysh[bot] marked this conversation as resolved.
Show resolved Hide resolved
fn default() -> Self {
let language = tree_sitter_c_sharp::language();

Self {
class_query: tree_sitter::Query::new(&language, CLASS_QUERY).unwrap(),
field_query: tree_sitter::Query::new(&language, FIELD_QUERY).unwrap(),
function_declaration_query: tree_sitter::Query::new(
&language,
FUNCTION_DECLARATION_QUERY,
)
.unwrap(),
}
}
}

impl Language for CSharp {
qltysh[bot] marked this conversation as resolved.
Show resolved Hide resolved
fn name(&self) -> &str {
"c_sharp"
}

fn self_keyword(&self) -> Option<&str> {
Some(Self::SELF)
}

fn class_query(&self) -> &tree_sitter::Query {
&self.class_query
}

fn function_declaration_query(&self) -> &tree_sitter::Query {
&self.function_declaration_query
}

fn field_query(&self) -> &tree_sitter::Query {
&self.field_query
}

fn if_nodes(&self) -> Vec<&str> {
vec![Self::IF]
}

fn block_nodes(&self) -> Vec<&str> {
vec![Self::BLOCK]
}

fn conditional_assignment_nodes(&self) -> Vec<&str> {
vec![]
}

fn invisible_container_nodes(&self) -> Vec<&str> {
vec![Self::COMPILATION_UNIT]
}

fn switch_nodes(&self) -> Vec<&str> {
vec![Self::SWITCH]
}

fn case_nodes(&self) -> Vec<&str> {
vec![Self::CASE]
}

fn ternary_nodes(&self) -> Vec<&str> {
vec![Self::TERNARY]
}

fn loop_nodes(&self) -> Vec<&str> {
vec![Self::FOR, Self::WHILE, Self::DO]
}

fn except_nodes(&self) -> Vec<&str> {
vec![Self::CATCH]
}

fn try_expression_nodes(&self) -> Vec<&str> {
vec![Self::TRY]
}

fn jump_nodes(&self) -> Vec<&str> {
vec![Self::BREAK, Self::CONTINUE]
}

fn return_nodes(&self) -> Vec<&str> {
vec![Self::RETURN]
}

fn binary_nodes(&self) -> Vec<&str> {
vec![Self::BINARY]
}

fn boolean_operator_nodes(&self) -> Vec<&str> {
vec![Self::AND, Self::OR]
}

fn field_nodes(&self) -> Vec<&str> {
vec![Self::FIELD_DECLARATION]
}

fn call_nodes(&self) -> Vec<&str> {
vec![Self::METHOD_INVOCATION]
}

fn function_nodes(&self) -> Vec<&str> {
vec![Self::METHOD_DECLARATION]
}

fn closure_nodes(&self) -> Vec<&str> {
vec![Self::LAMBDA]
}

fn comment_nodes(&self) -> Vec<&str> {
vec![Self::COMMENT]
}

fn string_nodes(&self) -> Vec<&str> {
vec![Self::STRING]
}

fn is_jump_label(&self, node: &Node) -> bool {
node.kind() == Self::IDENTIFIER
}

fn has_labeled_jumps(&self) -> bool {
true
}

fn call_identifiers(&self, source_file: &File, node: &Node) -> (Option<String>, String) {
match node.kind() {
Self::METHOD_INVOCATION => {
let (receiver, object) = self.field_identifiers(source_file, node);

(Some(receiver), object)
}
_ => (Some("<UNKNOWN>".to_string()), "<UNKNOWN>".to_string()),
}
}

fn field_identifiers(&self, source_file: &File, node: &Node) -> (String, String) {
qltysh[bot] marked this conversation as resolved.
Show resolved Hide resolved
let object_node = node.child_by_field_name("object");
let property_node = node
.child_by_field_name("name")
.or_else(|| node.child_by_field_name("field"));

match (&object_node, &property_node) {
(Some(obj), Some(prop)) if obj.kind() == Self::FIELD_ACCESS => {
let object_source =
get_node_source_or_default(obj.child_by_field_name("field"), source_file);
let property_source = get_node_source_or_default(Some(*prop), source_file);
(object_source, property_source)
}
(Some(obj), Some(prop)) => (
get_node_source_or_default(Some(*obj), source_file),
get_node_source_or_default(Some(*prop), source_file),
),
(None, Some(prop)) => (
Self::SELF.to_owned(),
get_node_source_or_default(Some(*prop), source_file),
),
_ => ("<UNKNOWN>".to_string(), "<UNKNOWN>".to_string()),
}
}

fn tree_sitter_language(&self) -> tree_sitter::Language {
tree_sitter_c_sharp::language()
}
}

fn get_node_source_or_default(node: Option<Node>, source_file: &File) -> String {
node.as_ref()
.map(|n| node_source(n, source_file))
.unwrap_or("<UNKNOWN>".to_string())
}
3 changes: 3 additions & 0 deletions qlty-config/default.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ exclude_patterns = [
[file_types.ALL]
globs = ["*.*"]

[file_types.c_sharp]
globs = ["*.cs"]

[file_types.css]
globs = ["*.css"]

Expand Down
Loading