rusty-javac 0.2.3

A Java compiler written in Rust.
Documentation
use crate::ast::{
    AstNode, ClassDecl, CompilationUnit as AstCompilationUnit, ImportDecl as AstImportDecl,
    TypeDecl as AstTypeDecl,
};
use crate::ast::{JavaSyntaxKind, JavaSyntaxNode};
use crate::call_resolver::ClassCatalog;
use crate::hir::lowering::member::lower_class_members;
use crate::hir::lowering::modifiers::access_flags;
use crate::hir::lowering::signature::{class_signature, lower_type_params};
use crate::hir::lowering::syntax::{qualified_name_text, qualified_name_text_range, source_line};
use crate::hir::lowering::types::TypeResolver;
use crate::hir::lowering::{LowerError, LowerResult};
use crate::hir::*;
use ustr::Ustr;

pub(super) fn lower_compilation_unit(
    node: &JavaSyntaxNode,
    catalog: &ClassCatalog,
) -> LowerResult<CompilationUnit> {
    let unit = AstCompilationUnit::cast(node.clone()).ok_or(LowerError::ExpectedCompilationUnit)?;
    let package = lower_package(&unit)?;
    let imports = lower_imports(&unit)?;
    let type_decls = lower_top_level_types(node, package.as_ref(), &imports, catalog)?;

    Ok(CompilationUnit {
        package,
        imports,
        type_decls,
    })
}

fn lower_package(unit: &AstCompilationUnit) -> LowerResult<Option<Package>> {
    unit.package()
        .map(|package| {
            let name = qualified_name_text(package.syntax())?;
            Ok(Package {
                name: Ustr::from(&name),
            })
        })
        .transpose()
}

fn lower_imports(unit: &AstCompilationUnit) -> LowerResult<Vec<Import>> {
    unit.imports().map(lower_import).collect()
}

fn lower_import(import: AstImportDecl) -> LowerResult<Import> {
    let (path, range) = qualified_name_text_range(import.syntax())?;
    Ok(Import {
        path: Ustr::from(&path),
        is_static: import.is_static(),
        is_wildcard: import.is_wildcard(),
        source_line: Some(source_line(import.syntax())),
        source_range: Some(range),
    })
}

fn lower_top_level_types(
    node: &JavaSyntaxNode,
    package: Option<&Package>,
    imports: &[Import],
    catalog: &ClassCatalog,
) -> LowerResult<Vec<TypeDecl>> {
    let mut pending_flags = 0;
    let mut pending_modifiers = None;
    let mut type_decls = Vec::new();
    for child in node.children() {
        match child.kind() {
            JavaSyntaxKind::ModifierList => {
                pending_flags = access_flags(&child);
                pending_modifiers = Some(child);
            }
            JavaSyntaxKind::ClassDecl
            | JavaSyntaxKind::InterfaceDecl
            | JavaSyntaxKind::EnumDecl
            | JavaSyntaxKind::RecordDecl
            | JavaSyntaxKind::AnnotationDecl => {
                let decl =
                    AstTypeDecl::cast(child).ok_or(LowerError::UnsupportedTypeDeclaration)?;
                type_decls.push(lower_type_decl(
                    decl,
                    pending_flags,
                    pending_modifiers.as_ref(),
                    package,
                    imports,
                    catalog,
                )?);
                pending_flags = 0;
                pending_modifiers = None;
            }
            _ => {}
        }
    }

    if type_decls.is_empty() {
        return Err(LowerError::ExpectedSingleTopLevelClass);
    }

    Ok(type_decls)
}

fn lower_type_decl(
    decl: AstTypeDecl,
    access_flags: u16,
    modifiers: Option<&JavaSyntaxNode>,
    package: Option<&Package>,
    imports: &[Import],
    catalog: &ClassCatalog,
) -> LowerResult<TypeDecl> {
    match decl {
        AstTypeDecl::Class(class) => {
            lower_class_decl(class, access_flags, modifiers, package, imports, catalog)
        }
        AstTypeDecl::Record(record) => crate::hir::lowering::record::lower_record_decl(
            record,
            access_flags,
            modifiers,
            package,
            imports,
            catalog,
        ),
        AstTypeDecl::Enum(enum_decl) => crate::hir::lowering::enum_decl::lower_enum_decl(
            enum_decl,
            access_flags,
            modifiers,
            package,
            imports,
            catalog,
        ),
        AstTypeDecl::Annotation(annotation) => {
            crate::hir::lowering::annotation::lower_annotation_decl(
                annotation,
                access_flags,
                modifiers,
                package,
                imports,
                catalog,
            )
        }
        AstTypeDecl::Interface(_) => Err(LowerError::UnsupportedTypeDeclaration),
    }
}

fn lower_class_decl(
    class: ClassDecl,
    access_flags: u16,
    modifiers: Option<&JavaSyntaxNode>,
    package: Option<&Package>,
    imports: &[Import],
    catalog: &ClassCatalog,
) -> LowerResult<TypeDecl> {
    let name = class.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 =
        crate::hir::lowering::annotation::lower_annotation_uses(modifiers, package, &resolver)?;
    let type_params = lower_type_params(class.syntax(), &resolver)?;
    let generic_signature = class_signature(class.syntax(), &type_params, &resolver)?;
    let members = class
        .body()
        .map(|body| {
            lower_class_members(
                body,
                &type_params,
                &resolver,
                Some(Ustr::from(&internal_name)),
            )
        })
        .transpose()?
        .unwrap_or_default();

    Ok(TypeDecl {
        id: HirId(0),
        name: Ustr::from(&internal_name),
        kind: TypeDeclKind::Class,
        access_flags,
        super_class: None,
        interfaces: Vec::new(),
        type_params,
        generic_signature,
        fields: members.fields,
        methods: members.methods,
        inner_types: members.inner_types,
        anonymous: None,
        record_components: Vec::new(),
        annotations,
    })
}

pub(super) fn internal_class_name(package: Option<&Package>, simple_name: &str) -> String {
    match package {
        Some(package) => format!(
            "{}/{}",
            package.name.as_str().replace('.', "/"),
            simple_name
        ),
        None => simple_name.to_string(),
    }
}