Skip to content

Commit

Permalink
Merge pull request chipsalliance#1080 from antmicro/wsip/rdformat-ref…
Browse files Browse the repository at this point in the history
…actor

move ViolationHandler to common
  • Loading branch information
hzeller authored Nov 9, 2021
2 parents 832b11c + 1289f4f commit 83b86f7
Show file tree
Hide file tree
Showing 12 changed files with 462 additions and 399 deletions.
15 changes: 15 additions & 0 deletions common/analysis/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ package(
"//verilog/CST:__subpackages__",
"//verilog/analysis:__subpackages__",
"//verilog/tools/kythe:__pkg__",
"//verilog/tools/lint:__subpackages__",
],
)

Expand Down Expand Up @@ -84,6 +85,20 @@ cc_library(
],
)

cc_library(
name = "violation_handler",
srcs = ["violation_handler.cc"],
hdrs = ["violation_handler.h"],
deps = [
":lint_rule_status",
"//common/strings:diff",
"//common/util:file_util",
"//common/util:user_interaction",
"@com_google_absl//absl/status",
"@com_google_absl//absl/strings",
]
)

cc_test(
name = "command_file_lexer_test",
size = "small",
Expand Down
13 changes: 0 additions & 13 deletions common/analysis/lint_rule_status.cc
Original file line number Diff line number Diff line change
Expand Up @@ -92,19 +92,6 @@ void LintStatusFormatter::FormatLintRuleStatus(std::ostream* stream,
}
}

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();
}
};

void LintStatusFormatter::FormatLintRuleStatuses(
std::ostream* stream, const std::vector<LintRuleStatus>& statuses,
absl::string_view base, absl::string_view path,
Expand Down
13 changes: 13 additions & 0 deletions common/analysis/lint_rule_status.h
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,19 @@ struct LintRuleStatus {
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:
Expand Down
248 changes: 248 additions & 0 deletions common/analysis/violation_handler.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,248 @@
// Copyright 2017-2021 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.

#include "common/analysis/violation_handler.h"

#include "absl/status/status.h"
#include "common/strings/diff.h"
#include "common/util/file_util.h"
#include "common/util/user_interaction.h"

namespace verible {
namespace {

void PrintFix(std::ostream& stream, absl::string_view text,
const verible::AutoFix& fix) {
std::string after = fix.Apply(text);
verible::LineDiffs diff(text, after);

verible::LineDiffsToUnifiedDiff(stream, diff, 1);
}

void PrintFixAlternatives(std::ostream& stream, absl::string_view text,
const std::vector<verible::AutoFix>& fixes) {
const bool print_alternative_number = fixes.size() > 1;
for (size_t i = 0; i < fixes.size(); ++i) {
if (print_alternative_number) {
stream << verible::term::inverse(absl::StrCat(
"[ ", (i + 1), ". Alternative ", fixes[i].Description(), " ]\n"));
} else {
stream << verible::term::inverse(
absl::StrCat("[ ", fixes[i].Description(), " ]\n"));
}
PrintFix(stream, text, fixes[i]);
}
}

} // namespace

void ViolationPrinter::HandleViolations(
const std::set<LintViolationWithStatus>& violations, absl::string_view base,
absl::string_view path) {
verible::LintStatusFormatter formatter(base);
for (auto violation : violations) {
formatter.FormatViolation(stream_, *violation.violation, base, path,
violation.status->url,
violation.status->lint_rule_name);
(*stream_) << std::endl;
}
}

void ViolationFixer::CommitFixes(absl::string_view source_content,
absl::string_view source_path,
const verible::AutoFix& fix) const {
if (fix.Edits().empty()) {
return;
}
std::string fixed_content = fix.Apply(source_content);

if (patch_stream_) {
verible::LineDiffs diff(source_content, fixed_content);
verible::LineDiffsToUnifiedDiff(*patch_stream_, diff, 1, source_path);
} else {
const absl::Status write_status =
verible::file::SetContents(source_path, fixed_content);
if (!write_status.ok()) {
LOG(ERROR) << "Failed to write fixes to file '" << source_path
<< "': " << write_status.ToString();
return;
}
}
}

void ViolationFixer::HandleViolations(
const std::set<LintViolationWithStatus>& violations, absl::string_view base,
absl::string_view path) {
verible::AutoFix fix;
verible::LintStatusFormatter formatter(base);
for (auto violation : violations) {
HandleViolation(*violation.violation, base, path, violation.status->url,
violation.status->lint_rule_name, formatter, &fix);
}

CommitFixes(base, path, fix);
}

void ViolationFixer::HandleViolation(
const verible::LintViolation& violation, absl::string_view base,
absl::string_view path, absl::string_view url, absl::string_view rule_name,
const verible::LintStatusFormatter& formatter, verible::AutoFix* fix) {
std::stringstream violation_message;
formatter.FormatViolation(&violation_message, violation, base, path, url,
rule_name);
(*message_stream_) << violation_message.str() << std::endl;

if (violation.autofixes.empty()) {
return;
}

static std::string_view previous_fix_conflict =
"The fix conflicts with "
"previously applied fixes, rejecting.\n";

Answer answer;
for (bool first_round = true; /**/; first_round = false) {
if (ultimate_answer_.choice != AnswerChoice::kUnknown) {
answer = ultimate_answer_;
} else if (auto found = rule_answers_.find(rule_name);
found != rule_answers_.end()) {
answer = found->second;
// If the ApplyAll specifies alternative not available here, use first.
if (answer.alternative >= violation.autofixes.size()) {
answer.alternative = 0;
}
} else {
if (is_interactive_ && first_round) { // Show the user what is available.
PrintFixAlternatives(*message_stream_, base, violation.autofixes);
}
answer = answer_chooser_(violation, rule_name);
}

switch (answer.choice) {
case AnswerChoice::kApplyAll:
ultimate_answer_ = {AnswerChoice::kApply};
[[fallthrough]];
case AnswerChoice::kApplyAllForRule:
rule_answers_[rule_name] = {AnswerChoice::kApply, answer.alternative};
[[fallthrough]];
case AnswerChoice::kApply: // Apply fix chosen in the alternatative
if (answer.alternative >= violation.autofixes.size())
continue; // ask again.
if (!fix->AddEdits(violation.autofixes[answer.alternative].Edits())) {
*message_stream_ << previous_fix_conflict;
}
break;
case AnswerChoice::kRejectAll:
ultimate_answer_ = {AnswerChoice::kReject};
[[fallthrough]];
case AnswerChoice::kRejectAllForRule:
rule_answers_[rule_name] = {AnswerChoice::kReject};
[[fallthrough]];
case AnswerChoice::kReject:
return;

case AnswerChoice::kPrintFix:
PrintFixAlternatives(*message_stream_, base, violation.autofixes);
continue;
case AnswerChoice::kPrintAppliedFixes:
PrintFix(*message_stream_, base, *fix);
continue;

default:
continue;
}

break;
}
}

ViolationFixer::Answer ViolationFixer::InteractiveAnswerChooser(
const verible::LintViolation& violation, absl::string_view rule_name) {
static absl::string_view fixed_help_message =
"n - reject fix\n"
"a - apply this and all remaining fixes for violations of this rule\n"
"d - reject this and all remaining fixes for violations of this rule\n"
"A - apply this and all remaining fixes\n"
"D - reject this and all remaining fixes\n"
"p - show fix\n"
"P - show fixes applied in this file so far\n"
"? - print this help and prompt again\n";

const size_t fix_count = violation.autofixes.size();
std::string help_message;
std::string alternative_list; // Show alternatives in the short-menu.
if (fix_count > 1) {
help_message =
absl::StrCat("y - apply first fix\n[1-", fix_count,
"] - apply given alternative\n", fixed_help_message);
for (size_t i = 0; i < fix_count; ++i)
absl::StrAppend(&alternative_list, (i + 1), ",");
} else {
help_message = absl::StrCat("y - apply fix\n", fixed_help_message);
}

for (;;) {
const char c = verible::ReadCharFromUser(
std::cin, std::cerr, verible::IsInteractiveTerminalSession(),
verible::term::bold("Autofix is available. Apply? [" +
alternative_list + "y,n,a,d,A,D,p,P,?] "));

// Single character digit chooses the available alternative.
if (c >= '1' && c <= '9' &&
c < static_cast<char>('1' + violation.autofixes.size())) {
return {AnswerChoice::kApply, static_cast<size_t>(c - '1')};
}

switch (c) {
case 'y':
return {AnswerChoice::kApply, 0};

// TODO(hzeller): Should we provide a way to choose 'all for rule'
// including an alternative ? Maybe with a two-letter response
// such as 1a, 2a, 3a ? Current assumption of interaction is
// single character.
case 'a':
return {AnswerChoice::kApplyAllForRule};
case 'A':
return {AnswerChoice::kApplyAll}; // No alternatives
case 'n':
return {AnswerChoice::kReject};
case 'd':
return {AnswerChoice::kRejectAllForRule};
case 'D':
return {AnswerChoice::kRejectAll};

case '\0':
// EOF: received when too few "answers" have been piped to stdin.
std::cerr << "Received EOF while there are questions left. "
<< "Rejecting all remaining fixes." << std::endl;
return {AnswerChoice::kRejectAll};

case 'p':
return {AnswerChoice::kPrintFix};
case 'P':
return {AnswerChoice::kPrintAppliedFixes};

case '\n':
continue;

case '?':
default:
std::cerr << help_message << std::endl;
continue;
}
}
}

} // namespace verible
Loading

0 comments on commit 83b86f7

Please sign in to comment.