diff options
author | Ian Lance Taylor <ian@gcc.gnu.org> | 2012-01-23 23:55:31 +0000 |
---|---|---|
committer | Ian Lance Taylor <ian@gcc.gnu.org> | 2012-01-23 23:55:31 +0000 |
commit | b1b3aec1b1e26c22791f21d971a851af4df2dad2 (patch) | |
tree | ed963d480affb07d364c84b0b56a97f7bb92427b /gcc/go | |
parent | 8bae34da8a4625767bc7e1caab295855963ff280 (diff) | |
download | gcc-b1b3aec1b1e26c22791f21d971a851af4df2dad2.zip gcc-b1b3aec1b1e26c22791f21d971a851af4df2dad2.tar.gz gcc-b1b3aec1b1e26c22791f21d971a851af4df2dad2.tar.bz2 |
compiler: Give an error if a variable is defined but not used.
From-SVN: r183458
Diffstat (limited to 'gcc/go')
-rw-r--r-- | gcc/go/gofrontend/expressions.cc | 1 | ||||
-rw-r--r-- | gcc/go/gofrontend/gogo.cc | 42 | ||||
-rw-r--r-- | gcc/go/gofrontend/gogo.h | 22 | ||||
-rw-r--r-- | gcc/go/gofrontend/lex.cc | 14 | ||||
-rw-r--r-- | gcc/go/gofrontend/lex.h | 7 | ||||
-rw-r--r-- | gcc/go/gofrontend/parse.cc | 52 | ||||
-rw-r--r-- | gcc/go/gofrontend/parse.h | 11 |
7 files changed, 146 insertions, 3 deletions
diff --git a/gcc/go/gofrontend/expressions.cc b/gcc/go/gofrontend/expressions.cc index fe361fb..ebecbbd 100644 --- a/gcc/go/gofrontend/expressions.cc +++ b/gcc/go/gofrontend/expressions.cc @@ -1472,6 +1472,7 @@ Unknown_expression::do_lower(Gogo*, Named_object*, Statement_inserter*, int) real->message_name().c_str()); return Expression::make_error(location); case Named_object::NAMED_OBJECT_VAR: + real->var_value()->set_is_used(); return Expression::make_var_reference(real, location); case Named_object::NAMED_OBJECT_FUNC: case Named_object::NAMED_OBJECT_FUNC_DECLARATION: diff --git a/gcc/go/gofrontend/gogo.cc b/gcc/go/gofrontend/gogo.cc index e995be4..61d1bd8 100644 --- a/gcc/go/gofrontend/gogo.cc +++ b/gcc/go/gofrontend/gogo.cc @@ -1036,6 +1036,23 @@ Gogo::add_named_object(Named_object* no) this->current_bindings()->add_named_object(no); } +// Mark all local variables used. This is used when some types of +// parse error occur. + +void +Gogo::mark_locals_used() +{ + for (Open_functions::iterator pf = this->functions_.begin(); + pf != this->functions_.end(); + ++pf) + { + for (std::vector<Block*>::iterator pb = pf->blocks.begin(); + pb != pf->blocks.end(); + ++pb) + (*pb)->bindings()->mark_locals_used(); + } +} + // Record that we've seen an interface type. void @@ -1731,6 +1748,15 @@ Check_types_traverse::variable(Named_object* named_object) reason.c_str()); var->clear_init(); } + else if (!var->is_used() + && !var->is_global() + && !var->is_parameter() + && !var->is_receiver() + && !var->type()->is_error() + && (init == NULL || !init->is_error_expression()) + && !Lex::is_invalid_identifier(named_object->name())) + error_at(var->location(), "%qs declared and not used", + named_object->message_name().c_str()); } return TRAVERSE_CONTINUE; } @@ -2973,6 +2999,7 @@ Function::closure_var() Type* struct_type = Type::make_struct_type(sfl, loc); Variable* var = new Variable(Type::make_pointer_type(struct_type), NULL, false, true, false, loc); + var->set_is_used(); this->closure_var_ = Named_object::make_variable("closure", NULL, var); // Note that the new variable is not in any binding contour. } @@ -3693,7 +3720,7 @@ Variable::Variable(Type* type, Expression* init, bool is_global, Location location) : type_(type), init_(init), preinit_(NULL), location_(location), backend_(NULL), is_global_(is_global), is_parameter_(is_parameter), - is_receiver_(is_receiver), is_varargs_parameter_(false), + is_receiver_(is_receiver), is_varargs_parameter_(false), is_used_(false), is_address_taken_(false), is_non_escaping_address_taken_(false), seen_(false), init_is_lowered_(false), type_from_init_tuple_(false), type_from_range_index_(false), type_from_range_value_(false), @@ -4877,6 +4904,19 @@ Bindings::define_type(Named_object* no, Named_type* type) this->named_objects_.push_back(no); } +// Mark all local variables as used. This is used for some types of +// parse error. + +void +Bindings::mark_locals_used() +{ + for (std::vector<Named_object*>::iterator p = this->named_objects_.begin(); + p != this->named_objects_.end(); + ++p) + if ((*p)->is_variable()) + (*p)->var_value()->set_is_used(); +} + // Traverse bindings. int diff --git a/gcc/go/gofrontend/gogo.h b/gcc/go/gofrontend/gogo.h index 1042f03..dfaa596 100644 --- a/gcc/go/gofrontend/gogo.h +++ b/gcc/go/gofrontend/gogo.h @@ -344,6 +344,11 @@ class Gogo void add_named_object(Named_object*); + // Mark all local variables in current bindings as used. This is + // used when there is a parse error to avoid useless errors. + void + mark_locals_used(); + // Return a name to use for a thunk function. A thunk function is // one we create during the compilation, for a go statement or a // defer statement or a method expression. @@ -1232,6 +1237,16 @@ class Variable this->is_varargs_parameter_ = true; } + // Return whether the variable has been used. + bool + is_used() const + { return this->is_used_; } + + // Mark that the variable has been used. + void + set_is_used() + { this->is_used_ = true; } + // Clear the initial value; used for error handling. void clear_init() @@ -1368,6 +1383,8 @@ class Variable bool is_receiver_ : 1; // Whether this is the varargs parameter of a function. bool is_varargs_parameter_ : 1; + // Whether this variable is ever referenced. + bool is_used_ : 1; // Whether something takes the address of this variable. For a // local variable this implies that the variable has to be on the // heap. @@ -2124,6 +2141,11 @@ class Bindings void remove_binding(Named_object*); + // Mark all variables as used. This is used for some types of parse + // error. + void + mark_locals_used(); + // Traverse the tree. See the Traverse class. int traverse(Traverse*, bool is_global); diff --git a/gcc/go/gofrontend/lex.cc b/gcc/go/gofrontend/lex.cc index af23e9b..d46334f 100644 --- a/gcc/go/gofrontend/lex.cc +++ b/gcc/go/gofrontend/lex.cc @@ -866,6 +866,7 @@ Lex::gather_identifier() this->lineoff_ = p - this->linebuf_; const char* pnext = this->advance_one_utf8_char(p, &ci, &issued_error); + bool is_invalid = false; if (!Lex::is_unicode_letter(ci) && !Lex::is_unicode_digit(ci)) { // There is no valid place for a non-ASCII character @@ -876,6 +877,7 @@ Lex::gather_identifier() error_at(this->location(), "invalid character 0x%x in identifier", ci); + is_invalid = true; } if (is_first) { @@ -887,6 +889,8 @@ Lex::gather_identifier() buf.assign(pstart, p - pstart); has_non_ascii_char = true; } + if (is_invalid && !Lex::is_invalid_identifier(buf)) + buf.append("$INVALID$"); p = pnext; char ubuf[50]; // This assumes that all assemblers can handle an identifier @@ -2312,3 +2316,13 @@ Lex::is_exported_name(const std::string& name) return Lex::is_unicode_uppercase(ci); } } + +// Return whether the identifier NAME contains an invalid character. +// This is based on how we handle invalid characters in +// gather_identifier. + +bool +Lex::is_invalid_identifier(const std::string& name) +{ + return name.find("$INVALID$") != std::string::npos; +} diff --git a/gcc/go/gofrontend/lex.h b/gcc/go/gofrontend/lex.h index b9b4b1f..b184b1e 100644 --- a/gcc/go/gofrontend/lex.h +++ b/gcc/go/gofrontend/lex.h @@ -349,6 +349,13 @@ class Lex static bool is_exported_name(const std::string& name); + // Return whether the identifier NAME is invalid. When we see an + // invalid character we still build an identifier, but we use a + // magic string to indicate that the identifier is invalid. We then + // use this to avoid knockon errors. + static bool + is_invalid_identifier(const std::string& name); + // A helper function. Append V to STR. IS_CHARACTER is true if V // is a Unicode character which should be converted into UTF-8, // false if it is a byte value to be appended directly. The diff --git a/gcc/go/gofrontend/parse.cc b/gcc/go/gofrontend/parse.cc index 21cd764..35af758 100644 --- a/gcc/go/gofrontend/parse.cc +++ b/gcc/go/gofrontend/parse.cc @@ -49,7 +49,8 @@ Parse::Parse(Lex* lex, Gogo* gogo) break_stack_(NULL), continue_stack_(NULL), iota_(0), - enclosing_vars_() + enclosing_vars_(), + type_switch_vars_() { } @@ -539,6 +540,7 @@ Parse::field_decl(Struct_field_list* sfl) else { error_at(this->location(), "expected field name"); + this->gogo_->mark_locals_used(); while (!token->is_op(OPERATOR_SEMICOLON) && !token->is_op(OPERATOR_RCURLY) && !token->is_eof()) @@ -554,6 +556,7 @@ Parse::field_decl(Struct_field_list* sfl) if (!this->peek_token()->is_identifier()) { error_at(this->location(), "expected field name"); + this->gogo_->mark_locals_used(); while (!token->is_op(OPERATOR_SEMICOLON) && !token->is_op(OPERATOR_RCURLY) && !token->is_eof()) @@ -1123,6 +1126,8 @@ Parse::block() if (!token->is_eof() || !saw_errors()) error_at(this->location(), "expected %<}%>"); + this->gogo_->mark_locals_used(); + // Skip ahead to the end of the block, in hopes of avoiding // lots of meaningless errors. Location ret = token->location(); @@ -1249,6 +1254,7 @@ Parse::method_spec(Typed_identifier_list* methods) "name list not allowed in interface type"); else error_at(location, "expected signature or type name"); + this->gogo_->mark_locals_used(); token = this->peek_token(); while (!token->is_eof() && !token->is_op(OPERATOR_SEMICOLON) @@ -1498,6 +1504,7 @@ Parse::type_spec(void*) if (type->is_error_type()) { + this->gogo_->mark_locals_used(); while (!this->peek_token()->is_op(OPERATOR_SEMICOLON) && !this->peek_token()->is_eof()) this->advance_token(); @@ -1558,6 +1565,7 @@ Parse::var_spec(void*) type = this->type(); if (type->is_error_type()) { + this->gogo_->mark_locals_used(); while (!this->peek_token()->is_op(OPERATOR_EQ) && !this->peek_token()->is_op(OPERATOR_SEMICOLON) && !this->peek_token()->is_eof()) @@ -1894,6 +1902,7 @@ Parse::init_var(const Typed_identifier& tid, Type* type, Expression* init, // initializer can be assigned to the type. Variable* var = new Variable(type, init, false, false, false, location); + var->set_is_used(); static int count; char buf[30]; snprintf(buf, sizeof buf, "sink$%d", count); @@ -2188,6 +2197,7 @@ Parse::receiver() if (!token->is_identifier()) { error_at(this->location(), "method has no receiver"); + this->gogo_->mark_locals_used(); while (!token->is_eof() && !token->is_op(OPERATOR_RPAREN)) token = this->advance_token(); if (!token->is_eof()) @@ -2227,6 +2237,7 @@ Parse::receiver() if (!token->is_identifier()) { error_at(this->location(), "expected receiver name or type"); + this->gogo_->mark_locals_used(); int c = token->is_op(OPERATOR_LPAREN) ? 1 : 0; while (!token->is_eof()) { @@ -2258,6 +2269,7 @@ Parse::receiver() error_at(this->location(), "method has multiple receivers"); else error_at(this->location(), "expected %<)%>"); + this->gogo_->mark_locals_used(); while (!token->is_eof() && !token->is_op(OPERATOR_RPAREN)) token = this->advance_token(); if (!token->is_eof()) @@ -2365,6 +2377,7 @@ Parse::operand(bool may_be_sink) } case Named_object::NAMED_OBJECT_VAR: case Named_object::NAMED_OBJECT_RESULT_VAR: + this->mark_var_used(named_object); return Expression::make_var_reference(named_object, location); case Named_object::NAMED_OBJECT_SINK: if (may_be_sink) @@ -2477,6 +2490,8 @@ Parse::enclosing_var_reference(Named_object* in_function, Named_object* var, { go_assert(var->is_variable() || var->is_result_variable()); + this->mark_var_used(var); + Named_object* this_function = this->gogo_->current_function(); Named_object* closure = this_function->func_value()->closure_var(); @@ -2648,6 +2663,7 @@ Parse::composite_lit(Type* type, int depth, Location location) { error_at(this->location(), "expected %<,%> or %<}%>"); + this->gogo_->mark_locals_used(); int depth = 0; while (!token->is_eof() && (depth > 0 || !token->is_op(OPERATOR_RCURLY))) @@ -3019,6 +3035,7 @@ Parse::id_to_expression(const std::string& name, Location location) return Expression::make_const_reference(named_object, location); case Named_object::NAMED_OBJECT_VAR: case Named_object::NAMED_OBJECT_RESULT_VAR: + this->mark_var_used(named_object); return Expression::make_var_reference(named_object, location); case Named_object::NAMED_OBJECT_SINK: return Expression::make_sink(location); @@ -3534,6 +3551,7 @@ Parse::simple_stat(bool may_be_composite_lit, bool* return_exp, { if (!exp->is_error_expression()) error_at(token->location(), "non-name on left side of %<:=%>"); + this->gogo_->mark_locals_used(); while (!token->is_op(OPERATOR_SEMICOLON) && !token->is_eof()) token = this->advance_token(); @@ -4287,7 +4305,15 @@ Parse::type_case_clause(Named_object* switch_no, Type_case_clauses* clauses, Variable* v = new Variable(type, init, false, false, false, location); v->set_is_type_switch_var(); - this->gogo_->add_variable(switch_no->name(), v); + Named_object* no = this->gogo_->add_variable(switch_no->name(), v); + + // We don't want to issue an error if the compiler + // introduced special variable is not used. Instead we want + // to issue an error if the variable defined by the switch + // is not used. That is handled via type_switch_vars_ and + // Parse::mark_var_used. + v->set_is_used(); + this->type_switch_vars_[no] = switch_no; } this->statement_list(); statements = this->gogo_->finish_block(this->location()); @@ -4343,6 +4369,7 @@ Parse::type_switch_case(std::vector<Type*>* types, bool* is_default) types->push_back(t); else { + this->gogo_->mark_locals_used(); token = this->peek_token(); while (!token->is_op(OPERATOR_COLON) && !token->is_op(OPERATOR_COMMA) @@ -5209,6 +5236,7 @@ Parse::program() else { error_at(this->location(), "expected declaration"); + this->gogo_->mark_locals_used(); do this->advance_token(); while (!this->peek_token()->is_eof() @@ -5267,6 +5295,7 @@ Parse::increment_iota() bool Parse::skip_past_error(Operator op) { + this->gogo_->mark_locals_used(); const Token* token = this->peek_token(); while (!token->is_op(op)) { @@ -5294,3 +5323,22 @@ Parse::verify_not_sink(Expression* expr) } return expr; } + +// Mark a variable as used. + +void +Parse::mark_var_used(Named_object* no) +{ + if (no->is_variable()) + { + no->var_value()->set_is_used(); + + // When a type switch uses := to define a variable, then for + // each case with a single type we introduce a new variable with + // the appropriate type. When we do, if the newly introduced + // variable is used, then the type switch variable is used. + Type_switch_vars::iterator p = this->type_switch_vars_.find(no); + if (p != this->type_switch_vars_.end()) + p->second->var_value()->set_is_used(); + } +} diff --git a/gcc/go/gofrontend/parse.h b/gcc/go/gofrontend/parse.h index 0a3fe64..f45aa85 100644 --- a/gcc/go/gofrontend/parse.h +++ b/gcc/go/gofrontend/parse.h @@ -155,6 +155,11 @@ class Parse // break or continue statement with no label. typedef std::vector<std::pair<Statement*, Label*> > Bc_stack; + // Map from type switch variables to the variables they mask, so + // that a use of the type switch variable can become a use of the + // real variable. + typedef Unordered_map(Named_object*, Named_object*) Type_switch_vars; + // Parser nonterminals. void identifier_list(Typed_identifier_list*); Expression_list* expression_list(Expression*, bool may_be_sink); @@ -288,6 +293,10 @@ class Parse Statement* find_bc_statement(const Bc_stack*, const std::string&) const; + // Mark a variable as used. + void + mark_var_used(Named_object*); + // The lexer output we are parsing. Lex* lex_; // The current token. @@ -307,6 +316,8 @@ class Parse // References from the local function to variables defined in // enclosing functions. Enclosing_vars enclosing_vars_; + // Map from type switch variables to real variables. + Type_switch_vars type_switch_vars_; }; |