Skip to content

Instantly share code, notes, and snippets.

@dareg
Created June 9, 2025 09:21
Show Gist options
  • Select an option

  • Save dareg/5c691d6ceda2ffeb70d12b0273aae89a to your computer and use it in GitHub Desktop.

Select an option

Save dareg/5c691d6ceda2ffeb70d12b0273aae89a to your computer and use it in GitHub Desktop.
GCC plugin to detect suspicious initialization of variables in struct initializations
#include <gcc-plugin.h>
#include <plugin-version.h>
#include <tree.h>
#include <c-family/c-pragma.h>
#include <c-family/c-common.h>
#include <diagnostic.h>
#include <langhooks.h>
/* Informations du plugin */
int plugin_is_GPL_compatible;
static struct plugin_info designated_init_plugin_info = {
.version = "1.0",
.help = "Détecte les designated initializers mal formés"
};
/* Fonction pour obtenir le nom d'un champ de structure */
static const char* get_field_name_from_type(tree struct_type, unsigned int index) __attribute__((unused));
static const char* get_field_name_from_type(tree struct_type, unsigned int index)
{
if (!struct_type || TREE_CODE(struct_type) != RECORD_TYPE)
return NULL;
tree field = TYPE_FIELDS(struct_type);
unsigned int i = 0;
while (field && i < index) {
field = DECL_CHAIN(field);
i++;
}
if (field && DECL_NAME(field)) {
return IDENTIFIER_POINTER(DECL_NAME(field));
}
return NULL;
}
/* Fonction pour vérifier si un nom correspond à un champ de structure */
static bool is_field_name(tree struct_type, const char* name)
{
if (!struct_type || TREE_CODE(struct_type) != RECORD_TYPE || !name)
return false;
tree field = TYPE_FIELDS(struct_type);
while (field) {
if (DECL_NAME(field)) {
const char* field_name = IDENTIFIER_POINTER(DECL_NAME(field));
if (strcmp(field_name, name) == 0) {
return true;
}
}
field = DECL_CHAIN(field);
}
return false;
}
/* Analyse améliorée des CONSTRUCTOR */
static void deep_analyze_constructor(tree init, location_t loc)
{
if (!init || TREE_CODE(init) != CONSTRUCTOR)
return;
vec<constructor_elt, va_gc> *elts = CONSTRUCTOR_ELTS(init);
if (!elts)
return;
constructor_elt *elt;
unsigned int i;
int designated_count = 0;
int total_count = vec_safe_length(elts);
tree struct_type = TREE_TYPE(init);
/* Compter les designated initializers et analyser chaque élément */
FOR_EACH_VEC_SAFE_ELT(elts, i, elt) {
if (elt->index && TREE_CODE(elt->index) == FIELD_DECL) {
designated_count++;
} else {
/* Élément non-désigné - vérifier si c'est une assignation suspecte */
if (elt->value && TREE_CODE(elt->value) == MODIFY_EXPR) {
tree lhs = TREE_OPERAND(elt->value, 0);
if (lhs && TREE_CODE(lhs) == VAR_DECL && DECL_NAME(lhs)) {
const char *var_name = IDENTIFIER_POINTER(DECL_NAME(lhs));
/* Vérifier si c'est un nom de champ de la structure */
if (is_field_name(struct_type, var_name)) {
warning_at(loc, 0,
"suspicious assignment '%s=...' in initializer - "
"did you mean '.%s=...'?",
var_name, var_name);
}
}
}
}
}
/* Détection du mélange de styles */
if (designated_count > 0 && designated_count < total_count) {
warning_at(loc, 0,
"mixing designated and non-designated initializers");
}
}
/* Analyse spéciale pour les expressions dans les initialiseurs */
static void analyze_initializer_expressions(tree init, tree struct_type, location_t loc)
{
if (!init)
return;
/* Chercher des expressions d'assignation dans l'initializer */
if (TREE_CODE(init) == MODIFY_EXPR) {
tree lhs = TREE_OPERAND(init, 0);
if (lhs && TREE_CODE(lhs) == VAR_DECL && DECL_NAME(lhs)) {
const char *var_name = IDENTIFIER_POINTER(DECL_NAME(lhs));
/* Vérifier si c'est un nom de champ */
if (is_field_name(struct_type, var_name)) {
warning_at(loc, 0,
"assignment '%s=...' in initializer context - "
"did you mean '.%s=...'?",
var_name, var_name);
}
}
}
}
/* Walker amélioré pour détecter plus de cas */
static tree analyze_tree_walker(tree *tp, int *walk_subtrees, void *data)
{
tree t = *tp;
if (!t)
return NULL_TREE;
location_t loc = EXPR_LOCATION(t);
if (loc == UNKNOWN_LOCATION)
loc = input_location;
/* Analyser les CONSTRUCTOR */
if (TREE_CODE(t) == CONSTRUCTOR) {
deep_analyze_constructor(t, loc);
}
/* Analyser les expressions d'assignation */
else if (TREE_CODE(t) == MODIFY_EXPR) {
/* Essayer de déterminer si on est dans un contexte d'initialisation */
tree parent = (tree)data;
if (parent && TREE_CODE(parent) == CONSTRUCTOR) {
tree struct_type = TREE_TYPE(parent);
analyze_initializer_expressions(t, struct_type, loc);
}
}
/* Passer le contexte aux sous-arbres */
if (TREE_CODE(t) == CONSTRUCTOR) {
tree subtree;
unsigned int i;
FOR_EACH_CONSTRUCTOR_VALUE(CONSTRUCTOR_ELTS(t), i, subtree) {
walk_tree(&subtree, analyze_tree_walker, t, NULL);
}
*walk_subtrees = 0; /* On a déjà traité les sous-arbres */
}
return NULL_TREE;
}
/* Callback pour analyser chaque fonction */
static void pre_genericize_callback(void *gcc_data, void *user_data)
{
tree fndecl = (tree)gcc_data;
if (!fndecl || TREE_CODE(fndecl) != FUNCTION_DECL)
return;
tree body = DECL_SAVED_TREE(fndecl);
if (body) {
walk_tree(&body, analyze_tree_walker, NULL, NULL);
}
}
/* Callback pour analyser les déclarations */
static void finish_decl_callback(void *gcc_data, void *user_data)
{
tree decl = (tree)gcc_data;
if (!decl)
return;
if (TREE_CODE(decl) == VAR_DECL && DECL_INITIAL(decl)) {
tree init = DECL_INITIAL(decl);
if (TREE_CODE(init) == CONSTRUCTOR) {
location_t loc = DECL_SOURCE_LOCATION(decl);
deep_analyze_constructor(init, loc);
}
}
}
/* Fonction d'initialisation du plugin */
int plugin_init(struct plugin_name_args *plugin_info,
struct plugin_gcc_version *version)
{
/* Vérification de la version de GCC */
if (!plugin_default_version_check(version, &gcc_version)) {
error("Plugin incompatible avec cette version de GCC");
return 1;
}
/* Enregistrement des informations du plugin */
register_callback(plugin_info->base_name,
PLUGIN_INFO,
NULL,
&designated_init_plugin_info);
/* Enregistrement des callbacks */
register_callback(plugin_info->base_name,
PLUGIN_FINISH_DECL,
finish_decl_callback,
NULL);
register_callback(plugin_info->base_name,
PLUGIN_PRE_GENERICIZE,
pre_genericize_callback,
NULL);
return 0;
}
# Cible principale
$(PLUGIN_NAME).so: $(PLUGIN_NAME).cpp
g++ $(CXXFLAGS) $(LDFLAGS) -o $@ $# Makefile pour le plugin designated_init_checker
# Configuration
PLUGIN_NAME = designated_init_checker
GCC_VERSION = $(shell gcc -dumpversion)
GCC_PLUGIN_DIR = $(shell gcc -print-file-name=plugin)
# Flags de compilation
CXXFLAGS = -fPIC -shared -O2 -Wall -fno-rtti
CXXFLAGS += -I$(GCC_PLUGIN_DIR)/include
CXXFLAGS += -std=c++11
# Flags de liaison
LDFLAGS = -shared -fPIC
# Cible principale
$(PLUGIN_NAME).so: $(PLUGIN_NAME).cpp
g++ $(CXXFLAGS) -o $@ $<
# Test du plugin
test: $(PLUGIN_NAME).so test.c
gcc -fplugin=./$(PLUGIN_NAME).so -c test.c
# Nettoyage
clean:
rm -f $(PLUGIN_NAME).so test.o
# Installation (optionnelle)
install: $(PLUGIN_NAME).so
sudo cp $(PLUGIN_NAME).so $(GCC_PLUGIN_DIR)/
.PHONY: test clean install
typedef struct {
int a, b, c;
} t;
int c = 0;
t f1() {
return (t){.a=1, .c=c}; // OK
}
t f2() {
return (t){.a=1, c=c}; // Devrait générer un warning
}
t f3() {
return (t){.a=1, c=1}; // Devrait générer un warning
}
t f4() {
return (t){1, .c=1}; // OK
}
t f5() {
int b;
return (t){.a=1, b=1, .c=c}; // Devrait générer un warning
}
t f6() {
int autre;
return (t){autre=7, .c=1}; // Bizarre mais OK
}
t f7() {
int foo,b;
return (t){.a=1, b=foo, .c=c}; // Devrait générer un warning
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment