2020import com .sonar .sslr .api .RecognitionException ;
2121import java .io .File ;
2222import java .io .IOException ;
23- import java .util .ArrayDeque ;
2423import java .util .ArrayList ;
2524import java .util .Collection ;
2625import java .util .Collections ;
27- import java .util .Deque ;
2826import java .util .HashSet ;
2927import java .util .List ;
3028import java .util .Map ;
31- import java .util .Optional ;
3229import java .util .Set ;
3330import java .util .concurrent .ConcurrentHashMap ;
3431import java .util .concurrent .atomic .AtomicBoolean ;
4037import org .slf4j .LoggerFactory ;
4138import org .sonar .api .SonarProduct ;
4239import org .sonar .api .batch .fs .InputFile ;
43- import org .sonar .api .batch .fs .TextRange ;
4440import org .sonar .api .batch .sensor .SensorContext ;
45- import org .sonar .api .batch .sensor .issue .NewIssue ;
46- import org .sonar .api .batch .sensor .issue .NewIssueLocation ;
4741import org .sonar .api .issue .NoSonarFilter ;
4842import org .sonar .api .measures .CoreMetrics ;
4943import org .sonar .api .measures .FileLinesContext ;
5044import org .sonar .api .measures .FileLinesContextFactory ;
5145import org .sonar .api .measures .Metric ;
52- import org .sonar .api .rule .RuleKey ;
53- import org .sonar .plugins .python .api .IssueLocation ;
5446import org .sonar .plugins .python .api .PythonCheck ;
55- import org .sonar .plugins .python .api .PythonCheck .PreciseIssue ;
5647import org .sonar .plugins .python .api .PythonFile ;
5748import org .sonar .plugins .python .api .PythonFileConsumer ;
5849import org .sonar .plugins .python .api .PythonInputFileContext ;
5950import org .sonar .plugins .python .api .PythonSubscriptionCheck ;
6051import org .sonar .plugins .python .api .PythonVisitorContext ;
61- import org .sonar .plugins .python .api .quickfix .PythonQuickFix ;
62- import org .sonar .plugins .python .api .quickfix .PythonTextEdit ;
6352import org .sonar .plugins .python .api .tree .FileInput ;
6453import org .sonar .plugins .python .cpd .PythonCpdAnalyzer ;
6554import org .sonar .plugins .python .indexer .PythonIndexer ;
@@ -91,6 +80,7 @@ public class PythonScanner extends Scanner {
9180 private final Map <String , Object > repositoryLocks ;
9281 private final NewSymbolsCollector newSymbolsCollector ;
9382 private final PythonHighlighter pythonHighlighter ;
83+ private final IssuesRepository issuesRepository ;
9484
9585 public PythonScanner (
9686 SensorContext context , PythonChecks checks , FileLinesContextFactory fileLinesContextFactory , NoSonarFilter noSonarFilter ,
@@ -110,6 +100,7 @@ public PythonScanner(
110100 this .repositoryLocks = new ConcurrentHashMap <>();
111101 this .newSymbolsCollector = new NewSymbolsCollector (this );
112102 this .pythonHighlighter = new PythonHighlighter (this );
103+ this .issuesRepository = new IssuesRepository (context , checks , indexer , isInSonarLint (context ), this );
113104 }
114105
115106 @ Override
@@ -135,7 +126,7 @@ protected void scanFile(PythonInputFile inputFile) throws IOException {
135126 architectureCallback .scanFile (visitorContext );
136127 }
137128
138- saveIssues (inputFile , visitorContext .getIssues ());
129+ issuesRepository . save (inputFile , visitorContext .getIssues ());
139130
140131 if (visitorContext .rootTree () != null && !isInSonarLint (context )) {
141132 newSymbolsCollector .collect (context .newSymbolTable ().onFile (inputFile .wrappedFile ()), visitorContext .rootTree ());
@@ -341,79 +332,6 @@ protected void reportStatistics(int numSkippedFiles, int numTotalFiles) {
341332 numSkippedFiles , numTotalFiles );
342333 }
343334
344- private synchronized void saveIssues (PythonInputFile inputFile , List <PreciseIssue > issues ) {
345- for (PreciseIssue preciseIssue : issues ) {
346- RuleKey ruleKey = checks .ruleKey (preciseIssue .check ());
347- NewIssue newIssue = context
348- .newIssue ()
349- .forRule (ruleKey );
350-
351- Integer cost = preciseIssue .cost ();
352- if (cost != null ) {
353- newIssue .gap (cost .doubleValue ());
354- }
355-
356- NewIssueLocation primaryLocation = newLocation (inputFile , newIssue , preciseIssue .primaryLocation ());
357- newIssue .at (primaryLocation );
358-
359- Deque <NewIssueLocation > secondaryLocationsFlow = new ArrayDeque <>();
360-
361- for (IssueLocation secondaryLocation : preciseIssue .secondaryLocations ()) {
362- String fileId = secondaryLocation .fileId ();
363- if (fileId != null ) {
364- InputFile issueLocationFile = component (fileId , context );
365- if (issueLocationFile != null ) {
366- secondaryLocationsFlow .addFirst (newLocation (new PythonInputFileImpl (issueLocationFile ), newIssue , secondaryLocation ));
367- }
368- } else {
369- newIssue .addLocation (newLocation (inputFile , newIssue , secondaryLocation ));
370- }
371- }
372-
373- // secondary locations on multiple files are only supported using flows
374- if (!secondaryLocationsFlow .isEmpty ()) {
375- secondaryLocationsFlow .addFirst (primaryLocation );
376- newIssue .addFlow (secondaryLocationsFlow );
377- }
378-
379- handleQuickFixes (inputFile .wrappedFile (), ruleKey , newIssue , preciseIssue );
380-
381- newIssue .save ();
382- }
383- }
384-
385- @ CheckForNull
386- private InputFile component (String fileId , SensorContext sensorContext ) {
387- var predicate = sensorContext .fileSystem ().predicates ().is (new File (fileId ));
388- InputFile inputFile = Optional .ofNullable (sensorContext .fileSystem ().inputFile (predicate ))
389- .orElseGet (() -> indexer .getFileWithId (fileId ));
390- if (inputFile == null ) {
391- LOG .debug ("Failed to find InputFile for {}" , fileId );
392- }
393- return inputFile ;
394- }
395-
396- private static NewIssueLocation newLocation (PythonInputFile inputFile , NewIssue issue , IssueLocation location ) {
397- NewIssueLocation newLocation = issue .newLocation ()
398- .on (inputFile .wrappedFile ());
399- if (location .startLine () != IssueLocation .UNDEFINED_LINE ) {
400- TextRange range ;
401- if (location .startLineOffset () == IssueLocation .UNDEFINED_OFFSET ) {
402- range = inputFile .wrappedFile ().selectLine (location .startLine ());
403- } else {
404- range = inputFile .wrappedFile ().newRange (location .startLine (), location .startLineOffset (), location .endLine (),
405- location .endLineOffset ());
406- }
407- newLocation .at (range );
408- }
409-
410- String message = location .message ();
411- if (message != null ) {
412- newLocation .message (message );
413- }
414- return newLocation ;
415- }
416-
417335 private synchronized void saveMeasures (PythonInputFile inputFile , PythonVisitorContext visitorContext ) {
418336 FileMetrics fileMetrics = new FileMetrics (visitorContext , isNotebook (inputFile ));
419337 FileLinesVisitor fileLinesVisitor = fileMetrics .fileLinesVisitor ();
@@ -467,40 +385,6 @@ private void saveMetricOnFile(PythonInputFile inputFile, Metric<Integer> metric,
467385 .save ();
468386 }
469387
470- private void handleQuickFixes (InputFile inputFile , RuleKey ruleKey , NewIssue newIssue , PreciseIssue preciseIssue ) {
471- if (isInSonarLint (context )) {
472- List <PythonQuickFix > quickFixes = preciseIssue .quickFixes ();
473- addQuickFixes (inputFile , ruleKey , quickFixes , newIssue );
474- }
475- }
476-
477- private static void addQuickFixes (InputFile inputFile , RuleKey ruleKey , Iterable <PythonQuickFix > quickFixes , NewIssue sonarLintIssue ) {
478- try {
479- for (PythonQuickFix quickFix : quickFixes ) {
480- var newQuickFix = sonarLintIssue .newQuickFix ()
481- .message (quickFix .getDescription ());
482-
483- var edit = newQuickFix .newInputFileEdit ().on (inputFile );
484-
485- quickFix .getTextEdits ().stream ()
486- .map (pythonTextEdit -> edit .newTextEdit ().at (rangeFromTextSpan (inputFile , pythonTextEdit ))
487- .withNewText (pythonTextEdit .replacementText ()))
488- .forEach (edit ::addTextEdit );
489- newQuickFix .addInputFileEdit (edit );
490- sonarLintIssue .addQuickFix (newQuickFix );
491- }
492- // TODO : is this try/catch still necessary ?
493- } catch (RuntimeException e ) {
494- // We still want to report the issue if we did not manage to create a quick fix.
495- LOG .warn (String .format ("Could not report quick fixes for rule: %s. %s: %s" , ruleKey , e .getClass ().getName (), e .getMessage ()));
496- }
497- }
498-
499- private static TextRange rangeFromTextSpan (InputFile file , PythonTextEdit pythonTextEdit ) {
500- return file .newRange (pythonTextEdit .startLine (), pythonTextEdit .startLineOffset (), pythonTextEdit .endLine (),
501- pythonTextEdit .endLineOffset ());
502- }
503-
504388 public int getRecognitionErrorCount () {
505389 return recognitionErrorCount .get ();
506390 }
0 commit comments