private void GenerateCodeFlows(Defect defect, Result result) { List <SFA> sfas = defect?.Path?.SFAs; if (sfas == null || sfas.Count == 0) { return; } int step = 0; var locations = new List <CodeFlowLocation>(); bool pathUsesKeyEvents = defect.Path.SFAs.Any(x => !string.IsNullOrWhiteSpace(x?.KeyEvent?.Id)); foreach (var sfa in defect.Path.SFAs) { var region = new Region() { StartColumn = sfa.Column + 1, StartLine = sfa.Line }; var uri = new Uri($"{sfa.FilePath}{sfa.FileName}", UriKind.Relative); var fileLocation = new PhysicalLocation(id: 0, fileLocation: new FileLocation(uri: uri, uriBaseId: null), region: region, contextRegion: null); var codeFlowLocation = new CodeFlowLocation { Location = new Location { PhysicalLocation = fileLocation }, Step = ++step }; if (pathUsesKeyEvents) { if (string.IsNullOrWhiteSpace(sfa.KeyEvent?.Id)) { codeFlowLocation.Importance = CodeFlowLocationImportance.Unimportant; } else { codeFlowLocation.SetProperty("keyEventId", sfa.KeyEvent.Id); if (Enum.TryParse(sfa.KeyEvent.Importance, true, out CodeFlowLocationImportance importance)) { codeFlowLocation.Importance = importance; } if (!string.IsNullOrWhiteSpace(sfa.KeyEvent.Message) && codeFlowLocation.Location?.Message != null) { codeFlowLocation.Location.Message.Text = sfa.KeyEvent.Message; } } } locations.Add(codeFlowLocation); } result.CodeFlows = new List <CodeFlow>() { SarifUtilities.CreateSingleThreadedCodeFlow(locations) }; }
private void ProcessLine(string logFileLine, ref int nestingLevel, Result result) { var codeFlow = result.CodeFlows[0]; const int STEP = 0; const int URI = 1; const int LINE = 2; // const int IMPORTANCE = 3; This value not persisted to SARIF const int STATE = 4; const int KIND1 = 5; // When KIND1 == "Atomic" the 6th slot is the // the remainder of the kind id, e.g., Atomic Assigment const int KIND2 = 6; // When KIND1 == "Call" the 6th and 7th slots are: const int CALLER = 6; const int CALLEE = 7; int step; string[] tokens = logFileLine.Split(' '); if (int.TryParse(tokens[STEP], out step)) { // If we find a numeric value as the first token, // this is a general step. Uri uri = null; string uriText = tokens[URI].Trim('"'); if (!uriText.Equals("?", StringComparison.Ordinal)) { if (File.Exists(uriText)) { uriText = Path.GetFullPath(uriText); } uri = new Uri(uriText, UriKind.RelativeOrAbsolute); } // We assume a valid line here. This code will throw if not. int line = int.Parse(tokens[LINE]); string sdvKind = tokens[KIND1]; if (sdvKind.Equals("Atomic", StringComparison.Ordinal)) { // For multipart SDV kinds 'Atomic XXX', we // map using the second value only, e.g, // 'Assignment' or 'Conditional' sdvKind = tokens[KIND2]; } sdvKind = sdvKind.Trim(); var codeFlowLocation = new CodeFlowLocation { Step = step + 1, Importance = CodeFlowLocationImportance.Unimportant, Location = new Location { Message = new Message() } }; if (uri != null) { codeFlowLocation.Location.PhysicalLocation = new PhysicalLocation { FileLocation = new FileLocation { Uri = uri }, Region = new Region { StartLine = line } }; } if (sdvKind == "Call") { string extraMsg = $"{tokens[KIND1]} {tokens[CALLER]} {tokens[CALLEE]}"; string caller, callee; if (ExtractCallerAndCallee(extraMsg.Trim(), out caller, out callee)) { codeFlowLocation.Location.FullyQualifiedLogicalName = caller; codeFlowLocation.Location.Message.Text = callee; codeFlowLocation.SetProperty("target", callee); _callers.Push(caller); } else { Debug.Assert(false); } codeFlowLocation.NestingLevel = nestingLevel++; if (uri == null) { codeFlowLocation.Importance = CodeFlowLocationImportance.Unimportant; } else if (IsHarnessOrRulesFiles(uriText)) { codeFlowLocation.Importance = CodeFlowLocationImportance.Important; } else { codeFlowLocation.Importance = CodeFlowLocationImportance.Essential; } } else if (sdvKind == "Return") { Debug.Assert(_callers.Count > 0); codeFlowLocation.NestingLevel = nestingLevel--; codeFlowLocation.Location.FullyQualifiedLogicalName = _callers.Pop(); } else { codeFlowLocation.NestingLevel = nestingLevel; codeFlowLocation.Location.Message.Text = sdvKind; } string separatorText = "^====Auto====="; string state = tokens[STATE]; string[] stateTokens = state.Split(new string[] { separatorText }, StringSplitOptions.RemoveEmptyEntries); if (stateTokens.Length > 0) { if (stateTokens.Length == 2) { codeFlowLocation.SetProperty("currentDataValues", stateTokens[0]); codeFlowLocation.SetProperty("permanentDataValues", stateTokens[1]); } else { Debug.Assert(stateTokens.Length == 1); if (stateTokens[0].StartsWith(separatorText)) { codeFlowLocation.SetProperty("permanentDataValues", stateTokens[0]); } else { codeFlowLocation.SetProperty("currentDataValues", stateTokens[0]); } } } codeFlow.ThreadFlows[0].Locations.Add(codeFlowLocation); } else { // This is the defect message. const int LEVEL = 0; string levelText = tokens[LEVEL]; result.Level = ConvertToResultLevel(levelText); // Everything on the line following defect level comprises the message result.Message = new Message { Text = logFileLine.Substring(levelText.Length).Trim() }; // SDV currently produces 'pass' notifications when // the final line is prefixed with 'Error'. We'll examine // the message text to detect this condition if (result.Message.Text.Contains("is satisfied")) { result.Level = ResultLevel.Pass; } // Finally, populate this result location with the // last observed location in the code flow. IList <CodeFlowLocation> locations = result.CodeFlows[0].ThreadFlows[0].Locations; for (int i = locations.Count - 1; i >= 0; --i) { if (locations[i].Location?.PhysicalLocation != null) { result.Locations.Add(new Location { PhysicalLocation = locations[i].Location.PhysicalLocation }); break; } } } }