Repository: fortran-lang/fortls (language: python)

Issue:
Erroneous diagnostic error with "recursive" argument in arg list
**Describe the bug**
False positive diagnostic error

**To Reproduce**
`fortls --debug_filepath literals.f90 --debug_rootpath . --debug_diagnostics`

```fortran
program test_literals
    implicit none
    contains
    subroutine foo(recursive, ierr)
        integer, intent(in) :: recursive
        integer, intent(out) :: ierr
        print*, recursive
    end subroutine foo
end program test_literals
```

**Expected behavior**
No diagnostic error

**Screenshots & Animations**
If applicable, add screenshots or GIF/MP4 animations to help explain your problem.

**Setup information (please complete the following information):**
 - OS: Linux
 - Python Version 3.8
 - fortls Version 2.10.0
 - Code editor used: VS Code



Discussion / hints:

Fix patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6811cf24..ef7281a3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,13 @@
 
 ## Unreleased
 
+## 2.11.0
+
+### Fixed
+
+- Fixed bug thorowing diagnostic errors if arguments were named `pure`, `elemental`, etc.
+  ([#157](https://github.com/gnikit/fortls/issues/157))
+
 ## 2.10.0
 
 ### Fixed
diff --git a/fortls/regex_patterns.py b/fortls/regex_patterns.py
index be150100..9df8f20b 100644
--- a/fortls/regex_patterns.py
+++ b/fortls/regex_patterns.py
@@ -15,7 +15,11 @@ class FortranRegularExpressions:
     INCLUDE: Pattern = compile(r"[ ]*INCLUDE[ :]*[\'\"]([^\'\"]*)", I)
     CONTAINS: Pattern = compile(r"[ ]*(CONTAINS)[ ]*$", I)
     IMPLICIT: Pattern = compile(r"[ ]*IMPLICIT[ ]+([a-z]*)", I)
-    SUB_MOD: Pattern = compile(r"[ ]*\b(PURE|IMPURE|ELEMENTAL|RECURSIVE)\b", I)
+    #: Parse procedure keywords but not if they start with , or ( or end with , or )
+    #: This is to avoid parsing as keywords variables named pure, impure, etc.
+    SUB_MOD: Pattern = compile(
+        r"[ ]*(?!<[,\()][ ]*)\b(PURE|IMPURE|ELEMENTAL|RECURSIVE)\b(?![,\)][ ]*)", I
+    )
     SUB: Pattern = compile(r"[ ]*SUBROUTINE[ ]+(\w+)", I)
     END_SUB: Pattern = compile(r"SUBROUTINE", I)
     FUN: Pattern = compile(r"[ ]*FUNCTION[ ]+(\w+)", I)


Test patch:
diff --git a/test/test_server_diagnostics.py b/test/test_server_diagnostics.py
index d6da6da4..f8dc7ada 100644
--- a/test/test_server_diagnostics.py
+++ b/test/test_server_diagnostics.py
@@ -404,3 +404,15 @@ def test_submodule_scopes():
     errcode, results = run_request(string, ["-n", "1"])
     assert errcode == 0
     assert results[1]["diagnostics"] == []
+
+
+def test_keyword_arg_list_var_names():
+    """Test argument list variables named as keywords are correctly parsed."""
+    string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir / "diag")})
+    file_path = str(test_dir / "diag" / "test_function_arg_list.f90")
+    string += write_rpc_notification(
+        "textDocument/didOpen", {"textDocument": {"uri": file_path}}
+    )
+    errcode, results = run_request(string, ["-n", "1"])
+    assert errcode == 0
+    assert results[1]["diagnostics"] == []
diff --git a/test/test_source/diag/test_function_arg_list.f90 b/test/test_source/diag/test_function_arg_list.f90
new file mode 100644
index 00000000..0f5025be
--- /dev/null
+++ b/test/test_source/diag/test_function_arg_list.f90
@@ -0,0 +1,26 @@
+program test_arg_names_as_keywords
+    implicit none
+    integer, parameter :: impure = 8
+contains
+    subroutine foo(recursive, ierr)
+        integer, intent(in) :: recursive
+        integer, intent(out) :: ierr
+        print*, recursive
+    end subroutine foo
+    real(8) impure elemental function foo2(recursive, elemental) result(pure)
+        integer, intent(in) :: recursive, elemental
+    end function foo2
+    real( kind = impure ) pure elemental function foo3(recursive) result(pure)
+        integer, intent(in) :: recursive
+    end function foo3
+    subroutine foo4(&
+        recursive, &
+        ierr)
+        integer, intent(in) :: recursive
+        integer, intent(out) :: ierr
+        print*, recursive
+    end subroutine foo4
+    pure real(impure) function foo5(recursive) result(val)
+        integer, intent(in) :: recursive
+    end function foo5
+end program test_arg_names_as_keywords