Skip to content

Commit

Permalink
Fixes bug with fortan line cont and preproc
Browse files Browse the repository at this point in the history
Fixes Preprocessor statements in declarations break parsing #203
Fixes Preprocessor statements in declarations break parsing #4
  • Loading branch information
gnikit committed Jan 17, 2022
1 parent 052f24d commit f86801d
Show file tree
Hide file tree
Showing 7 changed files with 32 additions and 8 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@
([gnikit/fortls#16](https://github.com/gnikit/fortls/issues/16))
- Fixes `END FORALL` end of scope error
([gnikit/fortls#18](https://github.com/gnikit/fortls/issues/18))
- Fixes Fortran line continuation definitions intermingled with preprocessor directives
([#203](https://github.com/hansec/fortran-language-server/issues/203))
([gnikit/fortls#4](https://github.com/gnikit/fortls/issues/4))

## 1.16.0

Expand Down
19 changes: 12 additions & 7 deletions fortls/parse_fortran.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@
NAT_VAR_REGEX,
NON_DEF_REGEX,
PARAMETER_VAL_REGEX,
PP_ANY_REGEX,
PP_DEF_REGEX,
PP_INCLUDE_REGEX,
PP_REGEX,
Expand Down Expand Up @@ -978,7 +979,7 @@ def get_code_line(
line_ind -= 1
else: # Free format file
opt_cont_match = FREE_CONT_REGEX.match(curr_line)
if opt_cont_match is not None:
if opt_cont_match:
curr_line = (
" " * opt_cont_match.end(0) + curr_line[opt_cont_match.end(0) :]
)
Expand All @@ -989,7 +990,7 @@ def get_code_line(
tmp_no_comm = tmp_line.split("!")[0]
cont_ind = tmp_no_comm.rfind("&")
opt_cont_match = FREE_CONT_REGEX.match(tmp_no_comm)
if opt_cont_match is not None:
if opt_cont_match:
if cont_ind == opt_cont_match.end(0) - 1:
break
tmp_no_comm = (
Expand Down Expand Up @@ -1022,21 +1023,27 @@ def get_code_line(
if iComm < 0:
iComm = iAmper + 1
next_line = ""
# Read the next line if needed
while (iAmper >= 0) and (iAmper < iComm):
if line_ind == line_number + 1:
curr_line = curr_line[:iAmper]
elif next_line != "":
post_lines[-1] = next_line[:iAmper]
next_line = self.get_line(line_ind, pp_content)
line_ind += 1
# Skip any preprocessor statements when seeking the next line
if PP_ANY_REGEX.match(next_line):
next_line = ""
post_lines.append("")
continue
# Skip empty or comment lines
match = FREE_COMMENT_LINE_MATCH.match(next_line)
if (next_line.rstrip() == "") or (match is not None):
if next_line.rstrip() == "" or match:
next_line = ""
post_lines.append("")
continue
opt_cont_match = FREE_CONT_REGEX.match(next_line)
if opt_cont_match is not None:
if opt_cont_match:
next_line = (
" " * opt_cont_match.end(0)
+ next_line[opt_cont_match.end(0) :]
Expand All @@ -1056,9 +1063,7 @@ def get_code_line(
def strip_comment(self, line: str) -> str:
"""Strip comment from line"""
if self.fixed:
if (FIXED_COMMENT_LINE_MATCH.match(line) is not None) and (
FIXED_OPENMP_MATCH.match(line) is not None
):
if FIXED_COMMENT_LINE_MATCH.match(line) and FIXED_OPENMP_MATCH.match(line):
return ""
else:
if FREE_OPENMP_MATCH.match(line) is None:
Expand Down
1 change: 1 addition & 0 deletions fortls/regex_patterns.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@
PP_DEF_REGEX = re.compile(r"#(define|undef)[ ]*([\w]+)(\((\w+(,[ ]*)?)+\))?", re.I)
PP_DEF_TEST_REGEX = re.compile(r"(![ ]*)?defined[ ]*\([ ]*([a-z0-9_]*)[ ]*\)$", re.I)
PP_INCLUDE_REGEX = re.compile(r"#include[ ]*([\"a-z0-9_\.]*)", re.I)
PP_ANY_REGEX = re.compile(r"(^#:?\w+)")
# Context matching rules
CALL_REGEX = re.compile(r"[ ]*CALL[ ]+[a-z0-9_%]*$", re.I)
INT_STMNT_REGEX = re.compile(r"^[ ]*[a-z]*$", re.I)
Expand Down
3 changes: 3 additions & 0 deletions test/test_preproc.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ def check_return(result_array, checks):
string += hover_req(file_path, 7, 40) # multi-lin variable
string += hover_req(file_path, 8, 7) # function with if conditional
string += hover_req(file_path, 9, 7) # multiline function with if conditional
file_path = os.path.join(test_dir, "pp", "preproc_keywords.F90")
string += hover_req(file_path, 6, 2) # ignores PP across Fortran line continuations
errcode, results = run_request(string, f" --config={root_dir}/.pp_conf.json")
assert errcode == 0

Expand All @@ -44,6 +46,7 @@ def check_return(result_array, checks):
"#define varVar 55",
"#define ewrite if (priority <= 3) write((priority), format)",
"#define ewrite2 if (priority <= 3) write((priority), format)",
"REAL, CONTIGUOUS, POINTER, DIMENSION(:)",
)
assert len(ref_results) == len(results) - 1
check_return(results[1:], ref_results)
1 change: 1 addition & 0 deletions test/test_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ def check_return(result_array):
["test_int", 2, 0],
["test_mod", 2, 0],
["test_nonint_mod", 2, 0],
["test_preproc_keywords", 2, 0],
["test_program", 2, 0],
["test_rename_sub", 6, 9],
["test_select", 2, 0],
Expand Down
3 changes: 2 additions & 1 deletion test/test_source/pp/.pp_conf.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@
"enable_code_actions": true,
"pp_suffixes": [".h", ".F90"],
"incl_suffixes": [".h"],
"include_dirs": ["include"]
"include_dirs": ["include"],
"pp_defs": { "HAVE_CONTIGUOUS": "" }
}
10 changes: 10 additions & 0 deletions test/test_source/pp/preproc_keywords.F90
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
program test_preproc_keywords
REAL &
#ifdef HAVE_CONTIGUOUS
, CONTIGUOUS &
#endif
, POINTER :: &
var1(:), &
var2(:)

end program test_preproc_keywords

0 comments on commit f86801d

Please sign in to comment.