Skip to content

Commit

Permalink
LS: Proc macro - scarb code
Browse files Browse the repository at this point in the history
Code calling dylib is replaced with one calling proc-macro-server. Also `aux_data` is removed as it is useless for LS.

commit-id:381d1f35
  • Loading branch information
Draggu committed Nov 19, 2024
1 parent dcdb5d7 commit 905c785
Show file tree
Hide file tree
Showing 7 changed files with 742 additions and 6 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions crates/cairo-lang-language-server/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ cairo-lang-syntax = { path = "../cairo-lang-syntax", version = "~2.8.5" }
cairo-lang-test-plugin = { path = "../cairo-lang-test-plugin", version = "~2.8.5" }
cairo-lang-utils = { path = "../cairo-lang-utils", version = "~2.8.5" }
cairo-lang-macro = "0.1.1"
convert_case.workspace = true
crossbeam = "0.8.4"
governor = { version = "0.7.0", default-features = false, features = ["std", "quanta"]}
indent.workspace = true
Expand All @@ -38,6 +39,7 @@ rustc-hash = "1.1.0"
salsa.workspace = true
scarb-metadata = "1.13"
scarb-proc-macro-server-types = "0.1"
scarb-stable-hash = "1.0.0"
serde = { workspace = true, default-features = true }
serde_json.workspace = true
smol_str.workspace = true
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
use cairo_lang_syntax::node::db::SyntaxGroup;

use crate::lang::db::AnalysisDatabase;

/// This function is necessary due to trait bounds that cannot be bypassed in any other way.
/// `generate_code()` takes db: [`&dyn SyntaxGroup`](`SyntaxGroup`),
/// but we need to use
/// [`ProcMacroGroup`](`crate::lang::proc_macros::db::ProcMacroGroup`). To do
/// this, we first convert the `db` reference to its original concrete type that implements both
/// traits [`AnalysisDatabase`]. After this,
/// [`ProcMacroGroup`](`crate::lang::proc_macros::db::ProcMacroGroup`) can be
/// accessed.
///
/// Safety: This function MUST only be invoked with an object that is of type
/// [AnalysisDatabase]. Using it with any other type leads to undefined behavior.
pub(super) unsafe fn unsafe_downcast_ref(db: &dyn SyntaxGroup) -> &AnalysisDatabase {
// Replicated logic from `impl dyn Any downcast_ref_unchecked()`.
// This approach works as long as `impl dyn Any downcast_ref_unchecked()` implementation is
// unchanged and the caller can ensure that `db` is truly an instance of AnalysisDatabase.
&*(db as *const dyn SyntaxGroup as *const AnalysisDatabase)
}

#[cfg(test)]
mod unsafe_downcast_ref_tests {
use cairo_lang_macro::TokenStream;
use cairo_lang_syntax::node::db::SyntaxGroup;
use rustc_hash::FxHashMap;
use scarb_proc_macro_server_types::methods::ProcMacroResult;
use scarb_proc_macro_server_types::methods::expand::ExpandAttributeParams;

use super::unsafe_downcast_ref;
use crate::lang::db::AnalysisDatabase;
use crate::lang::proc_macros::db::ProcMacroGroup;

#[test]
fn cast_succeed() {
let mut db = AnalysisDatabase::new(&Default::default());

let input = ExpandAttributeParams {
attr: "asd".to_string(),
args: TokenStream::new("asd".to_string()),
item: TokenStream::new("asd".to_string()),
};
let output = ProcMacroResult {
token_stream: TokenStream::new("asd".to_string()),
diagnostics: Default::default(),
};
let macro_resolution: FxHashMap<_, _> = [(input, output)].into_iter().collect();

db.set_attribute_macro_resolution(macro_resolution.clone());

let syntax: &dyn SyntaxGroup = &db;
let analysis_db = unsafe { unsafe_downcast_ref(syntax) };

assert_eq!(analysis_db.attribute_macro_resolution(), macro_resolution);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,15 @@ use std::sync::Arc;

use cairo_lang_defs::plugin::{InlineMacroExprPlugin, MacroPlugin};
use cairo_lang_semantic::plugin::PluginSuite;
use downcast::unsafe_downcast_ref;
use scarb::inline::inline_macro_generate_code;
use scarb::regular::macro_generate_code;
use scarb_proc_macro_server_types::methods::defined_macros::DefinedMacrosResponse;

mod downcast;
// TODO(#6666) Evict this module when this is possible.
mod scarb;

pub(crate) fn proc_macro_plugin_suite(defined_macros: DefinedMacrosResponse) -> PluginSuite {
let mut plugin_suite = PluginSuite::default();

Expand Down Expand Up @@ -32,11 +39,14 @@ struct ProcMacroPlugin {
impl MacroPlugin for ProcMacroPlugin {
fn generate_code(
&self,
_db: &dyn cairo_lang_syntax::node::db::SyntaxGroup,
_item_ast: cairo_lang_syntax::node::ast::ModuleItem,
db: &dyn cairo_lang_syntax::node::db::SyntaxGroup,
item_ast: cairo_lang_syntax::node::ast::ModuleItem,
_metadata: &cairo_lang_defs::plugin::MacroPluginMetadata<'_>,
) -> cairo_lang_defs::plugin::PluginResult {
todo!();
// Safety: We use this plugin only in AnalysisDatabase.
let analysis_db = unsafe { unsafe_downcast_ref(db) };

macro_generate_code(analysis_db, item_ast, &self.defined_attributes, &self.defined_derives)
}

fn declared_attributes(&self) -> Vec<String> {
Expand All @@ -54,10 +64,13 @@ struct InlineProcMacroPlugin;
impl InlineMacroExprPlugin for InlineProcMacroPlugin {
fn generate_code(
&self,
_db: &dyn cairo_lang_syntax::node::db::SyntaxGroup,
_item_ast: &cairo_lang_syntax::node::ast::ExprInlineMacro,
db: &dyn cairo_lang_syntax::node::db::SyntaxGroup,
item_ast: &cairo_lang_syntax::node::ast::ExprInlineMacro,
_metadata: &cairo_lang_defs::plugin::MacroPluginMetadata<'_>,
) -> cairo_lang_defs::plugin::InlinePluginResult {
todo!();
// Safety: We use this plugin only in AnalysisDatabase.
let analysis_db = unsafe { unsafe_downcast_ref(db) };

inline_macro_generate_code(analysis_db, item_ast)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
use cairo_lang_defs::plugin::{InlinePluginResult, PluginGeneratedFile};
use cairo_lang_filesystem::ids::{CodeMapping, CodeOrigin};
use cairo_lang_filesystem::span::{TextOffset, TextSpan, TextWidth};
use cairo_lang_macro::TokenStream;
use cairo_lang_syntax::node::{TypedStablePtr, TypedSyntaxNode, ast};
use scarb_proc_macro_server_types::methods::expand::ExpandInlineMacroParams;

use super::{FromSyntaxNode, into_cairo_diagnostics};
use crate::lang::db::AnalysisDatabase;
use crate::lang::proc_macros::db::ProcMacroGroup;

// https://github.com/software-mansion/scarb/blob/4e81d1c4498137f80e211c6e2c6a5a6de01c66f2/scarb/src/compiler/plugin/proc_macro/host.rs#L1015-L1059
pub fn inline_macro_generate_code(
db: &AnalysisDatabase,
syntax: &ast::ExprInlineMacro,
) -> InlinePluginResult {
let origin = CodeOrigin::Span(syntax.as_syntax_node().span(db));
let stable_ptr = syntax.clone().stable_ptr().untyped();
let token_stream = TokenStream::from_syntax_node(db, syntax);
// region: Modified scarb code
let result = db.get_inline_macros_expansion(ExpandInlineMacroParams {
name: syntax.path(db).as_syntax_node().get_text(db),
args: token_stream,
});
// endregion
// Handle diagnostics.
let diagnostics = into_cairo_diagnostics(result.diagnostics, stable_ptr);
let token_stream = result.token_stream.clone();
if token_stream.is_empty() {
// Remove original code
InlinePluginResult { code: None, diagnostics }
} else {
let content = token_stream.to_string();
InlinePluginResult {
code: Some(PluginGeneratedFile {
name: "inline_proc_macro".into(),
code_mappings: vec![CodeMapping {
origin,
span: TextSpan {
start: TextOffset::default(),
end: TextOffset::default().add_width(TextWidth::from_str(&content)),
},
}],
content,
aux_data: None,
}),
diagnostics,
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
use cairo_lang_defs::patcher::PatchBuilder;
use cairo_lang_defs::plugin::PluginDiagnostic;
use cairo_lang_macro::{Severity, TokenStream};
use cairo_lang_syntax::node::TypedSyntaxNode;
use cairo_lang_syntax::node::db::SyntaxGroup;
use cairo_lang_syntax::node::ids::SyntaxStablePtrId;

pub mod inline;
pub mod regular;

// https://github.com/software-mansion/scarb/blob/4e81d1c4498137f80e211c6e2c6a5a6de01c66f2/scarb/src/compiler/plugin/proc_macro/ffi.rs#L30-L40
trait FromSyntaxNode {
fn from_syntax_node(db: &dyn SyntaxGroup, node: &impl TypedSyntaxNode) -> Self;
}

impl FromSyntaxNode for TokenStream {
fn from_syntax_node(db: &dyn SyntaxGroup, node: &impl TypedSyntaxNode) -> Self {
let mut builder = PatchBuilder::new(db, node);
builder.add_node(node.as_syntax_node());
Self::new(builder.build().0)
}
}

// https://github.com/software-mansion/scarb/blob/4e81d1c4498137f80e211c6e2c6a5a6de01c66f2/scarb/src/compiler/plugin/proc_macro/host.rs#L1068-L1083
fn into_cairo_diagnostics(
diagnostics: Vec<cairo_lang_macro::Diagnostic>,
stable_ptr: SyntaxStablePtrId,
) -> Vec<PluginDiagnostic> {
diagnostics
.into_iter()
.map(|diag| PluginDiagnostic {
stable_ptr,
message: diag.message,
severity: match diag.severity {
Severity::Error => cairo_lang_diagnostics::Severity::Error,
Severity::Warning => cairo_lang_diagnostics::Severity::Warning,
},
})
.collect()
}
Loading

0 comments on commit 905c785

Please sign in to comment.