// See the file "COPYING" in the main distribution directory for copyright. #pragma once #include #include namespace zeek { class ODesc; namespace detail { class Location final { public: constexpr Location(const char* fname, int line_f, int line_l) noexcept : filename(fname) { SetLines(line_f, line_l); } Location() = default; void Describe(ODesc* d) const; bool operator==(const Location& l) const; bool operator!=(const Location& l) const { return ! (*this == l); } const char* FileName() const { return filename; } int FirstLine() const { return first_line; } int LastLine() const { return last_line; } void SetFile(const char* fname) { filename = fname; } void SetLine(int line) { SetLines(line, line); } constexpr void SetLines(int first, int last) { if ( first > last ) { // We don't use std::swap() here because it's not // constexpr-enabled on older versions of C++. auto tmp = first; first = last; last = tmp; } first_line = first; last_line = last; } void SetFirstLine(int line) { SetLines(line, last_line); } void SetLastLine(int line) { SetLines(first_line, line); } void IncrementLine(int incr = 1) { first_line += incr; last_line += incr; } private: const char* filename = nullptr; int first_line = 0, last_line = 0; }; #define YYLTYPE zeek::detail::yyltype using yyltype = Location; YYLTYPE GetCurrentLocation(); void SetCurrentLocation(YYLTYPE currloc); // Used to mean "no location associated with this object". inline constexpr Location no_location("", 0, 0); // Current start/end location. extern Location start_location; extern Location end_location; // Used by parser to set the above. inline void set_location(const Location loc) { start_location = end_location = loc; } inline void set_location(const Location start, const Location end) { start_location = start; end_location = end; } // Helper for updating e's location to the one used by o, returning e. template T with_location_of(T e, const U& o) { e->SetLocationInfo(o->GetLocationInfo()); return e; } } // namespace detail class Obj { public: Obj() { // A bit of a hack. We'd like to associate location // information with every object created when parsing, // since for them, the location is generally well-defined. // We could maintain a separate flag that tells us whether // we're inside a parse, but the parser also sets the // location to no_location when it's done, so it makes // sense to just check for that. *However*, start_location // and end_location are maintained as their own objects // rather than pointers or references, so we can't directly // check them for equality with no_location. So instead // we check for whether start_location has a line number // of 0, which should only happen if it's been assigned // to no_location (or hasn't been initialized at all). location = nullptr; if ( detail::start_location.FirstLine() != 0 ) SetLocationInfo(&detail::start_location, &detail::end_location); } virtual ~Obj(); /* disallow copying */ Obj(const Obj&) = delete; Obj& operator=(const Obj&) = delete; // Report user warnings/errors. If obj2 is given, then it's // included in the message, though if pinpoint_only is non-zero, // then obj2 is only used to pinpoint the location. void Warn(const char* msg, const Obj* obj2 = nullptr, bool pinpoint_only = false, const detail::Location* expr_location = nullptr) const; void Error(const char* msg, const Obj* obj2 = nullptr, bool pinpoint_only = false, const detail::Location* expr_location = nullptr) const; // Report internal errors. [[noreturn]] void BadTag(const char* msg, const char* t1 = nullptr, const char* t2 = nullptr) const; // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define CHECK_TAG(t1, t2, text, tag_to_text_func) \ { \ if ( (t1) != (t2) ) \ BadTag(text, tag_to_text_func(t1), tag_to_text_func(t2)); \ } [[noreturn]] void Internal(const char* msg) const; void InternalWarning(const char* msg) const; virtual void Describe(ODesc* d) const { /* FIXME: Add code */ }; void AddLocation(ODesc* d) const; // Get location info for debugging. virtual const detail::Location* GetLocationInfo() const { return location ? location : &detail::no_location; } virtual bool SetLocationInfo(const detail::Location* loc) { return SetLocationInfo(loc, loc); } // Location = range from start to end. virtual bool SetLocationInfo(const detail::Location* start, const detail::Location* end); // Set new end-of-location information. This is used to // extend compound objects such as statement lists. virtual void UpdateLocationEndInfo(const detail::Location& end); // Enable notification of plugins when this objects gets destroyed. void NotifyPluginsOnDtor() { notify_plugins = true; } int RefCnt() const { return ref_cnt; } // Helper class to temporarily suppress errors // as long as there exist any instances. class SuppressErrors { public: SuppressErrors() { ++Obj::suppress_errors; } ~SuppressErrors() { --Obj::suppress_errors; } }; void Print() const; protected: detail::Location* location; // all that matters in real estate private: friend class SuppressErrors; void DoMsg(ODesc* d, const char s1[], const Obj* obj2 = nullptr, bool pinpoint_only = false, const detail::Location* expr_location = nullptr) const; void PinPoint(ODesc* d, const Obj* obj2 = nullptr, bool pinpoint_only = false) const; friend inline void Ref(Obj* o); friend inline void Unref(Obj* o); int ref_cnt = 1; bool notify_plugins = false; // If non-zero, do not print runtime errors. Useful for // speculative evaluation. static int suppress_errors; }; // Sometimes useful when dealing with Obj subclasses that have their // own (protected) versions of Error. inline void Error(const Obj* o, const char* msg) { o->Error(msg); } [[noreturn]] extern void bad_ref(int type); inline void Ref(Obj* o) { if ( ++(o->ref_cnt) <= 1 ) bad_ref(0); if ( o->ref_cnt == INT_MAX ) bad_ref(1); } inline void Unref(Obj* o) { if ( o && --o->ref_cnt <= 0 ) { if ( o->ref_cnt < 0 ) bad_ref(2); delete o; // We could do the following if o were passed by reference. // o = (Obj*) 0xcd; } } // A dict_delete_func that knows to Unref() dictionary entries. extern void obj_delete_func(void* v); } // namespace zeek