blob: f1961dff70a2a7c7cd3161fabb336f6209ad96fc [file] [log] [blame]
// 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_