blob: dfe7ab8906719f1d296987955d0982f72db2f207 [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.
// 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_