aboutsummaryrefslogtreecommitdiff
path: root/gcc/go
diff options
context:
space:
mode:
authorIan Lance Taylor <ian@gcc.gnu.org>2012-01-23 23:55:31 +0000
committerIan Lance Taylor <ian@gcc.gnu.org>2012-01-23 23:55:31 +0000
commitb1b3aec1b1e26c22791f21d971a851af4df2dad2 (patch)
treeed963d480affb07d364c84b0b56a97f7bb92427b /gcc/go
parent8bae34da8a4625767bc7e1caab295855963ff280 (diff)
downloadgcc-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.cc1
-rw-r--r--gcc/go/gofrontend/gogo.cc42
-rw-r--r--gcc/go/gofrontend/gogo.h22
-rw-r--r--gcc/go/gofrontend/lex.cc14
-rw-r--r--gcc/go/gofrontend/lex.h7
-rw-r--r--gcc/go/gofrontend/parse.cc52
-rw-r--r--gcc/go/gofrontend/parse.h11
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_;
};