Skip to content

Commit

Permalink
fix: functions and throw statements are underlined even if caught (#81)
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelangeloio authored Dec 17, 2023
1 parent 594c9bd commit 16adf85
Show file tree
Hide file tree
Showing 11 changed files with 388 additions and 45 deletions.
2 changes: 1 addition & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,4 @@
"editor.defaultFormatter": "biomejs.biome"
},
"doesItThrow.trace.server": "verbose"
}
}
61 changes: 53 additions & 8 deletions crates/does-it-throw-wasm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use wasm_bindgen::prelude::*;

use does_it_throw::call_finder::CallToThrowMap;
use does_it_throw::throw_finder::{IdentifierUsage, ThrowMap};
use does_it_throw::{analyze_code, AnalysisResult};
use does_it_throw::{analyze_code, AnalysisResult, UserSettings};

// Define an extern block with the `console.log` function.
#[wasm_bindgen]
Expand Down Expand Up @@ -403,6 +403,7 @@ interface InputData {
function_throw_severity?: DiagnosticSeverityInput;
call_to_throw_severity?: DiagnosticSeverityInput;
call_to_imported_throw_severity?: DiagnosticSeverityInput;
include_try_statement_throws?: boolean;
}
"#;

Expand Down Expand Up @@ -441,12 +442,13 @@ pub struct InputData {
// uri: String,
// typescript_settings: Option<TypeScriptSettings>,
// ids_to_check: Vec<String>,
file_content: String,
debug: Option<bool>,
throw_statement_severity: Option<DiagnosticSeverityInput>,
function_throw_severity: Option<DiagnosticSeverityInput>,
call_to_throw_severity: Option<DiagnosticSeverityInput>,
call_to_imported_throw_severity: Option<DiagnosticSeverityInput>,
pub file_content: String,
pub debug: Option<bool>,
pub throw_statement_severity: Option<DiagnosticSeverityInput>,
pub function_throw_severity: Option<DiagnosticSeverityInput>,
pub call_to_throw_severity: Option<DiagnosticSeverityInput>,
pub call_to_imported_throw_severity: Option<DiagnosticSeverityInput>,
pub include_try_statement_throws: Option<bool>,
}

#[wasm_bindgen]
Expand All @@ -456,7 +458,11 @@ pub fn parse_js(data: JsValue) -> JsValue {

let cm: Lrc<SourceMap> = Default::default();

let (results, cm) = analyze_code(&input_data.file_content, cm);
let user_settings = UserSettings {
include_try_statement_throws: input_data.include_try_statement_throws.unwrap_or(false),
};

let (results, cm) = analyze_code(&input_data.file_content, cm, &user_settings);

let parse_result = ParseResult::into(results, &cm, input_data.debug, input_data);

Expand Down Expand Up @@ -870,4 +876,43 @@ mod tests {
"Function imported that may throw."
);
}

#[test]
fn test_should_include_throws_in_try_statement() {
// smoke test to ensure backwards compatibility
let cm = Lrc::new(SourceMap::default());
let source_file = cm.new_source_file(
FileName::Custom("test_file".into()),
"function foo() {\n try {\n throw new Error();\n } catch (e) {\n throw e;\n }\n}".into(),
);

let throw_span = Span::new(
source_file.start_pos + BytePos(34),
source_file.start_pos + BytePos(51),
Default::default(),
);

let functions_with_throws = HashSet::from([ThrowMap {
throw_statement: throw_span,
throw_spans: vec![throw_span],
function_or_method_name: "foo".to_string(),
class_name: None,
id: "foo".to_string(),
}]);

let mut diagnostics: Vec<Diagnostic> = Vec::new();

add_diagnostics_for_functions_that_throw(
&mut diagnostics,
functions_with_throws,
&cm,
None,
DiagnosticSeverity::Hint,
DiagnosticSeverity::Hint,
);

assert_eq!(diagnostics.len(), 2);
assert_eq!(diagnostics[0].severity, DiagnosticSeverity::Hint.to_int());
assert_eq!(diagnostics[0].message, "Function that may throw.");
}
}
3 changes: 2 additions & 1 deletion crates/does-it-throw/src/fixtures/.vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
{
"doesItThrow.trace.server": "verbose"
"doesItThrow.trace.server": "verbose",
"doesItThrow.includeTryStatementThrows": false
}
7 changes: 7 additions & 0 deletions crates/does-it-throw/src/fixtures/something.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export const SomeThrow = () => {
throw new Error('never gonna let you down');
}

export function Something () {
throw new Error('never gonna run around and desert you')
}
148 changes: 148 additions & 0 deletions crates/does-it-throw/src/fixtures/tryStatement.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
// @ts-nocheck
export const someConstThatThrows = () => {
try {
throw new Error('never gonna give you up')
} catch (e) {
console.log(e)
}
}

function callToConstThatThrows4() {
someConstThatThrows()
}

const someCondition = true
export class Something {
constructor() {
try {
throw new Error('hi khue')
} catch (e) {
console.log(e)
}
}

someMethodThatThrows() {
try {
throw new Error('hi khue')
} catch (e) {
console.log(e)
}
}

someMethodThatDoesNotThrow() {
console.log('hi khue')
}

someMethodThatThrows2() {
if (someCondition) {
throw new Error('hi khue')
}
}

nestedThrow() {
try {
if (someCondition) {
return true
}
throw new Error('hi khue')
} catch (e) {
console.log(e)
}
}

callNestedThrow() {
if (someCondition) {
return true
}
if (someCondition) {
return true
}
this.nestedThrow()
}
}

const _somethingCall = () => {
const something = new Something()
something.someMethodThatThrows()
}

export const somethingCall = () => {
const something = new Something()
something.someMethodThatThrows()
}

function _somethingCall2() {
const something = new Something()
something.someMethodThatThrows()
}

export function somethingCall2() {
const something = new Something()
something.someMethodThatThrows()
}

// @ts-nocheck
// should work for private class
class SomeClass {
constructor(public x: number) {}

async _contextFromWorkflow() {
try {
throw new Error('Some error')
} catch (e) {
console.log(e)
}
}

async someCallToThrow() {
const { user, stravaUser, streakContext } = opts?.contextFromWorkFlow ?? (await this._contextFromWorkflow(job))
}
}

// should work for exported class
// biome-ignore lint/suspicious/noRedeclare: <explanation>
export class SomeClass2 {
constructor(public x: number) {}

async _contextFromWorkflow() {
try {
throw new Error('Some error')
} catch (e) {
console.log(e)
}
}

async someCallToThrow() {
const { user, stravaUser, streakContext } = opts?.contextFromWorkFlow ?? (await this._contextFromWorkflow(job))
}
}

const server = http.createServer(async (req, res) => {
switch (req.url) {
case '/api/pong':
console.log('pong!', INSTANCE_ID, PRIVATE_IP)
try {
throw new Error('')
} catch (e) {
console.log(e)
}
break
case '/api/ping':
console.log('ping!', INSTANCE_ID, PRIVATE_IP)
const ips = await SomeThrow()
someObjectLiteral.objectLiteralThrow()
const others = ips.filter((ip) => ip !== PRIVATE_IP)

others.forEach((ip) => {
http.get(`http://[${ip}]:8080/api/pong`)
})
break
case '/api/throw':
someRandomThrow()
break
}

res.end()
})

const wss = new WebSocketServer({ noServer: true })
11 changes: 10 additions & 1 deletion crates/does-it-throw/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,15 @@ impl From<CombinedAnalyzers> for AnalysisResult {
}
}

pub fn analyze_code(content: &str, cm: Lrc<SourceMap>) -> (AnalysisResult, Lrc<SourceMap>) {
pub struct UserSettings {
pub include_try_statement_throws: bool,
}

pub fn analyze_code(
content: &str,
cm: Lrc<SourceMap>,
user_settings: &UserSettings,
) -> (AnalysisResult, Lrc<SourceMap>) {
let fm = cm.new_source_file(swc_common::FileName::Anon, content.into());
let lexer = Lexer::new(
Syntax::Typescript(swc_ecma_parser::TsConfig {
Expand All @@ -75,6 +83,7 @@ pub fn analyze_code(content: &str, cm: Lrc<SourceMap>) -> (AnalysisResult, Lrc<S
function_name_stack: vec![],
current_class_name: None,
current_method_name: None,
include_try_statement: user_settings.include_try_statement_throws,
};
throw_collector.visit_module(&module);
let mut call_collector = CallFinder {
Expand Down
Loading

0 comments on commit 16adf85

Please sign in to comment.