@@ -128,11 +128,10 @@ func (cli *CLI) inspectModule(opts Options, dir string, filterFiles []string) (t
128128 }
129129
130130 // Setup runners
131- runners , err := cli .setupRunners (opts , dir )
131+ rootRunner , moduleRunners , err := cli .setupRunners (opts , dir )
132132 if err != nil {
133133 return issues , changes , err
134134 }
135- rootRunner := runners [len (runners )- 1 ]
136135
137136 // Launch plugin processes
138137 rulesetPlugin , err := launchPlugins (cli .config , opts .Fix )
@@ -176,16 +175,33 @@ By setting TFLINT_LOG=trace, you can confirm the changes made by the autofix and
176175 }
177176
178177 for name , ruleset := range rulesetPlugin .RuleSets {
179- for _ , runner := range runners {
180- err = ruleset .Check (plugin .NewGRPCServer (runner , rootRunner , cli .loader .Files (), sdkVersions [name ]))
178+ if err := ruleset .Check (plugin .NewGRPCServer (rootRunner , rootRunner , cli .loader .Files (), sdkVersions [name ])); err != nil {
179+ return issues , changes , fmt .Errorf ("Failed to check ruleset; %w" , err )
180+ }
181+ // Run checks for module calls are performed in parallel.
182+ // The rootRunner is shared between goroutines but read-only, so this is goroutine-safe.
183+ // Note that checks against the rootRunner are not parallelized, as autofix may cause the module to be rebuilt.
184+ ch := make (chan error , len (moduleRunners ))
185+ for _ , runner := range moduleRunners {
186+ if opts .NoParallelRunners {
187+ ch <- ruleset .Check (plugin .NewGRPCServer (runner , rootRunner , cli .loader .Files (), sdkVersions [name ]))
188+ } else {
189+ go func (runner * tflint.Runner ) {
190+ ch <- ruleset .Check (plugin .NewGRPCServer (runner , rootRunner , cli .loader .Files (), sdkVersions [name ]))
191+ }(runner )
192+ }
193+ }
194+ for i := 0 ; i < len (moduleRunners ); i ++ {
195+ err = <- ch
181196 if err != nil {
182197 return issues , changes , fmt .Errorf ("Failed to check ruleset; %w" , err )
183198 }
184199 }
200+ close (ch )
185201 }
186202
187203 changesInAttempt := map [string ][]byte {}
188- for _ , runner := range runners {
204+ for _ , runner := range append ( moduleRunners , rootRunner ) {
189205 for _ , issue := range runner .LookupIssues (filterFiles ... ) {
190206 // On the second attempt, only fixable issues are appended to avoid duplicates.
191207 if loop == 1 || issue .Fixable {
@@ -214,15 +230,15 @@ By setting TFLINT_LOG=trace, you can confirm the changes made by the autofix and
214230 return issues , changes , nil
215231}
216232
217- func (cli * CLI ) setupRunners (opts Options , dir string ) ([]* tflint.Runner , error ) {
233+ func (cli * CLI ) setupRunners (opts Options , dir string ) (* tflint. Runner , []* tflint.Runner , error ) {
218234 configs , diags := cli .loader .LoadConfig (dir , cli .config .CallModuleType )
219235 if diags .HasErrors () {
220- return []* tflint.Runner {}, fmt .Errorf ("Failed to load configurations; %w" , diags )
236+ return nil , []* tflint.Runner {}, fmt .Errorf ("Failed to load configurations; %w" , diags )
221237 }
222238
223239 files , diags := cli .loader .LoadConfigDirFiles (dir )
224240 if diags .HasErrors () {
225- return []* tflint.Runner {}, fmt .Errorf ("Failed to load configurations; %w" , diags )
241+ return nil , []* tflint.Runner {}, fmt .Errorf ("Failed to load configurations; %w" , diags )
226242 }
227243 annotations := map [string ]tflint.Annotations {}
228244 for path , file := range files {
@@ -234,30 +250,30 @@ func (cli *CLI) setupRunners(opts Options, dir string) ([]*tflint.Runner, error)
234250 annotations [path ] = ants
235251 }
236252 if diags .HasErrors () {
237- return []* tflint.Runner {}, fmt .Errorf ("Failed to load configurations; %w" , diags )
253+ return nil , []* tflint.Runner {}, fmt .Errorf ("Failed to load configurations; %w" , diags )
238254 }
239255
240256 variables , diags := cli .loader .LoadValuesFiles (dir , cli .config .Varfiles ... )
241257 if diags .HasErrors () {
242- return []* tflint.Runner {}, fmt .Errorf ("Failed to load values files; %w" , diags )
258+ return nil , []* tflint.Runner {}, fmt .Errorf ("Failed to load values files; %w" , diags )
243259 }
244260 cliVars , diags := terraform .ParseVariableValues (cli .config .Variables , configs .Module .Variables )
245261 if diags .HasErrors () {
246- return []* tflint.Runner {}, fmt .Errorf ("Failed to parse variables; %w" , diags )
262+ return nil , []* tflint.Runner {}, fmt .Errorf ("Failed to parse variables; %w" , diags )
247263 }
248264 variables = append (variables , cliVars )
249265
250266 runner , err := tflint .NewRunner (cli .originalWorkingDir , cli .config , annotations , configs , variables ... )
251267 if err != nil {
252- return []* tflint.Runner {}, fmt .Errorf ("Failed to initialize a runner; %w" , err )
268+ return nil , []* tflint.Runner {}, fmt .Errorf ("Failed to initialize a runner; %w" , err )
253269 }
254270
255- runners , err := tflint .NewModuleRunners (runner )
271+ moduleRunners , err := tflint .NewModuleRunners (runner )
256272 if err != nil {
257- return []* tflint.Runner {}, fmt .Errorf ("Failed to prepare rule checking; %w" , err )
273+ return nil , []* tflint.Runner {}, fmt .Errorf ("Failed to prepare rule checking; %w" , err )
258274 }
259275
260- return append ( runners , runner ) , nil
276+ return runner , moduleRunners , nil
261277}
262278
263279func launchPlugins (config * tflint.Config , fix bool ) (* plugin.Plugin , error ) {
0 commit comments