use std::path::PathBuf;
use serde::{Deserialize, Serialize};
use crate::{
ast_context::AstContext,
config::{Config, RuleLevel},
};
#[derive(Debug, Clone, Copy, PartialEq, Eq, clap::ValueEnum, Serialize)]
pub enum Category {
Correctness,
Suspicious,
Style,
Complexity,
Perf,
Pedantic,
Restriction,
Portability,
Introspection,
}
impl Category {
pub fn as_str(&self) -> &'static str {
match self {
Self::Correctness => "correctness",
Self::Suspicious => "suspicious",
Self::Style => "style",
Self::Complexity => "complexity",
Self::Perf => "perf",
Self::Pedantic => "pedantic",
Self::Restriction => "restriction",
Self::Portability => "portability",
Self::Introspection => "introspection",
}
}
}
impl std::fmt::Display for Category {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Correctness => write!(f, "Correctness"),
Self::Suspicious => write!(f, "Suspicious"),
Self::Style => write!(f, "Style"),
Self::Complexity => write!(f, "Complexity"),
Self::Perf => write!(f, "Performance"),
Self::Pedantic => write!(f, "Pedantic"),
Self::Restriction => write!(f, "Restriction"),
Self::Portability => write!(f, "Portability"),
Self::Introspection => write!(f, "Introspection"),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Fix {
pub start_byte: usize,
pub end_byte: usize,
pub replacement: String,
}
impl Fix {
pub fn new(start_byte: usize, end_byte: usize, replacement: impl Into<String>) -> Self {
Self {
start_byte,
end_byte,
replacement: replacement.into(),
}
}
pub fn delete_line(location: &gobject_ast::SourceLocation, source: &[u8]) -> Self {
let mut line_start = location.start_byte;
while line_start > 0 && source[line_start - 1] != b'\n' {
line_start -= 1;
}
let mut line_end = location.end_byte;
while line_end < source.len() && source[line_end] != b'\n' {
line_end += 1;
}
if line_end < source.len() && source[line_end] == b'\n' {
line_end += 1;
}
Self::new(line_start, line_end, String::new())
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ConfigOption {
pub name: &'static str,
pub option_type: &'static str,
pub default_value: &'static str,
pub example_value: &'static str,
pub description: &'static str,
}
pub mod dead_code;
pub mod deprecated_add_private;
pub mod g_error_init;
pub mod g_error_leak;
pub mod g_object_virtual_methods_chain_up;
pub mod g_param_spec_null_nick_blurb;
pub mod g_param_spec_static_strings;
pub mod g_source_id_not_stored;
pub mod g_task_source_tag;
pub mod gi_missing_since;
pub mod gi_not_bindings_friendly;
pub mod include_order;
pub mod inconsistent_function_signature;
pub mod matching_declare_define;
pub mod missing_autoptr_cleanup;
pub mod missing_export_macro;
pub mod missing_implementation;
pub mod no_g_auto_macros;
pub mod property_canonical_name;
pub mod property_enum_convention;
pub mod property_enum_coverage;
pub mod property_switch_exhaustiveness;
pub mod signal_canonical_name;
pub mod signal_enum_coverage;
pub mod strcmp_explicit_comparison;
pub mod type_style;
pub mod unnecessary_null_check;
pub mod untranslated_string;
pub mod use_auto_cleanup;
pub mod use_clear_functions;
pub mod use_explicit_default_flags;
pub mod use_g_ascii_functions;
pub mod use_g_bytes_unref_to_data;
pub mod use_g_file_load_bytes;
pub mod use_g_gnuc_flag_enum;
pub mod use_g_new;
pub mod use_g_object_class_install_properties;
pub mod use_g_object_new_with_properties;
pub mod use_g_object_notify_by_pspec;
pub mod use_g_set_object;
pub mod use_g_set_str;
pub mod use_g_settings_typed;
pub mod use_g_source_constants;
pub mod use_g_source_once;
pub mod use_g_steal_pointer;
pub mod use_g_str_has_prefix_suffix;
pub mod use_g_strcmp0;
pub mod use_g_string_free_and_steal;
pub mod use_g_strlcpy;
pub mod use_g_value_set_static_string;
pub mod use_g_variant_new_typed;
pub mod use_pragma_once;
pub use dead_code::DeadCode;
pub use deprecated_add_private::DeprecatedAddPrivate;
pub use g_error_init::GErrorInit;
pub use g_error_leak::GErrorLeak;
pub use g_object_virtual_methods_chain_up::GObjectVirtualMethodsChainUp;
pub use g_param_spec_null_nick_blurb::GParamSpecNullNickBlurb;
pub use g_param_spec_static_strings::GParamSpecStaticStrings;
pub use g_source_id_not_stored::GSourceIdNotStored;
pub use g_task_source_tag::GTaskSourceTag;
pub use gi_missing_since::GiMissingSince;
pub use gi_not_bindings_friendly::GiNotBindingsFriendly;
pub use include_order::IncludeOrder;
pub use inconsistent_function_signature::InconsistentFunctionSignature;
pub use matching_declare_define::MatchingDeclareDefine;
pub use missing_autoptr_cleanup::MissingAutoptrCleanup;
pub use missing_export_macro::MissingExportMacro;
pub use missing_implementation::MissingImplementation;
pub use no_g_auto_macros::NoGAutoMacros;
pub use property_canonical_name::PropertyCanonicalName;
pub use property_enum_convention::PropertyEnumConvention;
pub use property_enum_coverage::PropertyEnumCoverage;
pub use property_switch_exhaustiveness::PropertySwitchExhaustiveness;
pub use signal_canonical_name::SignalCanonicalName;
pub use signal_enum_coverage::SignalEnumCoverage;
pub use strcmp_explicit_comparison::StrcmpExplicitComparison;
pub use type_style::TypeStyle;
pub use unnecessary_null_check::UnnecessaryNullCheck;
pub use untranslated_string::UntranslatedString;
pub use use_auto_cleanup::UseAutoCleanup;
pub use use_clear_functions::UseClearFunctions;
pub use use_explicit_default_flags::UseExplicitDefaultFlags;
pub use use_g_ascii_functions::UseGAsciiFunctions;
pub use use_g_bytes_unref_to_data::UseGBytesUnrefToData;
pub use use_g_file_load_bytes::UseGFileLoadBytes;
pub use use_g_gnuc_flag_enum::UseGGnucFlagEnum;
pub use use_g_new::UseGNew;
pub use use_g_object_class_install_properties::UseGObjectClassInstallProperties;
pub use use_g_object_new_with_properties::UseGObjectNewWithProperties;
pub use use_g_object_notify_by_pspec::UseGObjectNotifyByPspec;
pub use use_g_set_object::UseGSetObject;
pub use use_g_set_str::UseGSetStr;
pub use use_g_settings_typed::UseGSettingsTyped;
pub use use_g_source_constants::UseGSourceConstants;
pub use use_g_source_once::UseGSourceOnce;
pub use use_g_steal_pointer::UseGStealPointer;
pub use use_g_str_has_prefix_suffix::UseGStrHasPrefixSuffix;
pub use use_g_strcmp0::UseGStrcmp0;
pub use use_g_string_free_and_steal::UseGStringFreeAndSteal;
pub use use_g_strlcpy::UseGStrlcpy;
pub use use_g_value_set_static_string::UseGValueSetStaticString;
pub use use_g_variant_new_typed::UseGVariantNewTyped;
pub use use_pragma_once::UsePragmaOnce;
#[derive(Debug, Clone, serde::Serialize)]
pub struct Violation {
pub file: PathBuf,
pub line: usize,
pub column: usize,
pub message: String,
pub rule: &'static str,
pub category: Category,
pub level: RuleLevel,
pub snippet: Option<String>,
pub rule_index: usize,
pub fixes: Vec<Fix>,
}
pub trait Rule: Send + Sync {
fn name(&self) -> &'static str;
fn description(&self) -> &'static str;
fn long_description(&self) -> Option<&'static str> {
None
}
fn category(&self) -> Category;
fn fixable(&self) -> bool {
false
}
fn requires_meson(&self) -> bool {
false
}
fn config_options(&self) -> &'static [ConfigOption] {
&[]
}
#[allow(unused_variables)]
fn check_func_impl(
&self,
ast_context: &AstContext,
config: &Config,
func: &gobject_ast::top_level::FunctionDefItem,
file: &gobject_ast::FileModel,
violations: &mut Vec<Violation>,
) {
}
#[allow(unused_variables)]
fn check_func_decl(
&self,
ast_context: &AstContext,
config: &Config,
func: &gobject_ast::top_level::FunctionDeclItem,
file: &gobject_ast::FileModel,
violations: &mut Vec<Violation>,
) {
}
#[allow(unused_variables)]
fn check_enum(
&self,
ast_context: &AstContext,
config: &Config,
enum_info: &gobject_ast::EnumInfo,
file: &gobject_ast::FileModel,
violations: &mut Vec<Violation>,
) {
}
#[allow(unused_variables)]
fn check_gobject_type(
&self,
ast_context: &AstContext,
config: &Config,
gobject_type: &gobject_ast::GObjectType,
file: &gobject_ast::FileModel,
violations: &mut Vec<Violation>,
) {
}
fn check_all(
&self,
ast_context: &AstContext,
config: &Config,
violations: &mut Vec<Violation>,
) {
for (path, file) in ast_context.iter_all_files() {
let ext = path.extension().and_then(|e| e.to_str());
if ext == Some("c") {
for func in file.iter_function_definitions() {
self.check_func_impl(ast_context, config, func, file, violations);
}
}
if ext == Some("h") {
for func in file.iter_function_declarations() {
self.check_func_decl(ast_context, config, func, file, violations);
}
}
for gt in file.iter_all_gobject_types() {
self.check_gobject_type(ast_context, config, gt, file, violations);
}
for enum_info in file.iter_all_enums() {
self.check_enum(ast_context, config, enum_info, file, violations);
}
}
}
fn violation(
&self,
file: &std::path::Path,
line: usize,
column: usize,
message: String,
) -> Violation {
Violation {
file: file.to_path_buf(),
line,
column,
message,
rule: self.name(),
category: self.category(),
level: RuleLevel::Error, snippet: None,
rule_index: 0, fixes: Vec::new(),
}
}
fn violation_with_fix(
&self,
file: &std::path::Path,
line: usize,
column: usize,
message: String,
fix: Fix,
) -> Violation {
Violation {
file: file.to_path_buf(),
line,
column,
message,
rule: self.name(),
category: self.category(),
level: RuleLevel::Error, snippet: None,
rule_index: 0,
fixes: vec![fix],
}
}
fn violation_with_fixes(
&self,
file: &std::path::Path,
line: usize,
column: usize,
message: String,
fixes: Vec<Fix>,
) -> Violation {
Violation {
file: file.to_path_buf(),
line,
column,
message,
rule: self.name(),
category: self.category(),
level: RuleLevel::Error, snippet: None,
rule_index: 0,
fixes,
}
}
}