3
3
import * as path from 'path' ;
4
4
import * as cp from 'child_process' ;
5
5
import which from 'which' ;
6
-
6
+ import * as semver from 'semver' ;
7
7
import * as vscode from 'vscode' ;
8
+
8
9
import { Logger } from '../services/logging' ;
9
10
import {
10
11
EXTENSION_ID ,
@@ -19,10 +20,13 @@ import { RescanLint } from './commands';
19
20
import { GlobPaths } from '../lib/glob-paths' ;
20
21
21
22
export class LinterSettings {
23
+ private _modernGNU : boolean ;
24
+ private _version : string ;
22
25
private config : vscode . WorkspaceConfiguration ;
23
26
24
27
constructor ( private logger : Logger = new Logger ( ) ) {
25
28
this . config = vscode . workspace . getConfiguration ( EXTENSION_ID ) ;
29
+ this . GNUVersion ( this . compiler ) ; // populates version & modernGNU
26
30
}
27
31
public update ( event : vscode . ConfigurationChangeEvent ) {
28
32
console . log ( 'update settings' ) ;
@@ -50,6 +54,49 @@ export class LinterSettings {
50
54
public get modOutput ( ) : string {
51
55
return this . config . get < string > ( 'linter.modOutput' ) ;
52
56
}
57
+
58
+ // END OF API SETTINGS
59
+
60
+ /**
61
+ * Returns the version of the compiler and populates the internal variables
62
+ * `modernGNU` and `version`.
63
+ * @note Only supports `gfortran`
64
+ */
65
+ private GNUVersion ( compiler : string ) : string | undefined {
66
+ // Only needed for gfortran's diagnostics flag
67
+ this . modernGNU = false ;
68
+ if ( compiler !== 'gfortran' ) return ;
69
+ const child = cp . spawnSync ( compiler , [ '--version' ] ) ;
70
+ if ( child . error || child . status !== 0 ) {
71
+ this . logger . error ( `[lint] Could not spawn ${ compiler } to check version.` ) ;
72
+ return ;
73
+ }
74
+ const regex = / ^ G N U F o r t r a n \( [ \w . - ] + \) (?< version > .* ) $ / gm;
75
+ const version = regex . exec ( child . stdout . toString ( ) . trim ( ) ) . groups [ 'version' ] ;
76
+ if ( semver . valid ( version ) ) {
77
+ this . version = version ;
78
+ this . logger . info ( `[lint] Found GNU Fortran version ${ version } ` ) ;
79
+ this . logger . debug ( `[lint] Using Modern GNU Fortran diagnostics: ${ this . modernGNU } ` ) ;
80
+ return version ;
81
+ } else {
82
+ this . logger . error ( `[lint] invalid compiler version: ${ version } ` ) ;
83
+ }
84
+ }
85
+
86
+ public get version ( ) : string {
87
+ return this . _version ;
88
+ }
89
+ private set version ( version : string ) {
90
+ this . _version = version ;
91
+ this . modernGNU = semver . gte ( version , '11.0.0' ) ;
92
+ }
93
+ public get modernGNU ( ) : boolean {
94
+ return this . _modernGNU ;
95
+ }
96
+ private set modernGNU ( modernGNU : boolean ) {
97
+ this . _modernGNU = modernGNU ;
98
+ }
99
+
53
100
// FYPP options
54
101
55
102
public get fyppEnabled ( ) : boolean {
@@ -365,6 +412,7 @@ export class FortranLintingProvider {
365
412
const matches = [ ...msg . matchAll ( regex ) ] ;
366
413
switch ( this . compiler ) {
367
414
case 'gfortran' :
415
+ if ( this . settings . modernGNU ) return this . linterParserGCCPlainText ( matches ) ;
368
416
return this . linterParserGCC ( matches ) ;
369
417
370
418
case 'ifx' :
@@ -419,6 +467,42 @@ export class FortranLintingProvider {
419
467
return diagnostics ;
420
468
}
421
469
470
+ private linterParserGCCPlainText ( matches : RegExpMatchArray [ ] ) : vscode . Diagnostic [ ] {
471
+ const diagnostics : vscode . Diagnostic [ ] = [ ] ;
472
+ for ( const m of matches ) {
473
+ const g = m . groups ;
474
+ // m[0] is the entire match and then the captured groups follow
475
+ const lineNo : number = parseInt ( g [ 'ln' ] ) ;
476
+ const colNo : number = parseInt ( g [ 'cn' ] ) ;
477
+ const msgSev : string = g [ 'sev' ] ;
478
+ const msg : string = g [ 'msg' ] ;
479
+
480
+ const range = new vscode . Range (
481
+ new vscode . Position ( lineNo - 1 , colNo ) ,
482
+ new vscode . Position ( lineNo - 1 , colNo )
483
+ ) ;
484
+
485
+ let severity : vscode . DiagnosticSeverity ;
486
+ switch ( msgSev . toLowerCase ( ) ) {
487
+ case 'error' :
488
+ case 'fatal error' :
489
+ severity = vscode . DiagnosticSeverity . Error ;
490
+ break ;
491
+ case 'warning' :
492
+ severity = vscode . DiagnosticSeverity . Warning ;
493
+ break ;
494
+ case 'info' : // gfortran does not produce info AFAIK
495
+ severity = vscode . DiagnosticSeverity . Information ;
496
+ break ;
497
+ default :
498
+ severity = vscode . DiagnosticSeverity . Error ;
499
+ break ;
500
+ }
501
+ diagnostics . push ( new vscode . Diagnostic ( range , msg , severity ) ) ;
502
+ }
503
+ return diagnostics ;
504
+ }
505
+
422
506
private linterParserIntel ( matches : RegExpMatchArray [ ] ) : vscode . Diagnostic [ ] {
423
507
const diagnostics : vscode . Diagnostic [ ] = [ ] ;
424
508
for ( const m of matches ) {
@@ -529,6 +613,16 @@ export class FortranLintingProvider {
529
613
-------------------------------------------------------------------------
530
614
*/
531
615
case 'gfortran' :
616
+ /**
617
+ -----------------------------------------------------------------------
618
+ COMPILER: MESSAGE ANATOMY:
619
+ file:line:column: Severity: msg
620
+ -----------------------------------------------------------------------
621
+ see https://regex101.com/r/73TZQn/1
622
+ */
623
+ if ( this . settings . modernGNU ) {
624
+ return / (?< fname > (?: \w : \\ ) ? .* ) : (?< ln > \d + ) : (?< cn > \d + ) : (?< sev > E r r o r | W a r n i n g | F a t a l E r r o r ) : (?< msg > .* ) / g;
625
+ }
532
626
// see https://regex101.com/r/hZtk3f/1
533
627
return / (?: ^ (?< fname > (?: \w : \\ ) ? .* ) : (?< ln > \d + ) : (?< cn > \d + ) : (?: \s + .* \s + .* ?\s + ) (?< sev1 > E r r o r | W a r n i n g | F a t a l E r r o r ) : \s (?< msg1 > .* ) $ ) | (?: ^ (?< bin > \w + ) : \s * (?< sev2 > \w + \s * \w * ) : \s * (?< msg2 > .* ) $ ) / gm;
534
628
@@ -574,6 +668,9 @@ export class FortranLintingProvider {
574
668
switch ( compiler ) {
575
669
case 'flang' :
576
670
case 'gfortran' :
671
+ if ( this . settings . modernGNU ) {
672
+ return [ '-fsyntax-only' , '-cpp' , '-fdiagnostics-plain-output' ] ;
673
+ }
577
674
return [ '-fsyntax-only' , '-cpp' , '-fdiagnostics-show-option' ] ;
578
675
579
676
// ifort theoretically supports fsyntax-only too but I had trouble
0 commit comments