Repository: PlasmaFAIR/fortitude (language: python)

Issue:
Suggest that statement functions be made into internal functions
Periodically on Fortran Discourse, most recently at https://fortran-lang.discourse.group/t/anonymous-functions/8903, someone new to Fortran asks what some code with statement functions is doing. They are now obsolescent and should be warned about, as in the code

```Fortran
program mwe1
    implicit none
    double precision :: f,x
    f(x)= x**2 + 1.0d0
    print *, f(2.0d0)
end program mwe1
```

for which fortitude says only

```
xstatement_func.f90:3:5: P011 prefer 'real(real64)' to 'double precision' (see 'iso_fortran_env')
  |
1 | program mwe1
2 |     implicit none
3 |     double precision :: f,x
  |     ^^^^^^^^^^^^^^^^ P011
4 |     f(x)= x**2 + 1.0d0
5 |     print *, f(2.0d0)
  |


fortitude: 1 files scanned.
Number of errors: 1
```


Discussion / hints:
Definitely! I think we'll likely add a whole category for obsolescent features, including label `do` loops and computed go-tos

Fix patch:
diff --git a/fortitude/src/registry.rs b/fortitude/src/registry.rs
index 947bce5..9b8aa00 100644
--- a/fortitude/src/registry.rs
+++ b/fortitude/src/registry.rs
@@ -65,6 +65,9 @@ pub enum Category {
     /// Check path names, directory structures, etc.
     #[prefix = "F"]
     Filesystem,
+    /// Obsolescent features
+    #[prefix = "OB"]
+    Obsolescent,
 }
 
 pub trait RuleNamespace: Sized {
diff --git a/fortitude/src/rules/mod.rs b/fortitude/src/rules/mod.rs
index 5116d01..8696dcc 100644
--- a/fortitude/src/rules/mod.rs
+++ b/fortitude/src/rules/mod.rs
@@ -5,6 +5,7 @@ mod filesystem;
 #[macro_use]
 mod macros;
 mod modules;
+mod obsolescent;
 mod precision;
 mod style;
 mod testing;
@@ -93,6 +94,8 @@ pub fn code_to_rule(category: Category, code: &str) -> Option<(RuleGroup, Rule)>
         (Typing, "043") => (RuleGroup::Stable, Ast, typing::assumed_size::DeprecatedAssumedSizeCharacter),
         (Typing, "051") => (RuleGroup::Stable, Ast, typing::init_decls::InitialisationInDeclaration),
 
+        (Obsolescent, "001") => (RuleGroup::Stable, Ast, obsolescent::statement_functions::StatementFunction),
+
         (Precision, "001") => (RuleGroup::Stable, Ast, precision::kind_suffixes::NoRealSuffix),
         (Precision, "011") => (RuleGroup::Stable, Ast, precision::double_precision::DoublePrecision),
         (Precision, "021") => (RuleGroup::Stable, Ast, precision::implicit_kinds::ImplicitRealKind),
diff --git a/fortitude/src/rules/obsolescent/mod.rs b/fortitude/src/rules/obsolescent/mod.rs
new file mode 100644
index 0000000..f8d7860
--- /dev/null
+++ b/fortitude/src/rules/obsolescent/mod.rs
@@ -0,0 +1,27 @@
+pub mod statement_functions;
+
+#[cfg(test)]
+mod tests {
+    use std::convert::AsRef;
+    use std::path::Path;
+
+    use anyhow::Result;
+    use insta::assert_snapshot;
+    use test_case::test_case;
+
+    use crate::registry::Rule;
+    use crate::settings::Settings;
+    use crate::test::test_path;
+
+    #[test_case(Rule::StatementFunction, Path::new("OB001.f90"))]
+    fn rules(rule_code: Rule, path: &Path) -> Result<()> {
+        let snapshot = format!("{}_{}", rule_code.as_ref(), path.to_string_lossy());
+        let diagnostics = test_path(
+            Path::new("obsolescent").join(path).as_path(),
+            &[rule_code],
+            &Settings::default(),
+        )?;
+        assert_snapshot!(snapshot, diagnostics);
+        Ok(())
+    }
+}
diff --git a/fortitude/src/rules/obsolescent/statement_functions.rs b/fortitude/src/rules/obsolescent/statement_functions.rs
new file mode 100644
index 0000000..5a190b1
--- /dev/null
+++ b/fortitude/src/rules/obsolescent/statement_functions.rs
@@ -0,0 +1,52 @@
+use crate::settings::Settings;
+use crate::{AstRule, FromAstNode};
+use ruff_diagnostics::{Diagnostic, Violation};
+use ruff_macros::{derive_message_formats, violation};
+use ruff_source_file::SourceFile;
+use tree_sitter::Node;
+
+/// ## What it does
+/// Checks for statement functions.
+///
+/// ## Why is this bad?
+/// Statement functions are an obsolescent feature from Fortran 77,
+/// and have been entirely supplanted by internal
+/// procedures. Statement functions are much more limited in what they
+/// can do. They were declared obsolescent in Fortran 90 and removed
+/// in Fortran 95.
+///
+/// ## Examples
+/// Statement functions are easily replaced with internal procedures:
+///
+/// ```f90
+/// real :: f, x
+/// f(x) = x**2 + x
+/// ```
+/// becomes:
+///
+/// ```f90
+/// contains
+///   real function f(x)
+///     real, intent(in) :: x
+///     f = x**2 + x
+///   end function f
+/// ```
+#[violation]
+pub struct StatementFunction {}
+
+impl Violation for StatementFunction {
+    #[derive_message_formats]
+    fn message(&self) -> String {
+        format!("statement functions are obsolescent, prefer internal functions")
+    }
+}
+
+impl AstRule for StatementFunction {
+    fn check(_settings: &Settings, node: &Node, _src: &SourceFile) -> Option<Vec<Diagnostic>> {
+        some_vec![Diagnostic::from_node(StatementFunction {}, node)]
+    }
+
+    fn entrypoints() -> Vec<&'static str> {
+        vec!["statement_function"]
+    }
+}


Test patch:
diff --git a/fortitude/resources/test/fixtures/obsolescent/OB001.f90 b/fortitude/resources/test/fixtures/obsolescent/OB001.f90
new file mode 100644
index 0000000..0970789
--- /dev/null
+++ b/fortitude/resources/test/fixtures/obsolescent/OB001.f90
@@ -0,0 +1,15 @@
+      SUBROUTINE CEBCHVXX( THRESH, PATH )
+      CHARACTER FACT
+      COMPLEX            ZDUM
+!     .. Statement Functions ..
+      REAL               CABS1
+!     ..
+!     .. Statement Function Definitions ..
+      CABS1( ZDUM ) = ABS( REAL( ZDUM ) ) + ABS( AIMAG( ZDUM ) )
+
+!     .. Parameters ..
+      INTEGER            NWISE_I, CWISE_I
+      PARAMETER          (NWISE_I = 1, CWISE_I = 1)
+
+      FACT = 'E'
+      END SUBROUTINE CEBCHVXX
diff --git a/fortitude/src/rules/obsolescent/snapshots/fortitude__rules__obsolescent__tests__statement-function_OB001.f90.snap b/fortitude/src/rules/obsolescent/snapshots/fortitude__rules__obsolescent__tests__statement-function_OB001.f90.snap
new file mode 100644
index 0000000..d32b28a
--- /dev/null
+++ b/fortitude/src/rules/obsolescent/snapshots/fortitude__rules__obsolescent__tests__statement-function_OB001.f90.snap
@@ -0,0 +1,14 @@
+---
+source: fortitude/src/rules/obsolescent/mod.rs
+expression: diagnostics
+snapshot_kind: text
+---
+./resources/test/fixtures/obsolescent/OB001.f90:8:7: OB001 statement functions are obsolescent, prefer internal functions
+   |
+ 6 | !     ..
+ 7 | !     .. Statement Function Definitions ..
+ 8 |       CABS1( ZDUM ) = ABS( REAL( ZDUM ) ) + ABS( AIMAG( ZDUM ) )
+   |       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ OB001
+ 9 |
+10 | !     .. Parameters ..
+   |