Check_types_traverse(Gogo* gogo)
: Traverse(traverse_variables
| traverse_constants
+ | traverse_functions
| traverse_statements
| traverse_expressions),
gogo_(gogo)
int
constant(Named_object*, bool);
+ int
+ function(Named_object*);
+
int
statement(Block*, size_t* pindex, Statement*);
return TRAVERSE_CONTINUE;
}
+// There are no types to check in a function, but this is where we
+// issue warnings about labels which are defined but not referenced.
+
+int
+Check_types_traverse::function(Named_object* no)
+{
+ no->func_value()->check_labels();
+ return TRAVERSE_CONTINUE;
+}
+
// Check that types are valid in a statement.
int
}
else
{
- error_at(location, "redefinition of label %qs",
+ error_at(location, "label %qs already defined",
Gogo::message_name(label_name).c_str());
inform(label->location(), "previous definition of %qs was here",
Gogo::message_name(label_name).c_str());
if (!ins.second)
{
// The label was already in the hash table.
- return ins.first->second;
+ Label* label = ins.first->second;
+ label->set_is_used();
+ return label;
}
else
{
gcc_assert(ins.first->second == NULL);
Label* label = new Label(label_name);
ins.first->second = label;
+ label->set_is_used();
return label;
}
}
+// Warn about labels that are defined but not used.
+
+void
+Function::check_labels() const
+{
+ for (Labels::const_iterator p = this->labels_.begin();
+ p != this->labels_.end();
+ p++)
+ {
+ Label* label = p->second;
+ if (!label->is_used())
+ error_at(label->location(), "label %qs defined and not used",
+ Gogo::message_name(label->name()).c_str());
+ }
+}
+
// Swap one function with another. This is used when building the
// thunk we use to call a function which calls recover. It may not
// work for any other case.
Label*
add_label_reference(const std::string& label_name);
+ // Warn about labels that are defined but not used.
+ void
+ check_labels() const;
+
// Whether this function calls the predeclared recover function.
bool
calls_recover() const
{
public:
Label(const std::string& name)
- : name_(name), location_(0), decl_(NULL)
+ : name_(name), location_(0), is_used_(false), decl_(NULL)
{ }
// Return the label's name.
is_defined() const
{ return this->location_ != 0; }
+ // Return whether the label has been used.
+ bool
+ is_used() const
+ { return this->is_used_; }
+
+ // Record that the label is used.
+ void
+ set_is_used()
+ { this->is_used_ = true; }
+
// Return the location of the definition.
source_location
location() const
// The location of the definition. This is 0 if the label has not
// yet been defined.
source_location location_;
+ // Whether the label has been used.
+ bool is_used_;
// The LABEL_DECL.
tree decl_;
};
// LABEL is the label of this statement if it has one.
void
-Parse::statement(const Label* label)
+Parse::statement(Label* label)
{
const Token* token = this->peek_token();
switch (token->classification())
if (!this->statement_may_start_here())
{
+ // Mark the label as used to avoid a useless error about an
+ // unused label.
+ label->set_is_used();
+
error_at(location, "missing statement after label");
this->unget_token(Token::make_operator_token(OPERATOR_SEMICOLON,
location));
// TypeSwitchGuard = [ identifier ":=" ] Expression "." "(" "type" ")" .
void
-Parse::switch_stat(const Label* label)
+Parse::switch_stat(Label* label)
{
gcc_assert(this->peek_token()->is_keyword(KEYWORD_SWITCH));
source_location location = this->location();
// "{" { ExprCaseClause } "}"
Statement*
-Parse::expr_switch_body(const Label* label, Expression* switch_val,
+Parse::expr_switch_body(Label* label, Expression* switch_val,
source_location location)
{
Switch_statement* statement = Statement::make_switch_statement(switch_val,
// "{" { TypeCaseClause } "}" .
Statement*
-Parse::type_switch_body(const Label* label, const Type_switch& type_switch,
+Parse::type_switch_body(Label* label, const Type_switch& type_switch,
source_location location)
{
Named_object* switch_no = NULL;
// SelectStat = "select" "{" { CommClause } "}" .
void
-Parse::select_stat(const Label* label)
+Parse::select_stat(Label* label)
{
gcc_assert(this->peek_token()->is_keyword(KEYWORD_SELECT));
source_location location = this->location();
// Condition = Expression .
void
-Parse::for_stat(const Label* label)
+Parse::for_stat(Label* label)
{
gcc_assert(this->peek_token()->is_keyword(KEYWORD_FOR));
source_location location = this->location();
// Push a statement on the break stack.
void
-Parse::push_break_statement(Statement* enclosing, const Label* label)
+Parse::push_break_statement(Statement* enclosing, Label* label)
{
if (this->break_stack_ == NULL)
this->break_stack_ = new Bc_stack();
// Push a statement on the continue stack.
void
-Parse::push_continue_statement(Statement* enclosing, const Label* label)
+Parse::push_continue_statement(Statement* enclosing, Label* label)
{
if (this->continue_stack_ == NULL)
this->continue_stack_ = new Bc_stack();
for (Bc_stack::const_reverse_iterator p = bc_stack->rbegin();
p != bc_stack->rend();
++p)
- if (p->second != NULL && p->second->name() == label)
- return p->first;
+ {
+ if (p->second != NULL && p->second->name() == label)
+ {
+ p->second->set_is_used();
+ return p->first;
+ }
+ }
return NULL;
}
token->identifier());
if (enclosing == NULL)
{
- error_at(token->location(),
- ("break label %qs not associated with "
- "for or switch or select"),
+ // If there is a label with this name, mark it as used to
+ // avoid a useless error about an unused label.
+ this->gogo_->add_label_reference(token->identifier());
+
+ error_at(token->location(), "invalid break label %qs",
Gogo::message_name(token->identifier()).c_str());
this->advance_token();
return;
token->identifier());
if (enclosing == NULL)
{
- error_at(token->location(),
- "continue label %qs not associated with for",
+ // If there is a label with this name, mark it as used to
+ // avoid a useless error about an unused label.
+ this->gogo_->add_label_reference(token->identifier());
+
+ error_at(token->location(), "invalid continue label %qs",
Gogo::message_name(token->identifier()).c_str());
this->advance_token();
return;
// For break and continue we keep a stack of statements with
// associated labels (if any). The top of the stack is used for a
// break or continue statement with no label.
- typedef std::vector<std::pair<Statement*, const Label*> > Bc_stack;
+ typedef std::vector<std::pair<Statement*, Label*> > Bc_stack;
// Parser nonterminals.
void identifier_list(Typed_identifier_list*);
bool* is_type_switch);
Expression* qualified_expr(Expression*, source_location);
Expression* id_to_expression(const std::string&, source_location);
- void statement(const Label*);
+ void statement(Label*);
bool statement_may_start_here();
void labeled_stmt(const std::string&, source_location);
Expression* simple_stat(bool, bool, Range_clause*, Type_switch*);
void go_or_defer_stat();
void return_stat();
void if_stat();
- void switch_stat(const Label*);
- Statement* expr_switch_body(const Label*, Expression*, source_location);
+ void switch_stat(Label*);
+ Statement* expr_switch_body(Label*, Expression*, source_location);
void expr_case_clause(Case_clauses*, bool* saw_default);
Expression_list* expr_switch_case(bool*);
- Statement* type_switch_body(const Label*, const Type_switch&,
- source_location);
+ Statement* type_switch_body(Label*, const Type_switch&, source_location);
void type_case_clause(Named_object*, Type_case_clauses*, bool* saw_default);
void type_switch_case(std::vector<Type*>*, bool*);
- void select_stat(const Label*);
+ void select_stat(Label*);
void comm_clause(Select_clauses*, bool* saw_default);
bool comm_case(bool*, Expression**, Expression**, Expression**,
std::string*, std::string*, bool*);
bool send_or_recv_stmt(bool*, Expression**, Expression**, Expression**,
std::string*, std::string*);
- void for_stat(const Label*);
+ void for_stat(Label*);
void for_clause(Expression**, Block**);
void range_clause_decl(const Typed_identifier_list*, Range_clause*);
void range_clause_expr(const Expression_list*, Range_clause*);
- void push_break_statement(Statement*, const Label*);
- void push_continue_statement(Statement*, const Label*);
+ void push_break_statement(Statement*, Label*);
+ void push_continue_statement(Statement*, Label*);
void pop_break_statement();
void pop_continue_statement();
Statement* find_bc_statement(const Bc_stack*, const std::string&);
package main
func main() {
- var i int;
- var j int;
- if true {}
- { return }
- i = 0;
- if true {} else i++;
- type s struct {};
- i = 0;
- type s2 int;
- var k = func (a int) int { return a+1 }(3);
- _, _ = j, k;
-ro: ;
+ var i int
+ var j int
+ if true {
+ }
+ {
+ return
+ }
+ i = 0
+ if true {
+ } else {
+ i++
+ }
+ type s struct{}
+ i = 0
+ type s2 int
+ var k = func(a int) int { return a + 1 }(3)
+ _, _ = j, k
}
-// $G $D/$F.go && $L $F.$A && ./$A.out
+// $G $D/$F.go && $L $F.$A
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
package main
func f() {
-exit: ;
+exit:
+ ;
+ goto exit
}
func main() {
-exit: ; // this should be legal (labels not properly scoped?)
+exit:
+ ; // this should be legal (labels not properly scoped?)
+ goto exit
}
/*
package main
func main() {
- var exit int;
+ var exit int
exit:
- _ = exit;
+ _ = exit
+ goto exit
}
package main
func f1() {
- exit:
- print("hi\n");
+exit:
+ print("hi\n")
+ goto exit
}
func f2() {
- const c = 1234;
+ const c = 1234
}
func f3() {
- i := c; // ERROR "undef"
+ i := c // ERROR "undef"
}
func main() {
- f3();
+ f3()
}
func main() {
L1:
-L2: for i := 0; i < 10; i++ {
- print(i);
- break L2;
+L2:
+ for i := 0; i < 10; i++ {
+ print(i)
+ break L2
}
-L3: ;
-L4: for i := 0; i < 10; i++ {
- print(i);
- break L4;
+L3:
+ ;
+L4:
+ for i := 0; i < 10; i++ {
+ print(i)
+ break L4
}
+ goto L1
+ goto L3
}
/*
package main
func main() {
- if true {} else L1: ;
- if true {} else L2: main() ;
+ if true {
+ } else {
+ L1:
+ }
+ if true {
+ } else {
+ L2:
+ main()
+ }
+ goto L1
+ goto L2
}
/*
func main() {
L:
for i := 0; i < 1; i++ {
-L1:
+ L1:
for {
- break L;
+ break L
}
- panic("BUG: not reached - break");
+ panic("BUG: not reached - break")
}
L2:
for i := 0; i < 1; i++ {
-L3:
+ L3:
for {
- continue L2;
+ continue L2
}
- panic("BUG: not reached - continue");
+ panic("BUG: not reached - continue")
+ }
+ if false {
+ goto L1
+ }
+ if false {
+ goto L3
}
}
L:
for {
for {
- break L2; // ERROR "L2"
- continue L2; // ERROR "L2"
+ break L2 // ERROR "L2"
+ continue L2 // ERROR "L2"
}
}
L1:
- x := 1;
- _ = x;
+ x := 1
+ _ = x
for {
- break L1; // ERROR "L1"
- continue L1; // ERROR "L1"
+ break L1 // ERROR "L1"
+ continue L1 // ERROR "L1"
}
+
+ goto L
}
case 1:
L1: // ERROR "statement"
default:
- L2: // correct since no semicolon is required before a '}'
+ // correct since no semicolon is required before a '}'
+ L2: // GCCGO_ERROR "not used"
}
}