// Copyright (C) 2021-2024 Free Software Foundation, Inc. // This file is part of GCC. // GCC is free software; you can redistribute it and/or modify it under // the terms of the GNU General Public License as published by the Free // Software Foundation; either version 3, or (at your option) any later // version. // GCC is distributed in the hope that it will be useful, but WITHOUT ANY // WARRANTY; without even the implied warranty of MERCHANTABILITY or // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License // for more details. // You should have received a copy of the GNU General Public License // along with GCC; see the file COPYING3. If not see // . #include "rust-readonly-check.h" #include "rust-tree.h" #include "rust-gcc.h" namespace Rust { namespace Analysis { // ported over from c-family/c-warn.cc void readonly_error (location_t loc, tree arg, enum lvalue_use use) { gcc_assert (use == lv_assign || use == lv_increment || use == lv_decrement || use == lv_asm); STRIP_ANY_LOCATION_WRAPPER (arg); /* Using this macro rather than (for example) arrays of messages ensures that all the format strings are checked at compile time. */ #define READONLY_MSG(A, I, D, AS) \ (use == lv_assign \ ? (A) \ : (use == lv_increment ? (I) : (use == lv_decrement ? (D) : (AS)))) if (TREE_CODE (arg) == COMPONENT_REF) { if (TYPE_READONLY (TREE_TYPE (TREE_OPERAND (arg, 0)))) error_at (loc, READONLY_MSG (G_ ("assignment of member " "%qD in read-only object"), G_ ("increment of member " "%qD in read-only object"), G_ ("decrement of member " "%qD in read-only object"), G_ ("member %qD in read-only object " "used as % output")), TREE_OPERAND (arg, 1)); else error_at ( loc, READONLY_MSG (G_ ("assignment of read-only member %qD"), G_ ("increment of read-only member %qD"), G_ ("decrement of read-only member %qD"), G_ ("read-only member %qD used as % output")), TREE_OPERAND (arg, 1)); } else if (VAR_P (arg)) error_at (loc, READONLY_MSG (G_ ("assignment of read-only variable %qD"), G_ ("increment of read-only variable %qD"), G_ ("decrement of read-only variable %qD"), G_ ( "read-only variable %qD used as % output")), arg); else if (TREE_CODE (arg) == PARM_DECL) error_at (loc, READONLY_MSG (G_ ("assignment of read-only parameter %qD"), G_ ("increment of read-only parameter %qD"), G_ ("decrement of read-only parameter %qD"), G_ ( "read-only parameter %qD use as % output")), arg); else if (TREE_CODE (arg) == RESULT_DECL) { error_at (loc, READONLY_MSG (G_ ("assignment of " "read-only named return value %qD"), G_ ("increment of " "read-only named return value %qD"), G_ ("decrement of " "read-only named return value %qD"), G_ ("read-only named return value %qD " "used as %output")), arg); } else if (TREE_CODE (arg) == FUNCTION_DECL) error_at (loc, READONLY_MSG (G_ ("assignment of function %qD"), G_ ("increment of function %qD"), G_ ("decrement of function %qD"), G_ ("function %qD used as % output")), arg); else error_at (loc, READONLY_MSG (G_ ("assignment of read-only location %qE"), G_ ("increment of read-only location %qE"), G_ ("decrement of read-only location %qE"), G_ ( "read-only location %qE used as % output")), arg); } static void check_decl (tree *t) { if (TREE_CODE (*t) == MODIFY_EXPR) { tree lhs = TREE_OPERAND (*t, 0); if (TREE_READONLY (lhs) || TREE_CONSTANT (lhs)) { readonly_error (EXPR_LOCATION (*t), lhs, lv_assign); TREE_OPERAND (*t, 0) = error_mark_node; } } } static tree readonly_walk_fn (tree *t, int *, void *) { switch (TREE_CODE (*t)) { case MODIFY_EXPR: check_decl (t); break; default: break; } return NULL_TREE; } void ReadonlyCheck::Lint (Compile::Context &ctx) { for (auto &fndecl : ctx.get_func_decls ()) { for (tree p = DECL_ARGUMENTS (fndecl); p != NULL_TREE; p = DECL_CHAIN (p)) { check_decl (&p); } walk_tree_without_duplicates (&DECL_SAVED_TREE (fndecl), &readonly_walk_fn, &ctx); } for (auto &var : ctx.get_var_decls ()) { tree decl = var->get_decl (); check_decl (&decl); } for (auto &const_decl : ctx.get_const_decls ()) { check_decl (&const_decl); } } } // namespace Analysis } // namespace Rust