#![allow(unused)]
use std::path::Path;
#[cfg(feature = "go-ast")]
use crate::services::unified_go_analyzer::UnifiedGoAnalyzer;
#[cfg(feature = "wasm-ast")]
use crate::services::unified_wasm_analyzer::UnifiedWasmAnalyzer;
#[cfg(feature = "go-ast")]
use super::metrics::GO_UNIFIED_CACHE;
#[cfg(feature = "wasm-ast")]
use super::metrics::WASM_UNIFIED_CACHE;
#[allow(unused_variables)]
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "path_exists")]
pub async fn analyze_go_language(
file_path: &std::path::Path,
) -> anyhow::Result<Vec<crate::services::context::AstItem>> {
#[cfg(feature = "go-ast")]
{
let analyzer = UnifiedGoAnalyzer::new(file_path.to_path_buf());
match analyzer.analyze().await {
Ok(analysis) => {
GO_UNIFIED_CACHE.insert(file_path.to_path_buf(), analysis.file_metrics.clone());
Ok(analysis.ast_items)
}
Err(_) => {
analyze_go_file(file_path).await
}
}
}
#[cfg(not(feature = "go-ast"))]
Ok(Vec::new())
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "path_exists")]
pub async fn analyze_c_language(
file_path: &std::path::Path,
) -> anyhow::Result<Vec<crate::services::context::AstItem>> {
#[cfg(feature = "c-ast")]
{
use crate::services::ast::languages::c;
let file_context = c::analyze_c_file(file_path)
.await
.map_err(|e| anyhow::anyhow!("C analysis error: {}", e))?;
Ok(file_context.items)
}
#[cfg(not(feature = "c-ast"))]
analyze_c_file(file_path).await
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "path_exists")]
pub async fn analyze_cpp_language(
file_path: &std::path::Path,
) -> anyhow::Result<Vec<crate::services::context::AstItem>> {
#[cfg(feature = "cpp-ast")]
{
use crate::services::ast::languages::cpp;
let file_context = cpp::analyze_cpp_file(file_path)
.await
.map_err(|e| anyhow::anyhow!("C++ analysis error: {}", e))?;
Ok(file_context.items)
}
#[cfg(not(feature = "cpp-ast"))]
analyze_c_file(file_path).await }
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "path_exists")]
pub async fn analyze_kotlin_language(
file_path: &std::path::Path,
) -> anyhow::Result<Vec<crate::services::context::AstItem>> {
tracing::debug!("Analyzing Kotlin file: {}", file_path.display());
let items = analyze_kotlin_file(file_path).await?;
tracing::debug!("Kotlin analysis returned {} items", items.len());
Ok(items)
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "path_exists")]
pub async fn analyze_java_language(
file_path: &std::path::Path,
) -> anyhow::Result<Vec<crate::services::context::AstItem>> {
analyze_java_file(file_path).await
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "path_exists")]
pub async fn analyze_csharp_language(
file_path: &std::path::Path,
) -> anyhow::Result<Vec<crate::services::context::AstItem>> {
analyze_csharp_file(file_path).await
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "path_exists")]
pub async fn analyze_swift_language(
file_path: &std::path::Path,
) -> anyhow::Result<Vec<crate::services::context::AstItem>> {
analyze_swift_file(file_path).await
}
#[allow(unused_variables)]
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "path_exists")]
pub async fn analyze_wasm_language(
file_path: &std::path::Path,
) -> anyhow::Result<Vec<crate::services::context::AstItem>> {
#[cfg(feature = "wasm-ast")]
{
let analyzer = UnifiedWasmAnalyzer::new(file_path.to_path_buf());
match analyzer.analyze().await {
Ok(analysis) => {
WASM_UNIFIED_CACHE.insert(file_path.to_path_buf(), analysis.file_metrics.clone());
Ok(analysis.ast_items)
}
Err(_) => {
analyze_wasm_file(file_path).await
}
}
}
#[cfg(not(feature = "wasm-ast"))]
Ok(Vec::new())
}
#[allow(unused_variables)]
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "path_exists")]
pub async fn analyze_lean_language(
file_path: &std::path::Path,
) -> anyhow::Result<Vec<crate::services::context::AstItem>> {
#[cfg(feature = "lean-ast")]
{
use crate::services::languages::lean;
match lean::analyze_lean_file(file_path).await {
Ok(file_context) => Ok(file_context.items),
Err(_) => Ok(Vec::new()),
}
}
#[cfg(not(feature = "lean-ast"))]
Ok(Vec::new())
}
async fn analyze_go_file(
_file_path: &Path,
) -> anyhow::Result<Vec<crate::services::context::AstItem>> {
#[cfg(feature = "go-ast")]
{
use crate::services::languages::go;
match go::analyze_go_file(_file_path).await {
Ok(file_context) => Ok(file_context.items),
Err(_) => Ok(Vec::new()), }
}
#[cfg(not(feature = "go-ast"))]
Ok(Vec::new())
}
async fn analyze_c_file(
#[allow(unused_variables)] file_path: &Path,
) -> anyhow::Result<Vec<crate::services::context::AstItem>> {
#[cfg(feature = "c-ast")]
{
use crate::services::ast::languages::c;
let file_context = c::analyze_c_file(file_path)
.await
.map_err(|e| anyhow::anyhow!("C analysis error: {}", e))?;
Ok(file_context.items)
}
#[cfg(not(feature = "c-ast"))]
Ok(Vec::new())
}
async fn analyze_kotlin_file(
#[allow(unused_variables)] file_path: &Path,
) -> anyhow::Result<Vec<crate::services::context::AstItem>> {
Ok(Vec::new())
}
pub(crate) const JAVA_AST_UNAVAILABLE: &str =
"Java analysis unavailable: this build has no Java AST parser (feature `java-ast` is off, and it is not in the default feature set). Rebuild with --features java-ast. Reporting zero items would be indistinguishable from a Java file that genuinely declares none.";
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "path_exists")]
pub async fn analyze_java_file(
_file_path: &Path,
) -> anyhow::Result<Vec<crate::services::context::AstItem>> {
#[cfg(feature = "java-ast")]
{
use crate::services::languages::java::JavaAstVisitor;
use tokio::fs;
let source = fs::read_to_string(_file_path).await.map_err(|e| {
anyhow::anyhow!("Java analysis: cannot read {}: {}", _file_path.display(), e)
})?;
let visitor = JavaAstVisitor::new(_file_path);
visitor.analyze_java_source(&source).map_err(|e| {
anyhow::anyhow!(
"Java analysis: cannot parse {}: {}",
_file_path.display(),
e
)
})
}
#[cfg(not(feature = "java-ast"))]
Err(anyhow::anyhow!(
"{} (path: {})",
JAVA_AST_UNAVAILABLE,
_file_path.display()
))
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "path_exists")]
pub async fn analyze_csharp_file(
_file_path: &Path,
) -> anyhow::Result<Vec<crate::services::context::AstItem>> {
#[cfg(feature = "csharp-ast")]
{
use crate::services::languages::csharp::CSharpAstVisitor;
use tokio::fs;
match fs::read_to_string(_file_path).await {
Ok(source) => {
let visitor = CSharpAstVisitor::new(_file_path);
match visitor.analyze_csharp_source(&source) {
Ok(items) => Ok(items),
Err(_) => Ok(Vec::new()),
}
}
Err(_) => Ok(Vec::new()),
}
}
#[cfg(not(feature = "csharp-ast"))]
Ok(Vec::new())
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "path_exists")]
pub async fn analyze_swift_file(
_file_path: &Path,
) -> anyhow::Result<Vec<crate::services::context::AstItem>> {
#[cfg(feature = "swift-ast")]
{
Ok(Vec::new())
}
#[cfg(not(feature = "swift-ast"))]
Ok(Vec::new())
}
async fn analyze_wasm_file(
_file_path: &Path,
) -> anyhow::Result<Vec<crate::services::context::AstItem>> {
#[cfg(feature = "wasm-ast")]
{
use crate::services::languages::wasm::WasmModuleAnalyzer;
match tokio::fs::read_to_string(_file_path).await {
Ok(source) => {
let analyzer = WasmModuleAnalyzer::new(_file_path);
match analyzer.analyze_wat_text(&source) {
Ok(items) => Ok(items),
Err(_) => Ok(Vec::new()),
}
}
Err(_) => Ok(Vec::new()),
}
}
#[cfg(not(feature = "wasm-ast"))]
Ok(Vec::new())
}
#[cfg(test)]
mod java_failure_is_representable_tests {
use super::analyze_java_file;
#[cfg(not(feature = "java-ast"))]
#[tokio::test]
async fn without_java_ast_feature_refuses_instead_of_reporting_zero_items() {
let dir = tempfile::tempdir().unwrap();
let file = dir.path().join("Good.java");
std::fs::write(
&file,
"package com.example;\npublic class Good {\n public int add(int a, int b) { return a + b; }\n}\n",
)
.unwrap();
let err = analyze_java_file(&file)
.await
.expect_err("a build with no Java parser must refuse, not return an empty item list");
let msg = err.to_string();
assert!(
msg.contains("--features java-ast"),
"refusal must name the feature to rebuild with, got: {msg}"
);
assert!(
msg.contains("Java analysis unavailable"),
"refusal must say Java analysis is unavailable, got: {msg}"
);
}
#[cfg(feature = "java-ast")]
#[tokio::test]
async fn unparseable_java_is_an_error_not_an_empty_item_list() {
let dir = tempfile::tempdir().unwrap();
let file = dir.path().join("Broken.java");
std::fs::write(
&file,
"package com.example;\npublic class Broken {\n public int add(int a) { return a;\n",
)
.unwrap();
let err = analyze_java_file(&file)
.await
.expect_err("an unparseable Java file must not analyse as zero items");
assert!(
err.to_string().contains("cannot parse"),
"error must say the file could not be parsed, got: {err}"
);
}
#[cfg(feature = "java-ast")]
#[tokio::test]
async fn parseable_java_still_yields_items() {
let dir = tempfile::tempdir().unwrap();
let file = dir.path().join("Good.java");
std::fs::write(
&file,
"package com.example;\npublic class Good {\n public int add(int a, int b) { return a + b; }\n}\n",
)
.unwrap();
let items = analyze_java_file(&file)
.await
.expect("valid Java must parse");
assert!(
!items.is_empty(),
"a class with a method must yield AST items"
);
}
#[cfg(feature = "java-ast")]
#[tokio::test]
async fn unreadable_java_is_an_error_not_an_empty_item_list() {
let dir = tempfile::tempdir().unwrap();
let missing = dir.path().join("Absent.java");
let err = analyze_java_file(&missing)
.await
.expect_err("an unreadable Java file must not analyse as zero items");
assert!(
err.to_string().contains("cannot read"),
"error must say the file could not be read, got: {err}"
);
}
}