Skip to content

Commit

Permalink
feat: user can now discard warnings with ignore statements (#118)
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelangeloio authored Jan 6, 2024
1 parent a3cb052 commit 3f8957c
Show file tree
Hide file tree
Showing 11 changed files with 485 additions and 40 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ Untyped `throw` statements can be a pain for those who come from languages like
> The core of the code is written in Rust, and the LSP implementation for VsCode is written in Typescript. The Rust code is compiled to WASM and bundled with the VsCode extension. The extension is published to the VsCode marketplace, and the Rust code is published to [crates.io](https://crates.io/crates/does-it-throw).
## Usage

For a usage and configuration guide, check out the [usage](https://github.com/michaelangeloio/does-it-throw/blob/main/docs/usage.md) page!


## Limitations

Expand Down
3 changes: 3 additions & 0 deletions crates/does-it-throw-wasm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,7 @@ interface InputData {
call_to_throw_severity?: DiagnosticSeverityInput;
call_to_imported_throw_severity?: DiagnosticSeverityInput;
include_try_statement_throws?: boolean;
ignore_statements?: string[];
}
"#;

Expand Down Expand Up @@ -449,6 +450,7 @@ pub struct InputData {
pub call_to_throw_severity: Option<DiagnosticSeverityInput>,
pub call_to_imported_throw_severity: Option<DiagnosticSeverityInput>,
pub include_try_statement_throws: Option<bool>,
pub ignore_statements: Option<Vec<String>>,
}

#[wasm_bindgen]
Expand All @@ -460,6 +462,7 @@ pub fn parse_js(data: JsValue) -> JsValue {

let user_settings = UserSettings {
include_try_statement_throws: input_data.include_try_statement_throws.unwrap_or(false),
ignore_statements: input_data.ignore_statements.clone().unwrap_or_else(Vec::new),
};

let (results, cm) = analyze_code(&input_data.file_content, cm, &user_settings);
Expand Down
7 changes: 6 additions & 1 deletion crates/does-it-throw/src/fixtures/.vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
{
"doesItThrow.trace.server": "verbose",
"doesItThrow.includeTryStatementThrows": false
"doesItThrow.includeTryStatementThrows": false,
"doesItThrow.ignoreStatements": [
"@it-throws",
"@does-it-throw-ignore",
"@some-random-ignore"
]
}
96 changes: 96 additions & 0 deletions crates/does-it-throw/src/fixtures/ignoreStatements.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
// @ts-nocheck
const someCondition = true
export class Something {
constructor() {
//@it-throws
throw new Error('hi khue')
}

someMethodThatThrows() {
//@it-throws
throw new Error('hi khue')
}

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

someMethodThatThrows2() {
if (someCondition) {
//@it-throws
throw new Error('hi khue')
}
}

nestedThrow() {
if (someCondition) {
return true
}
//@it-throws
throw new Error('hi khue')
}

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


const someRandomThrow = () => {
//@it-throws
throw new Error('some random throw')
}

const server = http.createServer(async (req, res) => {
switch (req.url) {
case '/api/pong':
console.log('pong!', INSTANCE_ID, PRIVATE_IP)
//@it-throws
throw new Error('')
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 })


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

export function somethingCall2() {
const something = new Something()
something.someMethodThatThrows()
}
144 changes: 144 additions & 0 deletions crates/does-it-throw/src/fixtures/ignoreStatements.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
// @ts-nocheck
const someCondition = true
export class Something {
constructor() {
// @it-throws
throw new Error('hi khue')
}

someMethodThatThrows() {
// @it-throws
throw new Error('hi khue')
}

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

someMethodThatThrows2() {
if (someCondition) {
// @some-random-ignore
throw new Error('hi khue')
}
}

nestedThrow() {
if (someCondition) {
return true
}
// @it-throws-ignore
throw new Error('hi khue')
}

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


const someRandomThrow = () => {
//@it-throws
throw new Error('some random throw')
}

const server = http.createServer(async (req, res) => {
switch (req.url) {
case '/api/pong':
console.log('pong!', INSTANCE_ID, PRIVATE_IP)
//@it-throws
throw new Error('')
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 })


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

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


// @ts-nocheck
const connection = {}

const SomeThrow = () => {
//@it-throws
throw new Error('hi khue')
}

function SomeThrow2() {
//@it-throws
throw new Error('hi khue')
}

connection.onInitialized(() => {
if (hasConfigurationCapability) {
// Register for all configuration changes.
connection.client.register(DidChangeConfigurationNotification.type, undefined)
}
if (hasWorkspaceFolderCapability) {
connection.workspace.onDidChangeWorkspaceFolders((_event) => {
connection.console.log(`Workspace folder change event received. ${JSON.stringify(_event)}`)
})
}
SomeThrow()
SomeThrow2()
})

connection.onInitialized2(() => {
//@it-throws
throw new Error('hi khue')
})

SomeRandomCall(() => {
//@it-throws
throw new Error('hi khue')
})

SomeRandomCall2(() => {
SomeThrow()
SomeThrow2()
})

connection.oneWithASecondArg({}, () => {
//@it-throws
throw new Error('hi khue')
})
19 changes: 13 additions & 6 deletions crates/does-it-throw/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ pub mod import_usage_finder;
pub mod throw_finder;
use call_finder::{CallFinder, CallToThrowMap};
use import_usage_finder::ImportUsageFinder;
use throw_finder::{IdentifierUsage, ThrowAnalyzer, ThrowMap};
use swc_common::comments::SingleThreadedComments;
use throw_finder::{IdentifierUsage, ThrowAnalyzer, ThrowMap, ThrowFinderSettings};
extern crate swc_common;
extern crate swc_ecma_ast;
extern crate swc_ecma_parser;
Expand All @@ -29,13 +30,13 @@ pub struct AnalysisResult {
pub imported_identifier_usages: HashSet<IdentifierUsage>,
}

struct CombinedAnalyzers {
throw_analyzer: ThrowAnalyzer,
struct CombinedAnalyzers<'throwfinder_settings> {
throw_analyzer: ThrowAnalyzer<'throwfinder_settings>,
call_finder: CallFinder,
import_usage_finder: ImportUsageFinder,
}

impl From<CombinedAnalyzers> for AnalysisResult {
impl <'throwfinder_settings> From<CombinedAnalyzers<'throwfinder_settings>> for AnalysisResult {
fn from(analyzers: CombinedAnalyzers) -> Self {
Self {
functions_with_throws: analyzers.throw_analyzer.functions_with_throws,
Expand All @@ -51,6 +52,7 @@ impl From<CombinedAnalyzers> for AnalysisResult {

pub struct UserSettings {
pub include_try_statement_throws: bool,
pub ignore_statements: Vec<String>,
}

pub fn analyze_code(
Expand All @@ -59,6 +61,7 @@ pub fn analyze_code(
user_settings: &UserSettings,
) -> (AnalysisResult, Lrc<SourceMap>) {
let fm = cm.new_source_file(swc_common::FileName::Anon, content.into());
let comments = Lrc::new(SingleThreadedComments::default());
let lexer = Lexer::new(
Syntax::Typescript(swc_ecma_parser::TsConfig {
tsx: true,
Expand All @@ -69,12 +72,13 @@ pub fn analyze_code(
}),
EsVersion::latest(),
StringInput::from(&*fm),
None,
Some(&comments),
);

let mut parser = Parser::new_from(lexer);
let module = parser.parse_module().expect("Failed to parse module");
let mut throw_collector = ThrowAnalyzer {
comments: comments.clone(),
functions_with_throws: HashSet::new(),
json_parse_calls: vec![],
fs_access_calls: vec![],
Expand All @@ -83,7 +87,10 @@ pub fn analyze_code(
function_name_stack: vec![],
current_class_name: None,
current_method_name: None,
include_try_statement: user_settings.include_try_statement_throws,
throwfinder_settings: ThrowFinderSettings {
ignore_statements: &user_settings.ignore_statements.clone(),
include_try_statements: &user_settings.include_try_statement_throws.clone(),
}
};
throw_collector.visit_module(&module);
let mut call_collector = CallFinder {
Expand Down
Loading

0 comments on commit 3f8957c

Please sign in to comment.