mirror of
https://github.com/erg-lang/erg.git
synced 2025-09-28 20:14:45 +00:00
feat: check doc comments by the compiler
This commit is contained in:
parent
777af37de7
commit
0c47c95f25
4 changed files with 108 additions and 6 deletions
|
@ -178,9 +178,9 @@ pub fn trim_eliminate_top_indent(code: String) -> String {
|
||||||
let mut result = String::new();
|
let mut result = String::new();
|
||||||
for line in code.lines() {
|
for line in code.lines() {
|
||||||
if line.len() > indent {
|
if line.len() > indent {
|
||||||
result.push_str(&line[indent..]);
|
result.push_str(line[indent..].trim_end());
|
||||||
} else {
|
} else {
|
||||||
result.push_str(line);
|
result.push_str(line.trim_end());
|
||||||
}
|
}
|
||||||
result.push('\n');
|
result.push('\n');
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,11 +6,17 @@
|
||||||
use erg_common::log;
|
use erg_common::log;
|
||||||
use erg_common::traits::{Locational, Runnable, Stream};
|
use erg_common::traits::{Locational, Runnable, Stream};
|
||||||
use erg_common::Str;
|
use erg_common::Str;
|
||||||
|
use erg_parser::ast::AST;
|
||||||
|
use erg_parser::build_ast::ASTBuilder;
|
||||||
|
|
||||||
use crate::ty::{HasType, Type};
|
use crate::context::ContextKind;
|
||||||
|
use crate::link_ast::ASTLinker;
|
||||||
|
use crate::ty::{HasType, Type, ValueObj, VisibilityModifier};
|
||||||
|
|
||||||
use crate::error::{LowerError, LowerResult, LowerWarning, LowerWarnings, SingleLowerResult};
|
use crate::error::{
|
||||||
use crate::hir;
|
CompileErrors, LowerError, LowerResult, LowerWarning, LowerWarnings, SingleLowerResult,
|
||||||
|
};
|
||||||
|
use crate::hir::{self, Expr, HIR};
|
||||||
use crate::lower::ASTLowerer;
|
use crate::lower::ASTLowerer;
|
||||||
use crate::varinfo::VarInfo;
|
use crate::varinfo::VarInfo;
|
||||||
|
|
||||||
|
@ -182,4 +188,95 @@ impl ASTLowerer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn check_doc_comments(&mut self, hir: &HIR) {
|
||||||
|
for chunk in hir.module.iter() {
|
||||||
|
self.check_doc_comment(chunk);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_doc_comment(&mut self, chunk: &Expr) {
|
||||||
|
match chunk {
|
||||||
|
Expr::Lit(lit) if lit.is_doc_comment() => {
|
||||||
|
let first_line = lit.ln_begin().unwrap_or(1);
|
||||||
|
let ValueObj::Str(content) = &lit.value else { return; };
|
||||||
|
if content.starts_with("erg\n") {
|
||||||
|
let code = content.trim_start_matches("erg\n");
|
||||||
|
let indent = code.chars().take_while(|c| c.is_whitespace()).count();
|
||||||
|
let code = if indent > 0 {
|
||||||
|
format!(
|
||||||
|
"{}_ =\n{code}\n{}None",
|
||||||
|
"\n".repeat(first_line as usize - 1),
|
||||||
|
" ".repeat(indent)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
format!("{}{code}", "\n".repeat(first_line as usize))
|
||||||
|
};
|
||||||
|
match ASTBuilder::new(self.cfg().clone()).build(code) {
|
||||||
|
Ok(ast) => {
|
||||||
|
self.check_doc_ast(ast);
|
||||||
|
}
|
||||||
|
Err(errs) => {
|
||||||
|
let errs = CompileErrors::from(errs);
|
||||||
|
self.errs.extend(errs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Expr::ClassDef(class_def) => {
|
||||||
|
for chunk in class_def.methods.iter() {
|
||||||
|
self.check_doc_comment(chunk);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Expr::PatchDef(patch_def) => {
|
||||||
|
for chunk in patch_def.methods.iter() {
|
||||||
|
self.check_doc_comment(chunk);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_doc_ast(&mut self, ast: AST) {
|
||||||
|
let Ok(ast) = ASTLinker::new(self.cfg().clone()).link(ast, "exec") else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
self.module.context.grow(
|
||||||
|
"<doc>",
|
||||||
|
ContextKind::Instant,
|
||||||
|
VisibilityModifier::Private,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
let mut module = hir::Module::with_capacity(ast.module.len());
|
||||||
|
if let Err(errs) = self.module.context.preregister(ast.module.block()) {
|
||||||
|
self.errs.extend(errs);
|
||||||
|
}
|
||||||
|
for chunk in ast.module.into_iter() {
|
||||||
|
match self.lower_chunk(chunk) {
|
||||||
|
Ok(chunk) => {
|
||||||
|
module.push(chunk);
|
||||||
|
}
|
||||||
|
Err(errs) => {
|
||||||
|
self.errs.extend(errs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.module.context.clear_invalid_vars();
|
||||||
|
self.module.context.check_decls().unwrap_or_else(|errs| {
|
||||||
|
self.errs.extend(errs);
|
||||||
|
});
|
||||||
|
let hir = HIR::new(ast.name, module);
|
||||||
|
let hir = match self.module.context.resolve(hir) {
|
||||||
|
Ok(hir) => hir,
|
||||||
|
Err((_, errs)) => {
|
||||||
|
self.errs.extend(errs);
|
||||||
|
self.module.context.pop();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
self.warn_unused_expr(&hir.module, "exec");
|
||||||
|
// self.warn_unused_vars("exec");
|
||||||
|
self.check_doc_comments(&hir);
|
||||||
|
self.module.context.pop();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2415,7 +2415,7 @@ impl ASTLowerer {
|
||||||
/// The meaning of TypeAscription changes between chunk and expr.
|
/// The meaning of TypeAscription changes between chunk and expr.
|
||||||
/// For example, `x: Int`, as expr, is `x` itself,
|
/// For example, `x: Int`, as expr, is `x` itself,
|
||||||
/// but as chunk, it declares that `x` is of type `Int`, and is valid even before `x` is defined.
|
/// but as chunk, it declares that `x` is of type `Int`, and is valid even before `x` is defined.
|
||||||
fn lower_chunk(&mut self, chunk: ast::Expr) -> LowerResult<hir::Expr> {
|
pub(crate) fn lower_chunk(&mut self, chunk: ast::Expr) -> LowerResult<hir::Expr> {
|
||||||
log!(info "entered {}", fn_name!());
|
log!(info "entered {}", fn_name!());
|
||||||
match chunk {
|
match chunk {
|
||||||
ast::Expr::Def(def) => Ok(hir::Expr::Def(self.lower_def(def)?)),
|
ast::Expr::Def(def) => Ok(hir::Expr::Def(self.lower_def(def)?)),
|
||||||
|
@ -2521,6 +2521,7 @@ impl ASTLowerer {
|
||||||
};
|
};
|
||||||
self.warn_unused_expr(&hir.module, mode);
|
self.warn_unused_expr(&hir.module, mode);
|
||||||
self.warn_unused_vars(mode);
|
self.warn_unused_vars(mode);
|
||||||
|
self.check_doc_comments(&hir);
|
||||||
if self.errs.is_empty() {
|
if self.errs.is_empty() {
|
||||||
log!(info "the AST lowering process has completed.");
|
log!(info "the AST lowering process has completed.");
|
||||||
Ok(CompleteArtifact::new(
|
Ok(CompleteArtifact::new(
|
||||||
|
|
|
@ -9,6 +9,10 @@ Point2D::
|
||||||
one = 1
|
one = 1
|
||||||
Point2D.
|
Point2D.
|
||||||
zero = Point2D::one - 1
|
zero = Point2D::one - 1
|
||||||
|
'''erg
|
||||||
|
p = Point2D.new {x = 1; y = 2}
|
||||||
|
assert p.norm() == 5
|
||||||
|
'''
|
||||||
norm self = self::x**2 + self::y**2
|
norm self = self::x**2 + self::y**2
|
||||||
|
|
||||||
Point3D = Inherit Point2D, Additional := {z = Int}
|
Point3D = Inherit Point2D, Additional := {z = Int}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue