summaryrefslogtreecommitdiff
path: root/odb/odb/semantics/elements.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'odb/odb/semantics/elements.cxx')
-rw-r--r--odb/odb/semantics/elements.cxx619
1 files changed, 619 insertions, 0 deletions
diff --git a/odb/odb/semantics/elements.cxx b/odb/odb/semantics/elements.cxx
new file mode 100644
index 0000000..b5793d0
--- /dev/null
+++ b/odb/odb/semantics/elements.cxx
@@ -0,0 +1,619 @@
+// file : odb/semantics/elements.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/gcc.hxx>
+
+#include <libcutl/compiler/type-info.hxx>
+
+#include <odb/cxx-lexer.hxx>
+
+#include <odb/semantics/elements.hxx>
+#include <odb/semantics/namespace.hxx>
+#include <odb/semantics/unit.hxx>
+
+using namespace std;
+
+namespace semantics
+{
+ // access
+ //
+ static char const* access_str[] = {"public", "protected", "private"};
+
+ char const* access::
+ string () const
+ {
+ return access_str[value_];
+ }
+
+ //
+ //
+ node::
+ node (path const& file, size_t line, size_t column, tree tn)
+ : tree_node_ (tn), loc_ (file, line, column)
+ {
+ }
+
+ node::
+ node ()
+ : loc_ (0)
+ {
+ // GCC plugin machinery #define's abort as a macro.
+ //
+ // std::abort ();
+ abort ();
+ }
+
+ // nameable
+ //
+
+ bool nameable::
+ in_scope (scope_type& s)
+ {
+ for (scope_type* p (&scope ());; p = &p->scope_ ())
+ {
+ //@@ Need to handle namespace extensions.
+ //
+ if (p == &s)
+ return true;
+
+ if (!p->named_p () || p->global_scope ())
+ break;
+ }
+
+ return false;
+ }
+
+ bool nameable::
+ anonymous_ () const
+ {
+ tree n (tree_node ());
+
+ if (TYPE_P (n))
+ {
+ tree name (0);
+
+ if (tree decl = TYPE_NAME (n))
+ name = DECL_NAME (decl);
+
+ return name != 0 && IDENTIFIER_ANON_P (name);
+ }
+
+ return true;
+ }
+
+ bool nameable::
+ fq_anonymous_ (scope_entry const* prev) const
+ {
+ scope_entry scope (this, prev);
+
+ // Nameable is fq-anonymous if all the paths to the global scope
+ // have at least one anonymous link.
+ //
+ if (defined_ != 0 || !named_.empty ())
+ {
+ if (named ().global_scope ())
+ return false;
+
+ if (defined_ != 0)
+ {
+ nameable const& s (defined_->scope ());
+
+ if (!scope.find (&s) && !s.fq_anonymous_ (&scope))
+ return false;
+ }
+
+ for (names_list::const_iterator i (named_.begin ()), e (named_.end ());
+ i != e; ++i)
+ {
+ nameable const& s ((*i)->scope ());
+
+ if (!scope.find (&s) && !s.fq_anonymous_ (&scope))
+ return false;
+ }
+ }
+
+ // If we can get a literal name for this type node, then it is not
+ // anonymous as long as its scope is not anonymous.
+ //
+ tree type (tree_node ());
+
+ if (TYPE_P (type))
+ {
+ tree name (0);
+
+ if (tree decl = TYPE_NAME (type))
+ {
+ name = DECL_NAME (decl);
+ if (name != 0 && IDENTIFIER_ANON_P (name))
+ return true;
+
+ tree s (CP_DECL_CONTEXT (decl));
+
+ gcc_tree_code_type tc (TREE_CODE (s));
+
+ if (tc == TYPE_DECL)
+ s = TREE_TYPE (s);
+ else if (tc == NAMESPACE_DECL)
+ {
+ // "Unwind" any inline namespaces since they are not in
+ // semantic grapth.
+ //
+ while (s != global_namespace)
+ {
+ tree prev (CP_DECL_CONTEXT (s));
+
+#if BUILDING_GCC_MAJOR >= 8
+ if (!is_nested_namespace (prev, s, true))
+#else
+ if (!is_associated_namespace (prev, s))
+#endif
+ break;
+
+ s = prev;
+ }
+ }
+
+ if (nameable* n = dynamic_cast<nameable*> (unit ().find (s)))
+ return scope.find (n) || n->fq_anonymous_ (&scope);
+ }
+ else
+ return false; // Assume this is a derived type (e.g., pointer).
+ }
+
+ return true;
+ }
+
+ bool nameable::
+ fq_anonymous (names* hint) const
+ {
+ if (hint != 0 || defined_ != 0)
+ {
+ names& n (hint ? *hint : *defined_);
+
+ if (n.global_scope ())
+ return false;
+
+ return n.scope ().fq_anonymous ();
+ }
+ else
+ return fq_anonymous ();
+ }
+
+ static string
+ qualify_names (string const& n, bool qualify_first)
+ {
+ // @@ Creating a lexer for each call is a bad idea. Need
+ // to cache it somewhere.
+ //
+ cxx_string_lexer l;
+ l.start (n);
+
+ string r, t;
+ bool punc (false);
+ bool scoped (false);
+
+ // Names returned by GCC's type_as_string() (on which this function
+ // is called) include inline namespaces (e.g., std::__cxx11::string).
+ // So, besides fully-qualifying names, this function also needs to get
+ // rid of those. The idea is to resolve names as we lex them, skipping
+ // inline namespaces and stopping once we reach something other than a
+ // namespace.
+ //
+ tree ns (global_namespace);
+ tree id;
+
+ for (cpp_ttype tt = l.next (t, &id); tt != CPP_EOF; tt = l.next (t, &id))
+ {
+ if (punc && tt > CPP_LAST_PUNCTUATOR)
+ r += ' ';
+
+ punc = false;
+ tree new_ns (global_namespace); // By default, revert to global.
+
+ switch (static_cast<unsigned> (tt))
+ {
+ case CPP_LESS:
+ {
+ r += "< ";
+ break;
+ }
+ case CPP_GREATER:
+ {
+ r += " >";
+ break;
+ }
+ case CPP_COMMA:
+ {
+ r += ", ";
+ break;
+ }
+ case CPP_NAME:
+ {
+ // Check if this is a namespace and, if so, whether it is
+ // inline.
+ //
+ if (ns != 0)
+ {
+ new_ns = lookup_qualified_name (ns, id, false, false);
+
+ if (new_ns == error_mark_node ||
+ TREE_CODE (new_ns) != NAMESPACE_DECL)
+ new_ns = 0; // Not a namespace, stop resolving.
+ else
+ {
+ // Check if this is an inline namespace and skip it if so.
+ //
+#if BUILDING_GCC_MAJOR >= 8
+ if (is_nested_namespace (ns, new_ns, true))
+#else
+ if (is_associated_namespace (ns, new_ns))
+#endif
+ {
+ // Skip also the following scope operator. Strictly speaking
+ // there could be none (i.e., this is a name of an inline
+ // namespace) but we only use this function to print names
+ // of anonymous types.
+ //
+ assert (l.next (t) == CPP_SCOPE);
+ continue;
+ }
+ }
+ }
+ else
+ new_ns = 0; // Keep it disabled until we hit a new name.
+
+ // If the name was not preceeded with '::', qualify it.
+ //
+ if (!scoped)
+ {
+ if (!qualify_first)
+ qualify_first = true;
+ else
+ r += "::";
+ }
+
+ r += t;
+ punc = true;
+ break;
+ }
+ case CPP_KEYWORD:
+ case CPP_NUMBER:
+ {
+ r += t;
+ punc = true;
+ break;
+ }
+ case CPP_SCOPE:
+ {
+ new_ns = ns; // Don't change the namespace.
+ }
+ // Fall through.
+ default:
+ {
+ r += t;
+ break;
+ }
+ }
+
+ scoped = (tt == CPP_SCOPE);
+ ns = new_ns;
+ }
+
+ return r;
+ }
+
+ string nameable::
+ name_ () const
+ {
+ tree n (tree_node ());
+
+ if (!TYPE_P (n))
+ return "<anonymous>";
+
+ // @@ Doing this once and caching the result is probably a
+ // good idea.
+ //
+ return qualify_names (
+ type_as_string (n, TFF_PLAIN_IDENTIFIER | TFF_UNQUALIFIED_NAME), false);
+ }
+
+ string nameable::
+ fq_name () const
+ {
+ return fq_name_ (0);
+ }
+
+ string nameable::
+ fq_name_ (scope_entry const* prev) const
+ {
+ // @@ Doing this once and caching the result is probably a
+ // good idea.
+ //
+ scope_entry scope (this, prev);
+
+ if (named_p () && named ().global_scope ())
+ return "";
+
+ if (defined_ != 0)
+ {
+ nameable const& s (defined_->scope ());
+
+ if (!scope.find (&s) && !s.fq_anonymous_ (&scope))
+ return s.fq_name_ (&scope) + "::" + name ();
+ }
+
+ for (names_list::const_iterator i (named_.begin ()), e (named_.end ());
+ i != e; ++i)
+ {
+ nameable const& s ((*i)->scope ());
+
+ if (!scope.find (&s) && !s.fq_anonymous_ (&scope))
+ return s.fq_name_ (&scope) + "::" + name ();
+ }
+
+ tree n (tree_node ());
+
+ if (!TYPE_P (n))
+ return "<anonymous>";
+
+ return qualify_names (type_as_string (n, TFF_PLAIN_IDENTIFIER), true);
+ }
+
+ string nameable::
+ fq_name (names* hint) const
+ {
+ if (hint != 0 || defined_ != 0)
+ {
+ names& n (hint ? *hint : *defined_);
+
+ if (n.global_scope ())
+ return "";
+
+ return n.scope ().fq_name () + "::" + n.name ();
+ }
+ else
+ {
+ // Since there was no hint, prefer the literal name over the names
+ // edges.
+ //
+ tree n (tree_node ());
+
+ if (TYPE_P (n))
+ return qualify_names (type_as_string (n, TFF_PLAIN_IDENTIFIER), true);
+
+ // Last resort is to call the other version of fq_name which will
+ // check the names edges.
+ //
+ return fq_name ();
+ }
+ }
+
+ // scope
+ //
+
+ scope::names_iterator_pair scope::
+ find (string const& name) const
+ {
+ names_map::const_iterator i (names_map_.find (name));
+
+ if (i == names_map_.end ())
+ return names_iterator_pair (names_.end (), names_.end ());
+ else
+ return names_iterator_pair (i->second.begin (), i->second.end ());
+ }
+
+ scope::names_iterator scope::
+ find (names& e)
+ {
+ list_iterator_map::iterator i (iterator_map_.find (&e));
+ return i != iterator_map_.end () ? i->second : names_.end ();
+ }
+
+ static bool
+ is_base (type_id const& b, compiler::type_info const& d)
+ {
+ using compiler::type_info;
+
+ for (type_info::base_iterator i (d.begin_base ());
+ i != d.end_base (); ++i)
+ {
+ type_info const& ti (i->type_info ());
+
+ if (b == ti.type_id () || is_base (b, ti))
+ return true;
+ }
+
+ return false;
+ }
+
+ names* scope::
+ lookup (string const& name,
+ type_id const& ti,
+ unsigned int flags,
+ bool* hidden) const
+ {
+ names_iterator_pair p (find (name));
+ names* r (0);
+
+ for (names_const_iterator i (p.first); i != p.second; ++i)
+ {
+ type_id const& xti (typeid (i->named ()));
+
+ // If types are equal, then we found a match. Also check if ti is
+ // a base type of xti.
+ //
+ if (xti == ti || is_base (ti, compiler::lookup (xti)))
+ {
+ if (r != 0)
+ {
+ // If both are namespaces, then the one is just an extension
+ // of the other.
+ //
+ if (!(r->named ().is_a<namespace_> () &&
+ i->named ().is_a<namespace_> ()))
+ throw ambiguous (*r, *i);
+ }
+ else
+ r = &*i;
+ }
+ }
+
+ if (r != 0)
+ return r;
+
+ // If we found a name but the types didn't match, then bail out
+ // unless we want hidden names.
+ //
+ if (p.first != p.second)
+ {
+ if (hidden != 0)
+ *hidden = true;
+
+ if ((flags & include_hidden) == 0)
+ return 0;
+ }
+
+ // Look in the outer scope unless requested not to or if this is
+ // the global scope.
+ //
+ if ((flags & exclude_outer) == 0 && named_p () && !global_scope ())
+ return scope ().lookup (name, ti, flags, hidden);
+
+ return 0;
+ }
+
+ void scope::
+ add_edge_left (names& e)
+ {
+ names_list::iterator i (names_.insert (names_.end (), &e));
+ iterator_map_[&e] = i;
+ names_map_[e.name ()].push_back (&e);
+ }
+
+ void scope::
+ add_edge_left (names& e, names_iterator after)
+ {
+ names_list::iterator i;
+
+ if (after.base () == names_.end ())
+ i = names_.insert (names_.begin (), &e);
+ else
+ {
+ names_list::iterator j (after.base ());
+ i = names_.insert (++j, &e);
+ }
+
+ iterator_map_[&e] = i;
+ names_map_[e.name ()].push_back (&e);
+ }
+
+ // type info
+ //
+ namespace
+ {
+ struct init
+ {
+ init ()
+ {
+ using compiler::type_info;
+
+ // node
+ //
+ insert (type_info (typeid (node)));
+
+ // edge
+ //
+ insert (type_info (typeid (edge)));
+
+ // names
+ //
+ {
+ type_info ti (typeid (names));
+ ti.add_base (typeid (edge));
+ insert (ti);
+ }
+
+ // declares
+ //
+ {
+ type_info ti (typeid (declares));
+ ti.add_base (typeid (names));
+ insert (ti);
+ }
+
+ // defines
+ //
+ {
+ type_info ti (typeid (defines));
+ ti.add_base (typeid (declares));
+ insert (ti);
+ }
+
+ // typedefs
+ //
+ {
+ type_info ti (typeid (typedefs));
+ ti.add_base (typeid (declares));
+ insert (ti);
+ }
+
+ // nameable
+ //
+ {
+ type_info ti (typeid (nameable));
+ ti.add_base (typeid (node));
+ insert (ti);
+ }
+
+ // scope
+ //
+ {
+ type_info ti (typeid (scope));
+ ti.add_base (typeid (nameable));
+ insert (ti);
+ }
+
+ // type
+ //
+ {
+ type_info ti (typeid (type));
+ ti.add_base (typeid (nameable));
+ insert (ti);
+ }
+
+ // belongs
+ //
+ {
+ type_info ti (typeid (belongs));
+ ti.add_base (typeid (edge));
+ insert (ti);
+ }
+
+ // instance
+ //
+ {
+ type_info ti (typeid (instance));
+ ti.add_base (typeid (node));
+ insert (ti);
+ }
+
+ // data_member
+ //
+ {
+ type_info ti (typeid (data_member));
+ ti.add_base (typeid (nameable));
+ ti.add_base (typeid (instance));
+ insert (ti);
+ }
+
+ // unsupported_type
+ //
+ {
+ type_info ti (typeid (unsupported_type));
+ ti.add_base (typeid (type));
+ insert (ti);
+ }
+ }
+ } init_;
+ }
+}