rusty-javac 0.2.3

A Java compiler written in Rust.
Documentation
use crate::ast::{AnnotationDecl, AstNode, JavaSyntaxKind, JavaSyntaxNode};
use crate::call_resolver::ClassCatalog;
use crate::hir::lowering::literal::{parse_int_literal, string_literal_value};
use crate::hir::lowering::member::{MemberOptions, lower_members};
use crate::hir::lowering::signature::lower_type_params;
use crate::hir::lowering::syntax::{ExprToken, qualified_name_text, tokens_in_first_parens};
use crate::hir::lowering::types::TypeResolver;
use crate::hir::lowering::unit::internal_class_name;
use crate::hir::lowering::{LowerError, LowerResult};
use crate::hir::*;
use crate::ty::Ty;
use ustr::Ustr;

pub(super) fn lower_annotation_decl(
    annotation: AnnotationDecl,
    access_flags: u16,
    modifiers: Option<&JavaSyntaxNode>,
    package: Option<&Package>,
    imports: &[Import],
    catalog: &ClassCatalog,
) -> LowerResult<TypeDecl> {
    let name = annotation.name().ok_or(LowerError::MissingClassName)?;
    let internal_name = internal_class_name(package, name.text());
    let resolver = TypeResolver::for_class(package, imports, &internal_name, catalog)?;
    let annotations = lower_annotation_uses(modifiers, package, &resolver)?;
    let type_params = lower_type_params(annotation.syntax(), &resolver)?;
    let members = annotation
        .body()
        .map(|body| {
            lower_members(
                body.syntax(),
                &type_params,
                &resolver,
                Some(Ustr::from(&internal_name)),
                MemberOptions {
                    default_method_flags: crate::classfile::ACC_PUBLIC
                        | crate::classfile::ACC_ABSTRACT,
                    lower_constructors: false,
                    ..MemberOptions::default()
                },
            )
        })
        .transpose()?
        .unwrap_or_default();

    Ok(TypeDecl {
        id: HirId(0),
        name: Ustr::from(&internal_name),
        kind: TypeDeclKind::Annotation,
        access_flags: access_flags
            | crate::classfile::ACC_INTERFACE
            | crate::classfile::ACC_ABSTRACT
            | crate::classfile::ACC_ANNOTATION,
        super_class: None,
        interfaces: vec![Ty::class("java/lang/annotation/Annotation")],
        type_params,
        generic_signature: None,
        fields: members.fields,
        methods: members.methods,
        inner_types: members.inner_types,
        anonymous: None,
        record_components: Vec::new(),
        annotations,
    })
}

pub(super) fn lower_annotation_uses(
    modifiers: Option<&JavaSyntaxNode>,
    package: Option<&Package>,
    resolver: &TypeResolver,
) -> LowerResult<Vec<AnnotationUse>> {
    let Some(modifiers) = modifiers else {
        return Ok(Vec::new());
    };

    modifiers
        .children()
        .filter(|child| child.kind() == JavaSyntaxKind::Annotation)
        .map(|annotation| lower_annotation_use(&annotation, package, resolver))
        .collect()
}

fn lower_annotation_use(
    annotation: &JavaSyntaxNode,
    package: Option<&Package>,
    resolver: &TypeResolver,
) -> LowerResult<AnnotationUse> {
    let name = qualified_name_text(annotation)?;
    let internal_name = resolver
        .resolve_class_reference(&name)
        .unwrap_or_else(|| fallback_annotation_name(package, &name));

    Ok(AnnotationUse {
        descriptor: format!("L{internal_name};"),
        elements: annotation_elements(annotation)?,
    })
}

fn fallback_annotation_name(package: Option<&Package>, name: &str) -> String {
    if name.contains('.') {
        return name.replace('.', "/");
    }
    internal_class_name(package, name)
}

fn annotation_elements(annotation: &JavaSyntaxNode) -> LowerResult<Vec<AnnotationElement>> {
    let Ok(tokens) = tokens_in_first_parens(annotation) else {
        return Ok(Vec::new());
    };
    if tokens.is_empty() {
        return Ok(Vec::new());
    }

    split_annotation_args(tokens)
        .into_iter()
        .map(lower_annotation_element)
        .collect()
}

fn split_annotation_args(tokens: Vec<ExprToken>) -> Vec<Vec<ExprToken>> {
    let mut groups = Vec::new();
    let mut current = Vec::new();
    let mut depth = 0usize;

    for token in tokens {
        match token.kind {
            JavaSyntaxKind::LParen | JavaSyntaxKind::LBrace | JavaSyntaxKind::LBrack => {
                depth += 1;
                current.push(token);
            }
            JavaSyntaxKind::RParen | JavaSyntaxKind::RBrace | JavaSyntaxKind::RBrack => {
                depth = depth.saturating_sub(1);
                current.push(token);
            }
            JavaSyntaxKind::Comma if depth == 0 => {
                groups.push(std::mem::take(&mut current));
            }
            _ => current.push(token),
        }
    }

    if !current.is_empty() {
        groups.push(current);
    }
    groups
}

fn lower_annotation_element(tokens: Vec<ExprToken>) -> LowerResult<AnnotationElement> {
    let (name, value_tokens) = split_named_annotation_value(&tokens);
    Ok(AnnotationElement {
        name: Ustr::from(name),
        value: lower_annotation_value(value_tokens)?,
    })
}

fn split_named_annotation_value(tokens: &[ExprToken]) -> (&str, &[ExprToken]) {
    let Some(eq_index) = tokens
        .iter()
        .position(|token| token.kind == JavaSyntaxKind::Eq)
    else {
        return ("value", tokens);
    };
    let name = tokens[..eq_index]
        .iter()
        .find(|token| token.kind == JavaSyntaxKind::Ident)
        .map(|token| token.text.as_str())
        .unwrap_or("value");
    (name, &tokens[eq_index + 1..])
}

fn lower_annotation_value(tokens: &[ExprToken]) -> LowerResult<AnnotationValue> {
    let Some(token) = tokens
        .iter()
        .find(|token| token.kind != JavaSyntaxKind::Comma)
    else {
        return Err(LowerError::UnsupportedExpression);
    };

    match token.kind {
        JavaSyntaxKind::StringLiteral => {
            Ok(AnnotationValue::String(string_literal_value(&token.text)))
        }
        JavaSyntaxKind::IntLiteral => Ok(AnnotationValue::Int(parse_int_literal(&token.text))),
        JavaSyntaxKind::TrueKw => Ok(AnnotationValue::Boolean(true)),
        JavaSyntaxKind::FalseKw => Ok(AnnotationValue::Boolean(false)),
        _ => Err(LowerError::UnsupportedExpression),
    }
}