neo_solidity/solidity/validate/contract/
entry.rs1pub fn validate_contract(metadata: &ContractMetadata) -> Vec<Diagnostic> {
2 let mut diagnostics = Vec::new();
3
4 let method_name_counts = build_method_name_counts(metadata);
5 let constructor_count = validate_methods(metadata, &mut diagnostics);
6
7 if constructor_count > 1 {
8 diagnostics.push(Diagnostic::error(format!("multiple constructors defined ({} total)", constructor_count)));
9 }
10
11 validate_state_variables(metadata, &method_name_counts, &mut diagnostics);
12 validate_events(metadata, &mut diagnostics);
13 validate_return_types(metadata, &mut diagnostics);
14 validate_erc_nep_patterns(metadata, &mut diagnostics);
15 validate_using_directives(metadata, &mut diagnostics);
16 validate_type_definitions(metadata, &mut diagnostics);
17 validate_library(metadata, &mut diagnostics);
18 validate_abstract_contract(metadata, &mut diagnostics);
19 validate_flatten_warnings(metadata, &mut diagnostics);
20
21 diagnostics
22}
23
24fn validate_using_directives(metadata: &ContractMetadata, diagnostics: &mut Vec<Diagnostic>) {
25 if metadata.has_using_for_star {
26 diagnostics.push(
27 Diagnostic::warning(
28 "'using X for *' is supported; all library functions are available \
29 but type-specific filtering is not enforced at compile time"
30 )
31 .with_suggestion(
32 "this is functionally correct — library functions can be called \
33 as member functions on any type"
34 ),
35 );
36 }
37
38 if metadata.has_using_function_list {
39 diagnostics.push(
40 Diagnostic::warning(
41 "'using { f, g } for Type' is supported; all library functions are \
42 merged (selective filtering is not enforced at compile time)"
43 )
44 .with_suggestion(
45 "this is functionally correct — all library functions are available; \
46 unused functions are excluded during bytecode optimization"
47 ),
48 );
49 }
50}
51
52fn validate_type_definitions(_metadata: &ContractMetadata, _diagnostics: &mut Vec<Diagnostic>) {
53 }
56
57fn validate_abstract_contract(metadata: &ContractMetadata, diagnostics: &mut Vec<Diagnostic>) {
58 let unimplemented: Vec<&str> = metadata
59 .methods
60 .iter()
61 .filter(|m| {
62 matches!(m.kind, FunctionKind::Regular)
63 && m.body.is_none()
64 })
65 .map(|m| m.name.as_str())
66 .collect();
67
68 if unimplemented.is_empty() {
69 return;
70 }
71
72 if metadata.is_abstract {
73 diagnostics.push(
76 Diagnostic::warning(format!(
77 "abstract contract '{}' has {} unimplemented function(s): [{}]",
78 metadata.name,
79 unimplemented.len(),
80 unimplemented.join(", "),
81 ))
82 .with_suggestion(
83 "derived contracts must implement these functions or also be declared abstract"
84 ),
85 );
86 } else {
87 diagnostics.push(
89 Diagnostic::error(format!(
90 "contract '{}' has {} unimplemented function(s) [{}] but is not declared abstract; \
91 either provide implementations or declare the contract as 'abstract contract {}'",
92 metadata.name,
93 unimplemented.len(),
94 unimplemented.join(", "),
95 metadata.name,
96 ))
97 .with_suggestion(
98 "add 'abstract' before 'contract', or provide function bodies for all declared functions"
99 ),
100 );
101 }
102}
103
104fn validate_flatten_warnings(metadata: &ContractMetadata, diagnostics: &mut Vec<Diagnostic>) {
105 for warning in &metadata.flatten_warnings {
106 diagnostics.push(
107 Diagnostic::warning(warning.clone())
108 .with_code("W200")
109 .with_suggestion(
110 "mark the base function 'virtual' and the overriding function 'override'",
111 ),
112 );
113 }
114}