Skip to content

Commit

Permalink
compiler: Record each import as a distinct alias.
Browse files Browse the repository at this point in the history
This patch introduces the Package_alias class which is a
finer representation of the different between a package and the
aliases it is imported under.  Each alias keeps track of the location
of its import statement and how many times that alias has been used.
This allows the gofrontend to report when a specific import has not
been used even if a symbol from the package has been used by another
import.

Fixes golang/go#12326.

Change-Id: I03bef25c5f9a32beded31a7b1a8140f7bea54ed2
Reviewed-on: https://go-review.googlesource.com/14259
Reviewed-by: Ian Lance Taylor <[email protected]>
  • Loading branch information
Chris Manghane authored and ianlancetaylor committed Oct 6, 2015
1 parent d8150af commit 3039d79
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 75 deletions.
97 changes: 73 additions & 24 deletions go/gogo.cc
Original file line number Diff line number Diff line change
Expand Up @@ -516,7 +516,6 @@ Gogo::import_package(const std::string& filename,
{
Package* package = p->second;
package->set_location(location);
package->set_is_imported();
std::string ln = local_name;
bool is_ln_exported = is_local_name_exported;
if (ln.empty())
Expand All @@ -525,19 +524,22 @@ Gogo::import_package(const std::string& filename,
go_assert(!ln.empty());
is_ln_exported = Lex::is_exported_name(ln);
}
if (ln == ".")
if (ln == "_")
;
else if (ln == ".")
{
Bindings* bindings = package->bindings();
for (Bindings::const_declarations_iterator p =
bindings->begin_declarations();
p != bindings->end_declarations();
++p)
this->add_dot_import_object(p->second);
std::string dot_alias = "." + package->package_name();
package->add_alias(dot_alias, location);
}
else if (ln == "_")
package->set_uses_sink_alias();
else
{
package->add_alias(ln, location);
ln = this->pack_hidden_name(ln, is_ln_exported);
this->package_->bindings()->add_package(ln, package);
}
Expand All @@ -563,7 +565,6 @@ Gogo::import_package(const std::string& filename,
"being compiled (see -fgo-pkgpath option)"));

this->imports_.insert(std::make_pair(filename, package));
package->set_is_imported();
}

delete stream;
Expand Down Expand Up @@ -1544,7 +1545,10 @@ Gogo::lookup(const std::string& name, Named_object** pfunction) const
if (ret != NULL)
{
if (ret->package() != NULL)
ret->package()->note_usage();
{
std::string dot_alias = "." + ret->package()->package_name();
ret->package()->note_usage(dot_alias);
}
return ret;
}
}
Expand Down Expand Up @@ -1594,10 +1598,14 @@ Gogo::add_imported_package(const std::string& real_name,

*padd_to_globals = false;

if (alias_arg == ".")
*padd_to_globals = true;
else if (alias_arg == "_")
ret->set_uses_sink_alias();
if (alias_arg == "_")
;
else if (alias_arg == ".")
{
*padd_to_globals = true;
std::string dot_alias = "." + real_name;
ret->add_alias(dot_alias, location);
}
else
{
std::string alias = alias_arg;
Expand All @@ -1606,6 +1614,7 @@ Gogo::add_imported_package(const std::string& real_name,
alias = real_name;
is_alias_exported = Lex::is_exported_name(alias);
}
ret->add_alias(alias, location);
alias = this->pack_hidden_name(alias, is_alias_exported);
Named_object* no = this->package_->bindings()->add_package(alias, ret);
if (!no->is_package())
Expand Down Expand Up @@ -2356,15 +2365,30 @@ Gogo::clear_file_scope()
++p)
{
Package* package = p->second;
if (package != this->package_
&& package->is_imported()
&& !package->used()
&& !package->uses_sink_alias()
&& !quiet)
error_at(package->location(), "imported and not used: %s",
Gogo::message_name(package->package_name()).c_str());
package->clear_is_imported();
package->clear_uses_sink_alias();
if (package != this->package_ && !quiet)
{
for (Package::Aliases::const_iterator p1 = package->aliases().begin();
p1 != package->aliases().end();
++p1)
{
if (!p1->second->used())
{
// Give a more refined error message if the alias name is known.
std::string pkg_name = package->package_name();
if (p1->first != pkg_name && p1->first[0] != '.')
{
error_at(p1->second->location(),
"imported and not used: %s as %s",
Gogo::message_name(pkg_name).c_str(),
Gogo::message_name(p1->first).c_str());
}
else
error_at(p1->second->location(),
"imported and not used: %s",
Gogo::message_name(pkg_name).c_str());
}
}
}
package->clear_used();
}
}
Expand Down Expand Up @@ -7741,8 +7765,7 @@ Package::Package(const std::string& pkgpath,
const std::string& pkgpath_symbol, Location location)
: pkgpath_(pkgpath), pkgpath_symbol_(pkgpath_symbol),
package_name_(), bindings_(new Bindings(NULL)), priority_(0),
location_(location), used_(false), is_imported_(false),
uses_sink_alias_(false)
location_(location)
{
go_assert(!pkgpath.empty());

Expand Down Expand Up @@ -7796,6 +7819,16 @@ Package::set_priority(int priority)
this->priority_ = priority;
}

// Note that symbol from this package was and qualified by ALIAS.

void
Package::note_usage(const std::string& alias) const
{
Aliases::const_iterator p = this->aliases_.find(alias);
go_assert(p != this->aliases_.end());
p->second->note_usage();
}

// Forget a given usage. If forgetting this usage means this package becomes
// unused, report that error.

Expand All @@ -7811,7 +7844,7 @@ Package::forget_usage(Expression* usage) const

if (this->fake_uses_.empty())
error_at(this->location(), "imported and not used: %s",
Gogo::message_name(this->package_name()).c_str());
Gogo::message_name(this->package_name()).c_str());
}

// Clear the used field for the next file. If the only usages of this package
Expand All @@ -7820,10 +7853,26 @@ Package::forget_usage(Expression* usage) const
void
Package::clear_used()
{
if (this->used_ > this->fake_uses_.size())
std::string dot_alias = "." + this->package_name();
Aliases::const_iterator p = this->aliases_.find(dot_alias);
if (p != this->aliases_.end() && p->second->used() > this->fake_uses_.size())
this->fake_uses_.clear();

this->used_ = 0;
this->aliases_.clear();
}

Package_alias*
Package::add_alias(const std::string& alias, Location location)
{
Aliases::const_iterator p = this->aliases_.find(alias);
if (p == this->aliases_.end())
{
std::pair<Aliases::iterator, bool> ret;
ret = this->aliases_.insert(std::make_pair(alias,
new Package_alias(location)));
p = ret.first;
}
return p->second;
}

// Determine types of constants. Everything else in a package
Expand Down
97 changes: 50 additions & 47 deletions go/gogo.h
Original file line number Diff line number Diff line change
Expand Up @@ -2771,6 +2771,37 @@ class Unnamed_label
Blabel* blabel_;
};

// An alias for an imported package.

class Package_alias
{
public:
Package_alias(Location location)
: location_(location), used_(0)
{ }

// The location of the import statement.
Location
location()
{ return this->location_; }

// How many symbols from the package were used under this alias.
size_t
used() const
{ return this->used_; }

// Note that some symbol was used under this alias.
void
note_usage()
{ this->used_++; }

private:
// The location of the import statement.
Location location_;
// The amount of times some name from this package was used under this alias.
size_t used_;
};

// An imported package.

class Package
Expand All @@ -2793,7 +2824,7 @@ class Package
void
set_pkgpath_symbol(const std::string&);

// Return the location of the import statement.
// Return the location of the most recent import statement.
Location
location() const
{ return this->location_; }
Expand Down Expand Up @@ -2829,15 +2860,18 @@ class Package
bindings()
{ return this->bindings_; }

// Whether some symbol from the package was used.
bool
used() const
{ return this->used_ > 0; }
// Type used to map import names to package aliases.
typedef std::map<std::string, Package_alias*> Aliases;

// Note that some symbol from this package was used.
// Return the set of package aliases.
const Aliases&
aliases() const
{ return this->aliases_; }

// Note that some symbol from this package was used and qualified by ALIAS.
// For dot imports, the ALIAS should be ".PACKAGE_NAME".
void
note_usage() const
{ this->used_++; }
note_usage(const std::string& alias) const;

// Note that USAGE might be a fake usage of this package.
void
Expand All @@ -2852,36 +2886,6 @@ class Package
void
clear_used();

// Whether this package was imported in the current file.
bool
is_imported() const
{ return this->is_imported_; }

// Note that this package was imported in the current file.
void
set_is_imported()
{ this->is_imported_ = true; }

// Clear the imported field for the next file.
void
clear_is_imported()
{ this->is_imported_ = false; }

// Whether this package was imported with a name of "_".
bool
uses_sink_alias() const
{ return this->uses_sink_alias_; }

// Note that this package was imported with a name of "_".
void
set_uses_sink_alias()
{ this->uses_sink_alias_ = true; }

// Clear the sink alias field for the next file.
void
clear_uses_sink_alias()
{ this->uses_sink_alias_ = false; }

// Look up a name in the package. Returns NULL if the name is not
// found.
Named_object*
Expand All @@ -2898,6 +2902,10 @@ class Package
set_location(Location location)
{ this->location_ = location; }

// Add a package name as an ALIAS for this package.
Package_alias*
add_alias(const std::string& alias, Location);

// Add a constant to the package.
Named_object*
add_constant(const Typed_identifier& tid, Expression* expr)
Expand Down Expand Up @@ -2942,18 +2950,13 @@ class Package
// than the priority of all of the packages that it imports. This
// is used to run init functions in the right order.
int priority_;
// The location of the import statement.
// The location of the most recent import statement.
Location location_;
// The amount of times some name from this package was used. This is mutable
// because we can use a package even if we have a const pointer to it.
mutable size_t used_;
// A set of possibly fake uses of this package. This is mutable because we
// The set of aliases associated with this package.
Aliases aliases_;
// A set of possibly fake uses of this package. This is mutable because we
// can track fake uses of a package even if we have a const pointer to it.
mutable std::set<Expression*> fake_uses_;
// True if this package was imported in the current file.
bool is_imported_;
// True if this package was imported with a name of "_".
bool uses_sink_alias_;
};

// Return codes for the traversal functions. This is not an enum
Expand Down
4 changes: 2 additions & 2 deletions go/parse.cc
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ Parse::qualified_ident(std::string* pname, Named_object** ppackage)
return false;
}

package->package_value()->note_usage();
package->package_value()->note_usage(Gogo::unpack_hidden_name(name));

token = this->advance_token();
if (!token->is_identifier())
Expand Down Expand Up @@ -2430,7 +2430,7 @@ Parse::operand(bool may_be_sink, bool* is_parenthesized)
return Expression::make_error(location);
}
package = named_object->package_value();
package->note_usage();
package->note_usage(id);
id = this->peek_token()->identifier();
is_exported = this->peek_token()->is_identifier_exported();
packed = this->gogo_->pack_hidden_name(id, is_exported);
Expand Down
2 changes: 0 additions & 2 deletions go/unsafe.cc
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,6 @@ Gogo::import_unsafe(const std::string& local_name, bool is_local_name_exported,
}

package->set_location(location);
package->set_is_imported();

this->imports_.insert(std::make_pair("unsafe", package));

Bindings* bindings = package->bindings();
Expand Down

0 comments on commit 3039d79

Please sign in to comment.