| // 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. | |
| // Used for reporting the outcome of a LintRule. | |
| #ifndef VERIBLE_COMMON_ANALYSIS_LINT_RULE_STATUS_H_ | |
| #define VERIBLE_COMMON_ANALYSIS_LINT_RULE_STATUS_H_ | |
| #include <fstream> | |
| #include <functional> | |
| #include <iosfwd> | |
| #include <set> | |
| #include <string> | |
| #include <vector> | |
| #include "absl/strings/string_view.h" | |
| #include "common/analysis/citation.h" | |
| #include "common/strings/line_column_map.h" | |
| #include "common/text/symbol.h" | |
| #include "common/text/syntax_tree_context.h" | |
| #include "common/text/token_info.h" | |
| #include "common/util/logging.h" | |
| namespace verible { | |
| // Represents a single replace operation on a text fragment. | |
| // | |
| // Either fragment or replacement can be strings with zero width, providing a | |
| // way for, respectively, inserting and removing text. | |
| // | |
| // ReplacementEdit differs from editscript's Edit in that it stores a | |
| // replacement string, so it doesn't need the "after" text to be useful. | |
| struct ReplacementEdit { | |
| ReplacementEdit(absl::string_view fragment, const std::string& replacement) | |
| : fragment(fragment), replacement(replacement) {} | |
| ReplacementEdit(const TokenInfo& token, const std::string& replacement) | |
| : fragment(token.text()), replacement(replacement) {} | |
| bool operator<(const ReplacementEdit& other) const { | |
| // Check that the fragment is located before the other's fragment. When they | |
| // overlap, `this<other` and `other<this` return false, which makes them | |
| // equivalent in std::set. | |
| return (fragment.data() + fragment.size()) <= other.fragment.data(); | |
| } | |
| absl::string_view fragment; | |
| std::string replacement; | |
| }; | |
| // Collection of ReplacementEdits performing single violation fix. | |
| class AutoFix { | |
| public: | |
| AutoFix() {} | |
| AutoFix(const AutoFix& other) = default; | |
| AutoFix(AutoFix&& other) = default; | |
| AutoFix(absl::string_view description, | |
| std::initializer_list<ReplacementEdit> edits) | |
| : description_(description), edits_(edits) { | |
| CHECK_EQ(edits_.size(), edits.size()) << "Edits must not overlap."; | |
| } | |
| AutoFix(absl::string_view description, const ReplacementEdit& edit) | |
| : AutoFix(description, {edit}) {} | |
| // Applies the fix on a `base` and returns modified text. | |
| std::string Apply(absl::string_view base) const; | |
| bool AddEdits(const std::set<ReplacementEdit>& new_edits); | |
| const std::set<ReplacementEdit>& Edits() const { return edits_; } | |
| const std::string& Description() const { return description_; } | |
| private: | |
| std::string description_; | |
| std::set<ReplacementEdit> edits_; | |
| }; | |
| // LintViolation is a class that represents a single rule violation. | |
| struct LintViolation { | |
| // This construct records a token stream lint violation. | |
| LintViolation(const TokenInfo& token, const std::string& reason, | |
| const std::vector<AutoFix>& autofixes = {}) | |
| : root(nullptr), | |
| token(token), | |
| reason(reason), | |
| context(), | |
| autofixes(autofixes) {} | |
| // This construct records a syntax tree lint violation. | |
| // Use this variation when the violation can be localized to a single token. | |
| LintViolation(const TokenInfo& token, const std::string& reason, | |
| const SyntaxTreeContext& context, | |
| const std::vector<AutoFix>& autofixes = {}) | |
| : root(nullptr), | |
| token(token), | |
| reason(reason), | |
| context(context), | |
| autofixes(autofixes) {} | |
| // This construct records a syntax tree lint violation. | |
| // Use this variation when the range of violation is a subtree that spans | |
| // multiple tokens. The violation will be reported at the location of | |
| // the left-most leaf of the subtree. | |
| LintViolation(const Symbol& root, const std::string& reason, | |
| const SyntaxTreeContext& context, | |
| const std::vector<AutoFix>& autofixes = {}); | |
| // root is a reference into original ConcreteSyntaxTree that | |
| // linter was run against. LintViolations should not outlive this tree. | |
| // It should point to the root symbol that the linter failed on. | |
| const Symbol* root = nullptr; | |
| // The token at which the error occurs, which includes location information. | |
| const TokenInfo token; | |
| // The reason why the violation occurs. | |
| std::string reason; | |
| // The context (list of ancestors) of the offending token. | |
| // For non-syntax-tree analyses, leave this blank. | |
| const SyntaxTreeContext context; | |
| const std::vector<AutoFix> autofixes; | |
| bool operator<(const LintViolation& r) const { | |
| // compares addresses of violations, which correspond to substring | |
| // locations | |
| return token.text().data() < r.token.text().data(); | |
| } | |
| }; | |
| // LintRuleStatus represents the result of running a single lint rule. | |
| struct LintRuleStatus { | |
| LintRuleStatus() {} | |
| LintRuleStatus(const std::set<LintViolation>& vs, absl::string_view rule_name, | |
| const std::string& url) | |
| : lint_rule_name(rule_name), url(url), violations(vs) {} | |
| // TODO(hzeller): the LintRuleDescriptor is in verilog/analysis namespace, | |
| // don't want to move that to common in first step. So making this a | |
| // template for it to be a 'source code compatible' adaption. | |
| template <typename Descriptor> | |
| LintRuleStatus(const std::set<LintViolation>& vs, | |
| const Descriptor& descriptor) | |
| : lint_rule_name(descriptor.name), | |
| url(GetStyleGuideCitation(descriptor.topic)), | |
| violations(vs) {} | |
| explicit LintRuleStatus(const std::set<LintViolation>& vs) : violations(vs) {} | |
| bool isOk() const { return violations.empty(); } | |
| // Remove subset of violations that is waived from report. | |
| // If `is_waived`() is true, remove the finding from the set of violations. | |
| void WaiveViolations(std::function<bool(const LintViolation&)>&& is_waived); | |
| // Name of the lint rule that produced this status. | |
| absl::string_view lint_rule_name; | |
| // Hold link to engdoc summary of violated rule | |
| std::string url; | |
| // Contains all violations of the LintRule | |
| std::set<LintViolation> violations; | |
| }; | |
| struct LintViolationWithStatus { | |
| const LintViolation* violation; | |
| const LintRuleStatus* status; | |
| LintViolationWithStatus(const LintViolation* v, const LintRuleStatus* s) | |
| : violation(v), status(s) {} | |
| bool operator<(const LintViolationWithStatus& r) const { | |
| // compares addresses which correspond to locations within the same string | |
| return violation->token.text().data() < r.violation->token.text().data(); | |
| } | |
| }; | |
| // LintStatusFormatter is a class for printing LintRuleStatus's and | |
| // LintViolations to an output stream | |
| // Usage: | |
| // string filename = ... | |
| // string code_text = ... | |
| // LintRuleStatus status = ... | |
| // LintStatusFormatter formatter(code_text); | |
| // formatter.FormatLintRuleStatus(&std::cout, status, filename) | |
| class LintStatusFormatter { | |
| public: | |
| // Constructor takes a reference to the original text in order to setup | |
| // line_column_map | |
| explicit LintStatusFormatter(absl::string_view text) | |
| : line_column_map_(text) {} | |
| // Formats and outputs status to stream. | |
| // Path is the file path of original file. This is needed because it is not | |
| // contained in status. | |
| // Base is the string_view of the entire contents, used only for byte offset | |
| // calculation. | |
| void FormatLintRuleStatus(std::ostream* stream, const LintRuleStatus& status, | |
| absl::string_view base, | |
| absl::string_view path) const; | |
| // Formats, sorts and outputs status to stream with additional vulnerable code | |
| // line printed when enabled. | |
| // The violations contained in the statuses are sorted by their occurrence | |
| // in the code and are not grouped by the status object. | |
| // Path is the file path of original file. This is needed because it is not | |
| // contained in status. | |
| // Base is the string_view of the entire contents, used only for byte offset | |
| // calculation. | |
| void FormatLintRuleStatuses( | |
| std::ostream* stream, const std::vector<LintRuleStatus>& statuses, | |
| absl::string_view base, absl::string_view path, | |
| const std::vector<absl::string_view>& lines) const; | |
| // Formats and outputs violation on stream. | |
| // Path is file path of original file and url is a link to the ratified rule | |
| // that is being violated. | |
| // Base is the string_view of the entire contents, used only for byte offset | |
| // calculation. | |
| void FormatViolation(std::ostream* stream, const LintViolation& violation, | |
| absl::string_view base, absl::string_view path, | |
| absl::string_view url, | |
| absl::string_view rule_name) const; | |
| // Formats and outputs violation to a file stream in a syntax accepted by | |
| // --waiver_files flag. Path is file path of original file that is being | |
| // violated. Base is the string_view of the entire contents, used only for | |
| // byte offset calculation. | |
| void FormatViolationWaiver(std::ostream* stream, | |
| const LintViolation& violation, | |
| absl::string_view base, absl::string_view path, | |
| absl::string_view rule_name) const; | |
| private: | |
| // Translates byte offsets, which are supplied by LintViolations via | |
| // locations field, to line:column | |
| LineColumnMap line_column_map_; | |
| }; | |
| } // namespace verible | |
| #endif // VERIBLE_COMMON_ANALYSIS_LINT_RULE_STATUS_H_ |