Skip to content

Commit

Permalink
LS: Proc macros - scarb code
Browse files Browse the repository at this point in the history
commit-id:381d1f35
  • Loading branch information
Draggu committed Nov 12, 2024
1 parent 7fe32e4 commit 6b63f90
Show file tree
Hide file tree
Showing 7 changed files with 524 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.

4 changes: 4 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,8 @@ cairo-lang-syntax = { path = "../cairo-lang-syntax", version = "~2.8.4" }
cairo-lang-test-plugin = { path = "../cairo-lang-test-plugin", version = "~2.8.4" }
cairo-lang-utils = { path = "../cairo-lang-utils", version = "~2.8.4" }
cairo-lang-macro = { git = "https://github.com/software-mansion/scarb", rev = "fc99b54" } # not registry version because conflict with scarb-proc-macro-server-types local version
# Used in proc-macro code copied from scarb.
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 +40,8 @@ rustc-hash = "1.1.0"
salsa.workspace = true
scarb-metadata = "1.13"
scarb-proc-macro-server-types = { git = "https://github.com/software-mansion/scarb", rev = "fc99b54" }
# Used in proc-macro code copied from scarb.
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
/// [`ProcMacroCacheGroup`](`crate::lang::proc_macros::cache_group::ProcMacroCacheGroup`). To do
/// this, we first convert the `db` reference to its original concrete type that implements both
/// traits [`AnalysisDatabase`]. After this,
/// [`ProcMacroCacheGroup`](`crate::lang::proc_macros::cache_group::ProcMacroCacheGroup`) 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::ProcMacroCacheGroup;

#[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,16 @@ 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;

// Keep it private.
mod downcast;
// Code from scarb.
mod scarb;

/// Important: NEVER make it pub outside this crate. See [`unsafe_downcast_ref`] for more info.
pub(crate) fn proc_macro_plugin_suite(defined_macros: DefinedMacrosResponse) -> PluginSuite {
let mut plugin_suite = PluginSuite::default();
Expand Down Expand Up @@ -34,11 +42,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 @@ -57,10 +68,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,51 @@
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::ProcMacroCacheGroup;

// scarb code

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);
// modified
let result = db.get_inline_macros_expansion(ExpandInlineMacroParams {
name: syntax.path(db).as_syntax_node().get_text(db),
args: token_stream,
});
// end modified
// 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,39 @@
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;

// Code from scarb.
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)
}
}

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 6b63f90

Please sign in to comment.