Skip to content

Commit b31be6e

Browse files
committed
encapsulate user inputs to post_feedback()
also encapsulate `TidyNotification`s in a `TidyAdvice` struct
1 parent a298d6c commit b31be6e

File tree

5 files changed

+94
-69
lines changed

5 files changed

+94
-69
lines changed

cpp-linter-lib/src/clang_tools/clang_tidy.rs

+24-4
Original file line numberDiff line numberDiff line change
@@ -68,14 +68,34 @@ pub struct TidyNotification {
6868
pub suggestion: Vec<String>,
6969
}
7070

71+
impl TidyNotification {
72+
pub fn diagnostic_link(&self) -> String {
73+
let ret_val = if let Some((category, name)) = self.diagnostic.split_once('-') {
74+
format!(
75+
"[{}](https://clang.llvm.org/extra/clang-tidy/checks/{category}/{name}).html",
76+
self.diagnostic
77+
)
78+
} else {
79+
self.diagnostic.clone()
80+
};
81+
ret_val
82+
}
83+
}
84+
85+
/// A struct to hold notification from clang-tidy about a single file
86+
pub struct TidyAdvice {
87+
/// A list of notifications parsed from clang-tidy stdout.
88+
pub notes: Vec<TidyNotification>,
89+
}
90+
7191
/// Parses clang-tidy stdout.
7292
///
7393
/// Here it helps to have the JSON database deserialized for normalizing paths present
7494
/// in the notifications.
7595
fn parse_tidy_output(
7696
tidy_stdout: &[u8],
7797
database_json: &Option<CompilationDatabase>,
78-
) -> Vec<TidyNotification> {
98+
) -> TidyAdvice {
7999
let note_header = Regex::new(r"^(.+):(\d+):(\d+):\s(\w+):(.*)\[([a-zA-Z\d\-\.]+)\]$").unwrap();
80100
let mut notification = None;
81101
let mut result = Vec::new();
@@ -140,7 +160,7 @@ fn parse_tidy_output(
140160
if let Some(note) = notification {
141161
result.push(note);
142162
}
143-
result
163+
TidyAdvice { notes: result }
144164
}
145165

146166
/// Run clang-tidy, then parse and return it's output.
@@ -152,7 +172,7 @@ pub fn run_clang_tidy(
152172
database: &Option<PathBuf>,
153173
extra_args: &Option<Vec<&str>>,
154174
database_json: &Option<CompilationDatabase>,
155-
) -> Vec<TidyNotification> {
175+
) -> TidyAdvice {
156176
if !checks.is_empty() {
157177
cmd.args(["-checks", checks]);
158178
}
@@ -263,6 +283,6 @@ mod test {
263283
vec!["--extra-arg", "\"-std=c++17\"", "--extra-arg", "\"-Wall\""],
264284
args
265285
);
266-
assert!(!tidy_advice.is_empty());
286+
assert!(!tidy_advice.notes.is_empty());
267287
}
268288
}

cpp-linter-lib/src/clang_tools/mod.rs

+5-6
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use crate::logger::{end_log_group, start_log_group};
1414
pub mod clang_format;
1515
use clang_format::{run_clang_format, FormatAdvice};
1616
pub mod clang_tidy;
17-
use clang_tidy::{run_clang_tidy, CompilationDatabase, TidyNotification};
17+
use clang_tidy::{run_clang_tidy, CompilationDatabase, TidyAdvice};
1818

1919
/// Fetch the path to a clang tool by `name` (ie `"clang-tidy"` or `"clang-format"`) and
2020
/// `version`.
@@ -69,7 +69,7 @@ pub fn get_clang_tool_exe(name: &str, version: &str) -> Result<PathBuf, &'static
6969
/// Runs clang-tidy and/or clang-format and returns the parsed output from each.
7070
///
7171
/// The returned list of [`FormatAdvice`] is parallel to the `files` list passed in
72-
/// here. The returned 2D list of [`TidyNotification`] is also parallel on the first
72+
/// here. The returned 2D list of [`TidyAdvice`] is also parallel on the first
7373
/// dimension. The second dimension is a list of notes specific to a translation unit
7474
/// (each element of `files`).
7575
///
@@ -83,7 +83,7 @@ pub fn capture_clang_tools_output(
8383
lines_changed_only: u8,
8484
database: Option<PathBuf>,
8585
extra_args: Option<Vec<&str>>,
86-
) -> (Vec<FormatAdvice>, Vec<Vec<TidyNotification>>) {
86+
) -> (Vec<FormatAdvice>, Vec<TidyAdvice>) {
8787
// find the executable paths for clang-tidy and/or clang-format and show version
8888
// info as debugging output.
8989
let clang_tidy_command = if tidy_checks != "-*" {
@@ -126,9 +126,8 @@ pub fn capture_clang_tools_output(
126126
};
127127

128128
// iterate over the discovered files and run the clang tools
129-
let mut all_format_advice: Vec<clang_format::FormatAdvice> = Vec::with_capacity(files.len());
130-
let mut all_tidy_advice: Vec<Vec<clang_tidy::TidyNotification>> =
131-
Vec::with_capacity(files.len());
129+
let mut all_format_advice: Vec<FormatAdvice> = Vec::with_capacity(files.len());
130+
let mut all_tidy_advice: Vec<TidyAdvice> = Vec::with_capacity(files.len());
132131
for file in files {
133132
start_log_group(format!("Analyzing {}", file.name.to_string_lossy()));
134133
if let Some(tidy_cmd) = &clang_tidy_command {

cpp-linter-lib/src/rest_api/github_api.rs

+20-30
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,11 @@ use serde::Deserialize;
1313
use serde_json;
1414

1515
// project specific modules/crates
16-
use crate::clang_tools::{clang_format::FormatAdvice, clang_tidy::TidyNotification};
16+
use crate::clang_tools::{clang_format::FormatAdvice, clang_tidy::TidyAdvice};
1717
use crate::common_fs::FileObj;
1818
use crate::git::{get_diff, open_repo, parse_diff, parse_diff_from_buf};
1919

20-
use super::RestApiClient;
20+
use super::{FeedbackInput, RestApiClient, COMMENT_MARKER};
2121

2222
static USER_AGENT: &str =
2323
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:120.0) Gecko/20100101 Firefox/120.0";
@@ -186,16 +186,12 @@ impl RestApiClient for GithubApiClient {
186186
&self,
187187
files: &[FileObj],
188188
format_advice: &[FormatAdvice],
189-
tidy_advice: &[Vec<TidyNotification>],
190-
thread_comments: &str,
191-
no_lgtm: bool,
192-
step_summary: bool,
193-
file_annotations: bool,
194-
style: &str,
189+
tidy_advice: &[TidyAdvice],
190+
user_inputs: FeedbackInput,
195191
) {
196192
let (comment, format_checks_failed, tidy_checks_failed) =
197193
self.make_comment(files, format_advice, tidy_advice);
198-
if thread_comments != "false" {
194+
if user_inputs.thread_comments.as_str() != "false" {
199195
// post thread comment for PR or push event
200196
if let Some(repo) = &self.repo {
201197
let is_pr = self.event_name == "pull_request";
@@ -222,15 +218,13 @@ impl RestApiClient for GithubApiClient {
222218
} else {
223219
json["commit"]["comment_count"].as_u64().unwrap()
224220
};
225-
let user_id: u64 = 41898282;
226221
self.update_comment(
227222
&format!("{}/comments", &comments_url),
228223
&comment,
229224
count,
230-
user_id,
231-
no_lgtm,
225+
user_inputs.no_lgtm,
232226
format_checks_failed + tidy_checks_failed == 0,
233-
thread_comments == "update",
227+
user_inputs.thread_comments.as_str() == "update",
234228
);
235229
} else {
236230
let error = request.unwrap_err();
@@ -245,10 +239,15 @@ impl RestApiClient for GithubApiClient {
245239
}
246240
}
247241
}
248-
if file_annotations {
249-
self.post_annotations(files, format_advice, tidy_advice, style);
242+
if user_inputs.file_annotations {
243+
self.post_annotations(
244+
files,
245+
format_advice,
246+
tidy_advice,
247+
user_inputs.style.as_str(),
248+
);
250249
}
251-
if step_summary {
250+
if user_inputs.step_summary {
252251
self.post_step_summary(&comment);
253252
}
254253
self.set_exit_code(
@@ -276,7 +275,7 @@ impl GithubApiClient {
276275
&self,
277276
files: &[FileObj],
278277
format_advice: &[FormatAdvice],
279-
tidy_advice: &[Vec<TidyNotification>],
278+
tidy_advice: &[TidyAdvice],
280279
style: &str,
281280
) {
282281
if !format_advice.is_empty() {
@@ -321,7 +320,7 @@ impl GithubApiClient {
321320
// The tidy_advice vector is parallel to the files vector; meaning it serves as a file filterer.
322321
// lines are already filter as specified to clang-tidy CLI.
323322
for (index, advice) in tidy_advice.iter().enumerate() {
324-
for note in advice {
323+
for note in &advice.notes {
325324
if note.filename == files[index].name.to_string_lossy().replace('\\', "/") {
326325
println!(
327326
"::{severity} file={file},line={line},title={file}:{line}:{cols} [{diag}]::{info}",
@@ -344,13 +343,12 @@ impl GithubApiClient {
344343
url: &String,
345344
comment: &String,
346345
count: u64,
347-
user_id: u64,
348346
no_lgtm: bool,
349347
is_lgtm: bool,
350348
update_only: bool,
351349
) {
352350
let comment_url =
353-
self.remove_bot_comments(url, user_id, count, !update_only || (is_lgtm && no_lgtm));
351+
self.remove_bot_comments(url, count, !update_only || (is_lgtm && no_lgtm));
354352
#[allow(clippy::nonminimal_bool)] // an inaccurate assessment
355353
if (is_lgtm && !no_lgtm) || !is_lgtm {
356354
let payload = HashMap::from([("body", comment)]);
@@ -383,13 +381,7 @@ impl GithubApiClient {
383381
}
384382
}
385383

386-
fn remove_bot_comments(
387-
&self,
388-
url: &String,
389-
count: u64,
390-
user_id: u64,
391-
delete: bool,
392-
) -> Option<String> {
384+
fn remove_bot_comments(&self, url: &String, count: u64, delete: bool) -> Option<String> {
393385
let mut page = 1;
394386
let mut comment_url = None;
395387
let mut total = count;
@@ -402,9 +394,7 @@ impl GithubApiClient {
402394
let payload: JsonCommentsPayload = response.json().unwrap();
403395
let mut comment_count = 0;
404396
for comment in payload.comments {
405-
if comment.body.starts_with("<!-- cpp linter action -->")
406-
&& comment.user.id == user_id
407-
{
397+
if comment.body.starts_with(COMMENT_MARKER) {
408398
log::debug!(
409399
"comment id {} from user {} ({})",
410400
comment.id,

cpp-linter-lib/src/rest_api/mod.rs

+31-12
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,33 @@ use reqwest::header::{HeaderMap, HeaderValue};
1010

1111
// project specific modules/crates
1212
pub mod github_api;
13-
use crate::clang_tools::{clang_format::FormatAdvice, clang_tidy::TidyNotification};
13+
use crate::clang_tools::{clang_format::FormatAdvice, clang_tidy::TidyAdvice};
1414
use crate::common_fs::FileObj;
1515

16+
pub static COMMENT_MARKER: &str = "<!-- cpp linter action -->";
17+
18+
/// A struct to hold a collection of user inputs related to [`ResApiClient.post_feedback()`].
19+
pub struct FeedbackInput {
20+
pub thread_comments: String,
21+
pub no_lgtm: bool,
22+
pub step_summary: bool,
23+
pub file_annotations: bool,
24+
pub style: String,
25+
}
26+
27+
impl Default for FeedbackInput {
28+
/// Construct a [`UserInput`] instance with default values.
29+
fn default() -> Self {
30+
FeedbackInput {
31+
thread_comments: "false".to_string(),
32+
no_lgtm: true,
33+
step_summary: false,
34+
file_annotations: true,
35+
style: "llvm".to_string(),
36+
}
37+
}
38+
}
39+
1640
/// A custom trait that templates necessary functionality with a Git server's REST API.
1741
pub trait RestApiClient {
1842
/// A way to set output variables specific to cpp_linter executions in CI.
@@ -53,7 +77,7 @@ pub trait RestApiClient {
5377
&self,
5478
files: &[FileObj],
5579
format_advice: &[FormatAdvice],
56-
tidy_advice: &[Vec<TidyNotification>],
80+
tidy_advice: &[TidyAdvice],
5781
) -> (String, i32, i32) {
5882
let mut comment = String::from("<!-- cpp linter action -->\n# Cpp-Linter Report ");
5983
let mut format_checks_failed = 0;
@@ -73,8 +97,8 @@ pub trait RestApiClient {
7397
}
7498

7599
let mut tidy_comment = String::new();
76-
for (index, tidy_notes) in tidy_advice.iter().enumerate() {
77-
for tidy_note in tidy_notes {
100+
for (index, advice) in tidy_advice.iter().enumerate() {
101+
for tidy_note in &advice.notes {
78102
let file_path = PathBuf::from(&tidy_note.filename);
79103
if file_path == files[index].name {
80104
tidy_comment.push_str(&format!("- {}\n\n", tidy_note.filename));
@@ -84,7 +108,7 @@ pub trait RestApiClient {
84108
line = tidy_note.line,
85109
cols = tidy_note.cols,
86110
severity = tidy_note.severity,
87-
diagnostic = tidy_note.diagnostic,
111+
diagnostic = tidy_note.diagnostic_link(),
88112
rationale = tidy_note.rationale,
89113
concerned_code = if tidy_note.suggestion.is_empty() {String::from("")} else {
90114
format!("\n ```{ext}\n {suggestion}\n ```\n",
@@ -122,16 +146,11 @@ pub trait RestApiClient {
122146
/// clang-format and clang-tidy (see `capture_clang_tools_output()`).
123147
///
124148
/// All other parameters correspond to CLI arguments.
125-
#[allow(clippy::too_many_arguments)]
126149
fn post_feedback(
127150
&self,
128151
files: &[FileObj],
129152
format_advice: &[FormatAdvice],
130-
tidy_advice: &[Vec<TidyNotification>],
131-
thread_comments: &str,
132-
no_lgtm: bool,
133-
step_summary: bool,
134-
file_annotations: bool,
135-
style: &str,
153+
tidy_advice: &[TidyAdvice],
154+
user_inputs: FeedbackInput,
136155
);
137156
}

cpp-linter-lib/src/run.rs

+14-17
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use crate::cli::{convert_extra_arg_val, get_arg_parser, parse_ignore};
1717
use crate::common_fs::{list_source_files, FileObj};
1818
use crate::github_api::GithubApiClient;
1919
use crate::logger::{self, end_log_group, start_log_group};
20-
use crate::rest_api::RestApiClient;
20+
use crate::rest_api::{FeedbackInput, RestApiClient};
2121

2222
#[cfg(feature = "openssl-vendored")]
2323
fn probe_ssl_certs() {
@@ -111,32 +111,29 @@ pub fn run_main(args: Vec<String>) -> i32 {
111111
}
112112
end_log_group();
113113

114-
let style = args.get_one::<String>("style").unwrap();
114+
let user_inputs = FeedbackInput {
115+
style: args.get_one::<String>("style").unwrap().to_string(),
116+
no_lgtm: args.get_flag("no-lgtm"),
117+
step_summary: args.get_flag("step-summary"),
118+
thread_comments: args
119+
.get_one::<String>("thread-comments")
120+
.unwrap()
121+
.to_string(),
122+
file_annotations: args.get_flag("file-annotations"),
123+
};
124+
115125
let extra_args = convert_extra_arg_val(&args);
116126
let (format_advice, tidy_advice) = capture_clang_tools_output(
117127
&files,
118128
args.get_one::<String>("version").unwrap(),
119129
args.get_one::<String>("tidy-checks").unwrap(),
120-
style,
130+
user_inputs.style.as_str(),
121131
lines_changed_only,
122132
database_path,
123133
extra_args,
124134
);
125135
start_log_group(String::from("Posting feedback"));
126-
let no_lgtm = args.get_flag("no-lgtm");
127-
let step_summary = args.get_flag("step-summary");
128-
let thread_comments = args.get_one::<String>("thread-comments").unwrap();
129-
let file_annotations = args.get_flag("file-annotations");
130-
rest_api_client.post_feedback(
131-
&files,
132-
&format_advice,
133-
&tidy_advice,
134-
thread_comments,
135-
no_lgtm,
136-
step_summary,
137-
file_annotations,
138-
style,
139-
);
136+
rest_api_client.post_feedback(&files, &format_advice, &tidy_advice, user_inputs);
140137
end_log_group();
141138
0
142139
}

0 commit comments

Comments
 (0)