public SourceLocation ResolveSourceLocation()

in src/Analyzer.BicepProcessor/BicepSourceLocationResolver.cs [46:95]


        public SourceLocation ResolveSourceLocation(string pathInExpandedTemplate)
        {
            var jsonLine = this.jsonLineNumberResolver.ResolveSourceLocation(pathInExpandedTemplate).LineNumber;

            // Source map line numbers from Bicep are 0-indexed
            jsonLine--;

            // Find the most specific match in source map by getting the match with min matchSize
            var bestMatch = metadata.SourceMap.Entries
                .Select(sourceFile =>
                {
                    var match = sourceFile.SourceMap.FirstOrDefault(mapping => mapping.TargetLine == jsonLine, new(-1,-1));
                    // matchSize reflects how many other ARM lines have the same Bicep source line, for example
                    // a Bicep source line that points to a module will have a larger matchSize as it maps
                    // to all the other lines of the nested template, but a direct ARM mapping will be smaller
                    var matchSize = match.TargetLine < 0
                        ? int.MaxValue
                        : sourceFile.SourceMap.Count(mapping => mapping.SourceLine == match.SourceLine);
                    return (sourceFile.FilePath, match.SourceLine, matchSize);
                })
                .MinBy(tuple => tuple.matchSize);

            if (bestMatch.SourceLine < 0 )
            {
                return new SourceLocation(this.EntrypointFilePath, 0);
            }

            // Check if match is an ARM module reference, if so return location in that template
            var moduleMetadata = this.metadata.ModuleInfo.FirstOrDefault(info => info.FileName == bestMatch.FilePath);
            if (moduleMetadata != default && moduleMetadata.Modules.ContainsKey(bestMatch.SourceLine))
            {
                var modulePath = moduleMetadata.Modules[bestMatch.SourceLine];
                if (modulePath.EndsWith(".json") && File.Exists(modulePath))
                {
                    var templateContext = ArmTemplateContextCache.GetArmTemplateContext(modulePath);
                    var jsonSourceLocationResolver = new JsonSourceLocationResolver(templateContext);
                    var lineNumber = jsonSourceLocationResolver.ResolveSourceLocation(pathInExpandedTemplate)?.LineNumber;

                    // Fall back to location of module reference in parent file if failing to get line number
                    if (lineNumber != null)
                    {
                        return new SourceLocation(modulePath, lineNumber.Value);
                    }
                }
            }

            var entrypointFullPath = Path.GetDirectoryName(this.EntrypointFilePath);
            var matchFullFilePath = Path.GetFullPath(Path.Combine(entrypointFullPath, bestMatch.FilePath));
            return new SourceLocation(matchFullFilePath, bestMatch.SourceLine + 1); // convert line number back to 1-indexing
        }