| // Copyright 2017-2020 The Verible Authors. | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| #ifndef VERIBLE_VERILOG_ANALYSIS_VERILOG_PROJECT_H_ | |
| #define VERIBLE_VERILOG_ANALYSIS_VERILOG_PROJECT_H_ | |
| #include <map> | |
| #include <memory> | |
| #include <string> | |
| #include <vector> | |
| #include "absl/status/status.h" | |
| #include "absl/status/statusor.h" | |
| #include "absl/strings/string_view.h" | |
| #include "absl/types/optional.h" | |
| #include "common/strings/mem_block.h" | |
| #include "common/strings/string_memory_map.h" | |
| #include "common/text/text_structure.h" | |
| #include "verilog/analysis/verilog_analyzer.h" | |
| #include "verilog/analysis/verilog_filelist.h" | |
| namespace verilog { | |
| class VerilogProject; | |
| // A read-only view of a single Verilog source file. | |
| class VerilogSourceFile { | |
| public: | |
| VerilogSourceFile(absl::string_view referenced_path, | |
| absl::string_view resolved_path, absl::string_view corpus) | |
| : referenced_path_(referenced_path), | |
| resolved_path_(resolved_path), | |
| corpus_(corpus) {} | |
| // When a file is not found among a set of paths, remember it with an | |
| // error status. | |
| VerilogSourceFile(absl::string_view referenced_path, | |
| const absl::Status& status); | |
| VerilogSourceFile(const VerilogSourceFile&) = delete; | |
| VerilogSourceFile& operator=(const VerilogSourceFile&) = delete; | |
| VerilogSourceFile(VerilogSourceFile&&) = | |
| default; // need for std::map::emplace | |
| VerilogSourceFile& operator=(VerilogSourceFile&&) = delete; | |
| virtual ~VerilogSourceFile() = default; | |
| // Opens a file using the resolved path and loads the contents into memory. | |
| // This does not attempt to parse/analyze the contents. | |
| virtual absl::Status Open(); | |
| // After successful Open(), the content is filled; empty otherwise. | |
| virtual absl::string_view GetContent() const; | |
| // Attempts to lex and parse the file. | |
| // Will Open() if the file is not already opened. | |
| // Depending on context, not all files are suitable for standalone parsing. | |
| virtual absl::Status Parse(); | |
| // After Parse(), text structure may contain other analyzed structural forms. | |
| // Before successful Parse(), this is not initialized and returns nullptr. | |
| virtual const verible::TextStructureView* GetTextStructure() const; | |
| // Returns the first non-Ok status if there is one, else OkStatus(). | |
| absl::Status Status() const { return status_; } | |
| // Return human readable error messages if available. | |
| std::vector<std::string> ErrorMessages() const; | |
| // Returns the name used to reference the file. | |
| absl::string_view ReferencedPath() const { return referenced_path_; } | |
| // Returns the corpus to which this file belongs (e.g., | |
| // github.com/chipsalliance/verible). | |
| absl::string_view Corpus() const { return corpus_; } | |
| // Returns a (possibly more qualified) path to the file. | |
| absl::string_view ResolvedPath() const { return resolved_path_; } | |
| // Comparator for ordering files for internal storage. | |
| // Known limitation: this comparator won't work if you have multiple files | |
| // with the same name referenced without a distinguishing path prefix. | |
| struct Less { | |
| using is_transparent = void; // hetergenous compare | |
| static absl::string_view to_string_view(absl::string_view s) { return s; } | |
| static absl::string_view to_string_view(const VerilogSourceFile& f) { | |
| return f.ReferencedPath(); | |
| } | |
| static absl::string_view to_string_view(const VerilogSourceFile* f) { | |
| return f->ReferencedPath(); | |
| } | |
| // T1/T2 could be any combination of: | |
| // {const VerilogSourceFile&, absl::string_view}. | |
| template <typename T1, typename T2> | |
| bool operator()(T1 left, T2 right) const { | |
| return to_string_view(left) < to_string_view(right); | |
| } | |
| }; | |
| private: | |
| friend class VerilogProject; | |
| protected: | |
| // Tracking state for linear progression of analysis, which allows | |
| // prerequisite actions to be cached. | |
| enum State { | |
| // Only the paths have been established. | |
| kInitialized, | |
| // Files have been opened and contents loaded. | |
| kOpened, | |
| // Parse() as at least attempted. | |
| // Lexical and syntax tree structures may be available. | |
| kParsed, | |
| }; | |
| // This is the how the file is referenced either in a file list or `include. | |
| // This should be const for the lifetime of this object, but isn't declared as | |
| // such so this class can have a default move constructor. | |
| std::string referenced_path_; | |
| // Often a concatenation of a base path with a relative path. | |
| // This should be const for the lifetime of this object, but isn't declared as | |
| // such so this class can have a default move constructor. | |
| std::string resolved_path_; | |
| // The corpus to which this file belongs to (e.g., | |
| // github.com/chipsalliance/verible). | |
| absl::string_view corpus_; | |
| // State of this file. | |
| State state_ = State::kInitialized; | |
| // Holds any diagostics for problems encountered finding/reading this file. | |
| absl::Status status_; | |
| // MemBock holding the file content so that it can be used in other contexts. | |
| std::shared_ptr<verible::MemBlock> content_; | |
| // Contains token streams and syntax tree after Parse(). | |
| std::unique_ptr<VerilogAnalyzer> analyzed_structure_; | |
| }; | |
| // Printable representation for debugging. | |
| std::ostream& operator<<(std::ostream&, const VerilogSourceFile&); | |
| // An in-memory source file that doesn't require file-system access, | |
| // nor create temporary files. | |
| class InMemoryVerilogSourceFile final : public VerilogSourceFile { | |
| public: | |
| // filename can be fake, it is not used to open any file. | |
| InMemoryVerilogSourceFile(absl::string_view filename, | |
| std::shared_ptr<verible::MemBlock> content, | |
| absl::string_view corpus = "") | |
| : VerilogSourceFile(filename, filename, corpus) { | |
| content_ = std::move(content); | |
| } | |
| // Legacy | |
| InMemoryVerilogSourceFile(absl::string_view filename, | |
| absl::string_view contents, | |
| absl::string_view corpus = "") | |
| : InMemoryVerilogSourceFile( | |
| filename, std::make_shared<verible::StringMemBlock>(contents), | |
| corpus) {} | |
| // Load text into analyzer structure without actually opening a file. | |
| absl::Status Open() final; | |
| }; | |
| // Source file that was already parsed and got its own TextStructure. | |
| // Doesn't require file-system access, nor create temporary files. | |
| class ParsedVerilogSourceFile final : public VerilogSourceFile { | |
| public: | |
| // filename can be fake, it is not used to open any file. | |
| // text_structure is a pointer to a TextStructureView object of | |
| // already parsed file. Current implementation does _not_ make a | |
| // copy of it and expects it will be available for the lifetime of | |
| // object of this class. | |
| ParsedVerilogSourceFile(absl::string_view filename, | |
| const verible::TextStructureView* text_structure, | |
| absl::string_view corpus = "") | |
| : VerilogSourceFile(filename, filename, corpus), | |
| text_structure_(text_structure) {} | |
| // Do nothing (file contents already loaded) | |
| absl::Status Open() final; | |
| // Do nothing (contents already parsed) | |
| absl::Status Parse() final; | |
| // Return TextStructureView provided previously in constructor | |
| const verible::TextStructureView* GetTextStructure() const final; | |
| absl::string_view GetContent() const final { | |
| return text_structure_->Contents(); | |
| } | |
| private: | |
| const verible::TextStructureView* text_structure_; | |
| }; | |
| // VerilogProject represents a set of files as a cohesive unit of compilation. | |
| // Files can include top-level translation units and preprocessor included | |
| // files. This is responsible for owning string memory that corresponds | |
| // to files' contents. | |
| class VerilogProject { | |
| // Collection of per-file metadata and analyzer objects | |
| // key: referenced file name (as opposed to resolved filename) | |
| typedef std::map<std::string, std::unique_ptr<VerilogSourceFile>, | |
| VerilogSourceFile::Less> | |
| file_set_type; | |
| public: | |
| typedef file_set_type::iterator iterator; | |
| typedef file_set_type::const_iterator const_iterator; | |
| // Constructor. Note that `populate_string_maps` (populating internal string | |
| // view maps) fragments the class's usage. Enabling it prevents removing files | |
| // from the project (which is required for Kythe facts extraction). | |
| VerilogProject(absl::string_view root, | |
| const std::vector<std::string>& include_paths, | |
| absl::string_view corpus = "", | |
| bool populate_string_maps = true) | |
| : translation_unit_root_(root), | |
| include_paths_(include_paths), | |
| corpus_(corpus), | |
| populate_string_maps_(populate_string_maps) {} | |
| VerilogProject(const VerilogProject&) = delete; | |
| VerilogProject(VerilogProject&&) = delete; | |
| VerilogProject& operator=(const VerilogProject&) = delete; | |
| VerilogProject& operator=(VerilogProject&&) = delete; | |
| const_iterator begin() const { return files_.begin(); } | |
| iterator begin() { return files_.begin(); } | |
| const_iterator end() const { return files_.end(); } | |
| iterator end() { return files_.end(); } | |
| // Returns the directory to which translation units are referenced relatively. | |
| absl::string_view TranslationUnitRoot() const { | |
| return translation_unit_root_; | |
| } | |
| // Returns the corpus to which this project belongs to. | |
| absl::string_view Corpus() const { return corpus_; } | |
| // Opens a single top-level file, known as a "translation unit". | |
| // This uses translation_unit_root_ directory to calculate the file's path. | |
| // If the file was previously opened, that data is returned. | |
| absl::StatusOr<VerilogSourceFile*> OpenTranslationUnit( | |
| absl::string_view referenced_filename); | |
| // Opens a file that was `included. | |
| // If the file was previously opened, that data is returned. | |
| absl::StatusOr<VerilogSourceFile*> OpenIncludedFile( | |
| absl::string_view referenced_filename); | |
| // Adds an already opened file by directly passing its content. | |
| // TODO(refactor): is this needed ? | |
| void AddVirtualFile(absl::string_view resolved_filename, | |
| absl::string_view content); | |
| // Returns a previously referenced file, or else nullptr. | |
| VerilogSourceFile* LookupRegisteredFile( | |
| absl::string_view referenced_filename) { | |
| return LookupRegisteredFileInternal(referenced_filename); | |
| } | |
| // Removes the file from project and releases the resources. Returns true if | |
| // the file was removed. | |
| bool RemoveRegisteredFile(absl::string_view referenced_filename); | |
| // Non-modifying variant of lookup. | |
| const VerilogSourceFile* LookupRegisteredFile( | |
| absl::string_view referenced_filename) const { | |
| return LookupRegisteredFileInternal(referenced_filename); | |
| } | |
| // Find the source file that a particular string_view came from. | |
| // Returns nullptr if lookup failed for any reason. | |
| const VerilogSourceFile* LookupFileOrigin( | |
| absl::string_view content_substring) const; | |
| private: | |
| absl::StatusOr<VerilogSourceFile*> OpenFile( | |
| absl::string_view referenced_filename, | |
| absl::string_view resolved_filename, absl::string_view corpus); | |
| // Error status factory, when include file is not found. | |
| absl::Status IncludeFileNotFoundError( | |
| absl::string_view referenced_filename) const; | |
| // Returns a previously referenced file, or else nullptr. | |
| VerilogSourceFile* LookupRegisteredFileInternal( | |
| absl::string_view referenced_filename) const; | |
| // Returns the opened file or parse/not found error. If the file is not | |
| // opened, returns nullopt. | |
| absl::optional<absl::StatusOr<VerilogSourceFile*>> FindOpenedFile( | |
| absl::string_view filename) const; | |
| // The path from which top-level translation units are referenced relatively | |
| // (often from a file list). This path can be relative or absolute. | |
| // Default: the working directory of the invoking process. | |
| const std::string translation_unit_root_ = "."; | |
| // The sequence of directories from which to search for `included files. | |
| // These can be absolute, or relative to the process's working directory. | |
| const std::vector<std::string> include_paths_; | |
| // The corpus to which this project belongs (e.g., | |
| // 'github.com/chipsalliance/verible'). | |
| const std::string corpus_; | |
| // If true, opening a file will add its contents to string_view_map_ and | |
| // buffer_to_analyzer_map_. NOTE: string view maps don't support removal | |
| // operation. Setting this option prevents removing of the files from the | |
| // project (removing the files leads to undefined behavior). | |
| const bool populate_string_maps_; | |
| // Set of opened files, keyed by referenced (not resolved) filename. | |
| file_set_type files_; | |
| // Maps any string_view (substring) to its full source file text | |
| // (superstring). | |
| verible::StringViewSuperRangeMap string_view_map_; | |
| // Maps start of text buffer to its corresponding analyzer object. | |
| // key: the starting address of a string buffer belonging to an opened file. | |
| // This can come from the .begin() of any entry in string_view_map_. | |
| std::map<absl::string_view::const_iterator, file_set_type::const_iterator> | |
| buffer_to_analyzer_map_; | |
| }; | |
| } // namespace verilog | |
| #endif // VERIBLE_VERILOG_ANALYSIS_VERILOG_PROJECT_H_ |