Skip to content

Commit

Permalink
Introduce elp explain, show links to Erlang Error Index
Browse files Browse the repository at this point in the history
Summary:
Introduce a `elp explain` command which, given a diagnostic code, returns a link to the respective entry in the Erlang Error Index.

Also enable links to the index from diagnostics.

Later on we will probably want to show the actual content instead of just the link.

Reviewed By: alanz

Differential Revision: D49230473

fbshipit-source-id: 63b4d10eae3ed9f8ba31a52718db60be0e2be1e5
  • Loading branch information
robertoaloi authored and facebook-github-bot committed Sep 15, 2023
1 parent 29794ce commit 0731696
Show file tree
Hide file tree
Showing 8 changed files with 113 additions and 22 deletions.
15 changes: 15 additions & 0 deletions crates/elp/src/bin/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,13 @@ pub struct Lint {
pub ignore_apps: Vec<String>,
}

#[derive(Clone, Debug, Bpaf)]
pub struct Explain {
/// Error code to explain
#[bpaf(argument("CODE"))]
pub code: String,
}

#[derive(Clone, Debug, Bpaf)]
pub struct Shell {
/// Path to directory with project (defaults to `.`)
Expand All @@ -269,6 +276,7 @@ pub enum Command {
Lint(Lint),
Version(Version),
Shell(Shell),
Explain(Explain),
Help(),
}

Expand Down Expand Up @@ -366,6 +374,12 @@ pub fn command() -> impl Parser<Command> {
.command("shell")
.help("Starts an interactive ELP shell");

let explain = explain()
.map(Command::Explain)
.to_options()
.command("explain")
.help("Explain a diagnostic code");

construct!([
eqwalize,
eqwalize_all,
Expand All @@ -381,6 +395,7 @@ pub fn command() -> impl Parser<Command> {
version,
shell,
eqwalize_stats,
explain,
])
.fallback(Help())
}
Expand Down
24 changes: 24 additions & 0 deletions crates/elp/src/bin/explain_cli.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under both the MIT license found in the
* LICENSE-MIT file in the root directory of this source tree and the Apache
* License, Version 2.0 found in the LICENSE-APACHE file in the root directory
* of this source tree.
*/

use anyhow::Result;
use elp::cli::Cli;
use elp_ide::diagnostics::DiagnosticCode;

use crate::args::Explain;

pub fn explain(args: &Explain, cli: &mut dyn Cli) -> Result<()> {
if let Some(code) = DiagnosticCode::maybe_from_string(&args.code) {
if let Some(uri) = DiagnosticCode::as_uri(&code) {
let label = code.as_label();
return Ok(writeln!(cli, "{uri} ({label})")?);
}
}
Ok(writeln!(cli, "Unkwnown code: {}", args.code)?)
}
33 changes: 33 additions & 0 deletions crates/elp/src/bin/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ mod build_info_cli;
mod elp_parse_cli;
mod eqwalizer_cli;
mod erlang_service_cli;
mod explain_cli;
mod lint_cli;
mod reporting;
mod shell;
Expand Down Expand Up @@ -86,6 +87,7 @@ fn try_main(cli: &mut dyn Cli, args: Args) -> Result<()> {
let help = batteries::get_usage(args::args());
writeln!(cli, "{}", help)?
}
args::Command::Explain(args) => explain_cli::explain(&args, cli)?,
}

log::logger().flush();
Expand Down Expand Up @@ -140,6 +142,7 @@ mod tests {

use bpaf::Args;
use elp::cli::Fake;
use elp_ide::diagnostics::BASE_URL;
use expect_test::expect;
use expect_test::expect_file;
use expect_test::Expect;
Expand Down Expand Up @@ -855,6 +858,36 @@ mod tests {
expected.assert_eq(&stdout);
}

#[test]
fn explain_help() {
let args = args::args()
.run_inner(Args::from(&["explain", "--help"]))
.unwrap_err();
let expected = expect_file!["../resources/test/explain_help.stdout"];
let stdout = args.unwrap_stdout();
expected.assert_eq(&stdout);
}

#[test]
fn explain_code() {
let args = args_vec!["explain", "--code", "W0005"];
let (stdout, stderr, code) = elp(args);
let expected = expect_file!["../resources/test/explain_code.stdout"];
expected.assert_eq(&stdout.strip_prefix(BASE_URL).unwrap());
assert!(stderr.is_empty());
assert_eq!(code, 0);
}

#[test]
fn explain_unknown_code() {
let args = args_vec!["explain", "--code", "does_not_exist"];
let (stdout, stderr, code) = elp(args);
let expected = expect_file!["../resources/test/explain_unkwnown_code.stdout"];
expected.assert_eq(&stdout);
assert!(stderr.is_empty());
assert_eq!(code, 0);
}

fn simple_snapshot(
args: Vec<OsString>,
project: &str,
Expand Down
1 change: 1 addition & 0 deletions crates/elp/src/resources/test/explain_code.stdout
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/erlang-error-index/w/W0005 (mutable_variable_bug)
5 changes: 5 additions & 0 deletions crates/elp/src/resources/test/explain_help.stdout
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Usage: --code CODE

Available options:
--code <CODE> Error code to explain
-h, --help Prints help information
1 change: 1 addition & 0 deletions crates/elp/src/resources/test/explain_unkwnown_code.stdout
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Unkwnown code: does_not_exist
1 change: 1 addition & 0 deletions crates/elp/src/resources/test/help.stdout
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@ Available commands:
version Print version
shell Starts an interactive ELP shell
eqwalize-stats Return statistics about code quality for eqWAlizer
explain Explain a diagnostic code
55 changes: 33 additions & 22 deletions crates/ide/src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,9 @@ pub struct Diagnostic {
pub uri: Option<String>,
}

// @fb-only: pub const BASE_URL: &str = "https://www.internalfb.com/intern/staticdocs/elp/docs";
// @oss-only pub const BASE_URL: &str = "https://whatsapp.github.io/erlang-language-platform/docs";

impl Diagnostic {
pub(crate) fn new(
code: DiagnosticCode,
Expand All @@ -105,14 +108,14 @@ impl Diagnostic {
) -> Diagnostic {
let message = message.into();
Diagnostic {
code,
code: code.clone(),
message,
range,
severity: Severity::Error,
categories: HashSet::new(),
fixes: None,
related_info: None,
uri: None,
uri: code.as_uri(),
}
}

Expand Down Expand Up @@ -406,6 +409,30 @@ impl DiagnosticCode {
}
}

pub fn namespace(code: &String) -> Option<String> {
let first = code.to_string().chars().next()?;
Some(first.to_lowercase().to_string())
}

pub fn as_namespace(&self) -> Option<String> {
match self {
DiagnosticCode::DefaultCodeForEnumIter => None,
DiagnosticCode::AdHoc(_) => None,
// @fb-only: DiagnosticCode::MetaOnly(_) => None,
DiagnosticCode::ErlangService(code) => Self::namespace(code),
_ => Self::namespace(&self.as_code()),
}
}

pub fn as_uri(&self) -> Option<String> {
let namespace = self.as_namespace()?;
let code = self.as_code();
Some(format!(
"{}/erlang-error-index/{namespace}/{code}",
BASE_URL.to_string()
))
}

/// Check if the diagnostic label is for an AdHoc one.
fn is_adhoc(s: &str) -> Option<String> {
// Looking for something like "ad-hoc: ad-hoc-title-1"
Expand Down Expand Up @@ -707,16 +734,9 @@ fn no_module_definition_diagnostic(
parse: &Parse<ast::SourceFile>,
) {
let mut report = |range| {
diagnostics.push(Diagnostic {
message: "no module definition".to_string(),
range,
severity: Severity::Error,
categories: HashSet::new(),
fixes: None,
related_info: None,
code: DiagnosticCode::MissingModule,
uri: None,
});
let diagnostic =
Diagnostic::new(DiagnosticCode::MissingModule, "no module definition", range);
diagnostics.push(diagnostic);
};
for form in parse.tree().forms() {
match form {
Expand Down Expand Up @@ -883,16 +903,7 @@ fn non_whitespace_prev_token(node: &SyntaxNode) -> Option<NodeOrToken> {

fn make_missing_diagnostic(range: TextRange, item: &'static str, code: String) -> Diagnostic {
let message = format!("Missing '{}'", item);
Diagnostic {
message,
range,
severity: Severity::Warning,
categories: HashSet::new(),
fixes: None,
related_info: None,
code: DiagnosticCode::Missing(code),
uri: None,
}
Diagnostic::new(DiagnosticCode::Missing(code), message, range).severity(Severity::Warning)
}

pub fn erlang_service_diagnostics(
Expand Down

0 comments on commit 0731696

Please sign in to comment.