lisette-semantics 0.4.1

Little language inspired by Rust that compiles to Go
Documentation
use diagnostics::LocalSink;
use rustc_hash::FxHashMap as HashMap;
use syntax::ast::Span;
use syntax::types::Type;

use crate::facts::Usage;
use crate::store::Store;

pub(super) fn build_index(store: &Store) -> HashMap<Span, String> {
    let mut index = HashMap::default();
    for module in store.modules.values() {
        for definition in module.definitions.values() {
            if !is_function_type(&definition.ty) {
                continue;
            }
            let Some(name_span) = definition.name_span() else {
                continue;
            };
            let Some(doc) = definition.doc() else {
                continue;
            };
            if let Some(message) = deprecation_message(doc) {
                index.insert(name_span, message);
            }
        }
    }
    index
}

fn is_function_type(ty: &Type) -> bool {
    match ty {
        Type::Function(_) => true,
        Type::Forall { body, .. } => is_function_type(body),
        _ => false,
    }
}

pub(super) fn sweep(usages: &[&Usage], index: &HashMap<Span, String>, sink: &LocalSink) {
    for usage in usages {
        if let Some(message) = index.get(&usage.definition_span) {
            sink.push(diagnostics::lint::deprecated_api(
                &usage.usage_span,
                message,
            ));
        }
    }
}

fn deprecation_message(doc: &str) -> Option<String> {
    if !doc.contains("Deprecated:") {
        return None;
    }

    let mut paragraph = doc
        .lines()
        .map(str::trim)
        .skip_while(|line| !line.starts_with("Deprecated:"))
        .take_while(|line| !line.is_empty());

    let mut message = paragraph
        .next()?
        .trim_start_matches("Deprecated:")
        .trim()
        .to_string();
    for line in paragraph {
        message.push(' ');
        message.push_str(line);
    }

    if message.is_empty() {
        return None;
    }

    let mut chars = message.chars();
    let capitalized = match chars.next() {
        Some(first) => first.to_uppercase().collect::<String>() + chars.as_str(),
        None => message,
    };
    Some(capitalized)
}