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
}