lang: add more docs to macros to allow missing_doc (#1498)
This commit is contained in:
parent
905d26bb09
commit
de08fec0b9
|
@ -1,6 +1,7 @@
|
|||
use crate::{AccountField, AccountsStruct, Ty};
|
||||
use heck::SnakeCase;
|
||||
use quote::quote;
|
||||
use std::str::FromStr;
|
||||
|
||||
// Generates the private `__client_accounts` mod implementation, containing
|
||||
// a generated struct mapping 1-1 to the `Accounts` struct, except with
|
||||
|
@ -20,6 +21,11 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream {
|
|||
.map(|f: &AccountField| match f {
|
||||
AccountField::CompositeField(s) => {
|
||||
let name = &s.ident;
|
||||
let docs = if !s.docs.is_empty() {
|
||||
proc_macro2::TokenStream::from_str(&format!("#[doc = \"{}\"]", s.docs)).unwrap()
|
||||
} else {
|
||||
quote!()
|
||||
};
|
||||
let symbol: proc_macro2::TokenStream = format!(
|
||||
"__client_accounts_{0}::{1}",
|
||||
s.symbol.to_snake_case(),
|
||||
|
@ -28,12 +34,19 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream {
|
|||
.parse()
|
||||
.unwrap();
|
||||
quote! {
|
||||
#docs
|
||||
pub #name: #symbol
|
||||
}
|
||||
}
|
||||
AccountField::Field(f) => {
|
||||
let name = &f.ident;
|
||||
let docs = if !f.docs.is_empty() {
|
||||
proc_macro2::TokenStream::from_str(&format!("#[doc = \"{}\"]", f.docs)).unwrap()
|
||||
} else {
|
||||
quote!()
|
||||
};
|
||||
quote! {
|
||||
#docs
|
||||
pub #name: anchor_lang::solana_program::pubkey::Pubkey
|
||||
}
|
||||
}
|
||||
|
@ -99,6 +112,13 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream {
|
|||
})
|
||||
.collect()
|
||||
};
|
||||
|
||||
let struct_doc = proc_macro2::TokenStream::from_str(&format!(
|
||||
"#[doc = \" Generated client accounts for [`{}`].\"]",
|
||||
name
|
||||
))
|
||||
.unwrap();
|
||||
|
||||
quote! {
|
||||
/// An internal, Anchor generated module. This is used (as an
|
||||
/// implementation detail), to generate a struct for a given
|
||||
|
@ -114,6 +134,7 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream {
|
|||
use anchor_lang::prelude::borsh;
|
||||
#(#re_exports)*
|
||||
|
||||
#struct_doc
|
||||
#[derive(anchor_lang::AnchorSerialize)]
|
||||
pub struct #name {
|
||||
#(#account_struct_fields),*
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
use std::str::FromStr;
|
||||
|
||||
use crate::{AccountField, AccountsStruct, Ty};
|
||||
use heck::SnakeCase;
|
||||
use quote::quote;
|
||||
|
@ -20,6 +22,11 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream {
|
|||
.map(|f: &AccountField| match f {
|
||||
AccountField::CompositeField(s) => {
|
||||
let name = &s.ident;
|
||||
let docs = if !s.docs.is_empty() {
|
||||
proc_macro2::TokenStream::from_str(&format!("#[doc = \"{}\"]", s.docs)).unwrap()
|
||||
} else {
|
||||
quote!()
|
||||
};
|
||||
let symbol: proc_macro2::TokenStream = format!(
|
||||
"__cpi_client_accounts_{0}::{1}",
|
||||
s.symbol.to_snake_case(),
|
||||
|
@ -28,12 +35,19 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream {
|
|||
.parse()
|
||||
.unwrap();
|
||||
quote! {
|
||||
#docs
|
||||
pub #name: #symbol<'info>
|
||||
}
|
||||
}
|
||||
AccountField::Field(f) => {
|
||||
let name = &f.ident;
|
||||
let docs = if !f.docs.is_empty() {
|
||||
proc_macro2::TokenStream::from_str(&format!("#[doc = \"{}\"]", f.docs)).unwrap()
|
||||
} else {
|
||||
quote!()
|
||||
};
|
||||
quote! {
|
||||
#docs
|
||||
pub #name: anchor_lang::solana_program::account_info::AccountInfo<'info>
|
||||
}
|
||||
}
|
||||
|
@ -124,6 +138,11 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream {
|
|||
} else {
|
||||
quote! {<'info>}
|
||||
};
|
||||
let struct_doc = proc_macro2::TokenStream::from_str(&format!(
|
||||
"#[doc = \" Generated CPI struct of the accounts for [`{}`].\"]",
|
||||
name
|
||||
))
|
||||
.unwrap();
|
||||
quote! {
|
||||
/// An internal, Anchor generated module. This is used (as an
|
||||
/// implementation detail), to generate a CPI struct for a given
|
||||
|
@ -131,12 +150,13 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream {
|
|||
/// AccountInfo.
|
||||
///
|
||||
/// To access the struct in this module, one should use the sibling
|
||||
/// `cpi::accounts` module (also generated), which re-exports this.
|
||||
/// [`cpi::accounts`] module (also generated), which re-exports this.
|
||||
pub(crate) mod #account_mod_name {
|
||||
use super::*;
|
||||
|
||||
#(#re_exports)*
|
||||
|
||||
#struct_doc
|
||||
pub struct #name#generics {
|
||||
#(#account_struct_fields),*
|
||||
}
|
||||
|
|
|
@ -61,6 +61,7 @@ pub fn generate(error: Error) -> proc_macro2::TokenStream {
|
|||
#error_enum
|
||||
|
||||
impl #enum_name {
|
||||
/// Gets the name of this [#enum_name].
|
||||
pub fn name(&self) -> String {
|
||||
match self {
|
||||
#(#name_variant_dispatch),*
|
||||
|
|
|
@ -73,6 +73,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
|
|||
dispatch(program_id, accounts, data)
|
||||
}
|
||||
|
||||
/// Module representing the program.
|
||||
pub mod program {
|
||||
use super::*;
|
||||
|
||||
|
|
|
@ -207,6 +207,8 @@ pub struct Field {
|
|||
pub constraints: ConstraintGroup,
|
||||
pub instruction_constraints: ConstraintGroup,
|
||||
pub ty: Ty,
|
||||
/// Documentation string.
|
||||
pub docs: String,
|
||||
}
|
||||
|
||||
impl Field {
|
||||
|
@ -441,6 +443,8 @@ pub struct CompositeField {
|
|||
pub instruction_constraints: ConstraintGroup,
|
||||
pub symbol: String,
|
||||
pub raw_field: syn::Field,
|
||||
/// Documentation string.
|
||||
pub docs: String,
|
||||
}
|
||||
|
||||
// A type of an account field.
|
||||
|
|
|
@ -128,6 +128,21 @@ fn constraints_cross_checks(fields: &[AccountField]) -> ParseResult<()> {
|
|||
|
||||
pub fn parse_account_field(f: &syn::Field, has_instruction_api: bool) -> ParseResult<AccountField> {
|
||||
let ident = f.ident.clone().unwrap();
|
||||
let docs: String = f
|
||||
.attrs
|
||||
.iter()
|
||||
.map(|a| {
|
||||
let meta_result = a.parse_meta();
|
||||
if let Ok(syn::Meta::NameValue(meta)) = meta_result {
|
||||
if meta.path.is_ident("doc") {
|
||||
if let syn::Lit::Str(doc) = meta.lit {
|
||||
return format!(" {}\n", doc.value().trim());
|
||||
}
|
||||
}
|
||||
}
|
||||
"".to_string()
|
||||
})
|
||||
.collect::<String>();
|
||||
let account_field = match is_field_primitive(f)? {
|
||||
true => {
|
||||
let ty = parse_ty(f)?;
|
||||
|
@ -138,6 +153,7 @@ pub fn parse_account_field(f: &syn::Field, has_instruction_api: bool) -> ParseRe
|
|||
ty,
|
||||
constraints: account_constraints,
|
||||
instruction_constraints,
|
||||
docs,
|
||||
})
|
||||
}
|
||||
false => {
|
||||
|
@ -149,6 +165,7 @@ pub fn parse_account_field(f: &syn::Field, has_instruction_api: bool) -> ParseRe
|
|||
instruction_constraints,
|
||||
symbol: ident_string(f)?,
|
||||
raw_field: f.clone(),
|
||||
docs,
|
||||
})
|
||||
}
|
||||
};
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
[provider]
|
||||
cluster = "localnet"
|
||||
wallet = "~/.config/solana/id.json"
|
||||
|
||||
[programs.localnet]
|
||||
errors = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"
|
||||
|
||||
[scripts]
|
||||
test = "yarn run mocha -t 1000000 tests/"
|
||||
|
||||
[features]
|
|
@ -0,0 +1,4 @@
|
|||
[workspace]
|
||||
members = [
|
||||
"programs/*"
|
||||
]
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"name": "errors",
|
||||
"version": "0.22.0",
|
||||
"license": "(MIT OR Apache-2.0)",
|
||||
"homepage": "https://github.com/project-serum/anchor#readme",
|
||||
"bugs": {
|
||||
"url": "https://github.com/project-serum/anchor/issues"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/project-serum/anchor.git"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=11"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "anchor test"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
[package]
|
||||
name = "docs"
|
||||
version = "0.1.0"
|
||||
description = "Created with Anchor"
|
||||
edition = "2018"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "lib"]
|
||||
name = "errors"
|
||||
|
||||
[features]
|
||||
no-entrypoint = []
|
||||
cpi = ["no-entrypoint"]
|
||||
|
||||
[dependencies]
|
||||
anchor-lang = { path = "../../../../lang" }
|
|
@ -0,0 +1,2 @@
|
|||
[target.bpfel-unknown-unknown.dependencies.std]
|
||||
features = []
|
|
@ -0,0 +1,49 @@
|
|||
//! This example enforces the missing documentation lint.
|
||||
#![deny(missing_docs)]
|
||||
|
||||
use anchor_lang::prelude::*;
|
||||
|
||||
declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
|
||||
|
||||
/// Program for testing that the `missing_docs` lint can be applied.
|
||||
#[program]
|
||||
mod docs {
|
||||
use super::*;
|
||||
|
||||
/// Hello.
|
||||
pub fn hello(_ctx: Context<Hello>) -> Result<()> {
|
||||
err!(MyError::Hello)
|
||||
}
|
||||
}
|
||||
|
||||
/// Hello accounts.
|
||||
#[derive(Accounts)]
|
||||
pub struct Hello<'info> {
|
||||
/// Rent sysvar.
|
||||
/// Multi line docs.
|
||||
pub rent: Sysvar<'info, Rent>,
|
||||
/// Composite accounts test.
|
||||
/// Multiple lines supported.
|
||||
pub other: HelloComposite<'info>,
|
||||
}
|
||||
|
||||
/// Hello accounts.
|
||||
#[derive(Accounts)]
|
||||
pub struct HelloComposite<'info> {
|
||||
/// Rent sysvar 2.
|
||||
pub rent2: Sysvar<'info, Rent>,
|
||||
}
|
||||
|
||||
/// MyError.
|
||||
#[error_code]
|
||||
pub enum MyError {
|
||||
/// test
|
||||
#[msg("This is an error message clients will automatically display")]
|
||||
Hello,
|
||||
/// test2
|
||||
HelloNoMsg = 123,
|
||||
/// test3
|
||||
HelloNext,
|
||||
/// test4
|
||||
HelloCustom,
|
||||
}
|
Loading…
Reference in New Issue