Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[MPMD-309] Add configuration option to show suppressed violations #59

Merged
merged 1 commit into from Mar 24, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -22,12 +22,15 @@
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.codehaus.plexus.util.StringUtils;

import net.sourceforge.pmd.Report;
import net.sourceforge.pmd.Report.ProcessingError;
import net.sourceforge.pmd.Report.SuppressedViolation;
import net.sourceforge.pmd.RuleViolation;
import net.sourceforge.pmd.renderers.AbstractRenderer;
import net.sourceforge.pmd.util.datasource.DataSource;
Expand All @@ -41,8 +44,9 @@
*/
public class PmdCollectingRenderer extends AbstractRenderer
{
private List<ProcessingError> errors = Collections.synchronizedList( new ArrayList<ProcessingError>() );
private List<RuleViolation> violations = Collections.synchronizedList( new ArrayList<RuleViolation>() );
private List<ProcessingError> errors = Collections.synchronizedList( new ArrayList<>() );
private List<RuleViolation> violations = Collections.synchronizedList( new ArrayList<>() );
private List<SuppressedViolation> suppressed = Collections.synchronizedList( new ArrayList<> () );

/**
* Collects all reports from all threads.
Expand All @@ -57,6 +61,7 @@ public void renderFileReport( Report report ) throws IOException
{
violations.addAll( report.getViolations() );
errors.addAll( report.getProcessingErrors() );
suppressed.addAll( report.getSuppressedViolations() );
}

/**
Expand Down Expand Up @@ -129,6 +134,19 @@ public Report asReport()
{
report.addError( e );
}
Map<Integer, String> suppressedLines = new HashMap<Integer, String>();
for ( SuppressedViolation s : suppressed )
{
if ( s.suppressedByNOPMD() )
{
suppressedLines.put( s.getRuleViolation().getBeginLine(), s.getUserMessage() );
}
}
report.suppress( suppressedLines );
for ( SuppressedViolation s : suppressed )
{
report.addRuleViolation( s.getRuleViolation() );
}
return report;
}

Expand Down
12 changes: 12 additions & 0 deletions src/main/java/org/apache/maven/plugins/pmd/PmdReport.java
Expand Up @@ -216,6 +216,14 @@ public class PmdReport
@Parameter( property = "pmd.renderViolationsByPriority", defaultValue = "true" )
private boolean renderViolationsByPriority = true;

/**
* Add a section in the HTML report that lists the suppressed violations.
*
* @since 3.17.0
*/
@Parameter( property = "pmd.renderSuppressedViolations", defaultValue = "true" )
private boolean renderSuppressedViolations = true;

/**
* Before PMD is executed, the configured rulesets are resolved and copied into this directory.
* <p>Note: Before 3.13.0, this was by default ${project.build.directory}.
Expand Down Expand Up @@ -501,6 +509,10 @@ private void generateMavenSiteReport( Locale locale )
doxiaRenderer.setRenderViolationsByPriority( renderViolationsByPriority );
doxiaRenderer.setFiles( filesToProcess );
doxiaRenderer.setViolations( pmdResult.getViolations() );
if ( renderSuppressedViolations )
{
doxiaRenderer.setSuppressedViolations( pmdResult.getSuppressedViolations() );
}
if ( renderProcessingErrors )
{
doxiaRenderer.setProcessingErrors( pmdResult.getErrors() );
Expand Down
80 changes: 80 additions & 0 deletions src/main/java/org/apache/maven/plugins/pmd/PmdReportGenerator.java
Expand Up @@ -35,6 +35,7 @@
import org.apache.maven.doxia.sink.Sink;
import org.apache.maven.plugin.logging.Log;
import org.apache.maven.plugins.pmd.model.ProcessingError;
import org.apache.maven.plugins.pmd.model.SuppressedViolation;
import org.apache.maven.plugins.pmd.model.Violation;
import org.codehaus.plexus.util.StringUtils;

Expand All @@ -58,6 +59,8 @@ public class PmdReportGenerator

private Set<Violation> violations = new HashSet<>();

private List<SuppressedViolation> suppressedViolations = new ArrayList<>();

private List<ProcessingError> processingErrors = new ArrayList<>();

private boolean aggregate;
Expand Down Expand Up @@ -93,6 +96,11 @@ public List<Violation> getViolations()
return new ArrayList<>( violations );
}

public void setSuppressedViolations( Collection<SuppressedViolation> suppressedViolations )
{
this.suppressedViolations = new ArrayList<>( suppressedViolations );
}

public void setProcessingErrors( Collection<ProcessingError> errors )
{
this.processingErrors = new ArrayList<>( errors );
Expand Down Expand Up @@ -377,6 +385,73 @@ private void outputLineLink( int line, PmdFileInfo fileInfo )
}
}

// PMD might run the analysis multi-threaded, so the suppressed violations might be reported
// out of order. We sort them here by filename before writing them to
// the report.
private void renderSuppressedViolations()
throws IOException
{
sink.section1();
sink.sectionTitle1();
sink.text( bundle.getString( "report.pmd.suppressedViolations.title" ) );
sink.sectionTitle1_();

Collections.sort( suppressedViolations, new Comparator<SuppressedViolation>()
{
@Override
public int compare( SuppressedViolation o1, SuppressedViolation o2 )
{
return o1.getFilename().compareTo( o2.getFilename() );
}
} );

sink.table();
sink.tableRow();
sink.tableHeaderCell();
sink.text( bundle.getString( "report.pmd.suppressedViolations.column.filename" ) );
sink.tableHeaderCell_();
sink.tableHeaderCell();
sink.text( bundle.getString( "report.pmd.suppressedViolations.column.ruleMessage" ) );
sink.tableHeaderCell_();
sink.tableHeaderCell();
sink.text( bundle.getString( "report.pmd.suppressedViolations.column.suppressionType" ) );
sink.tableHeaderCell_();
sink.tableHeaderCell();
sink.text( bundle.getString( "report.pmd.suppressedViolations.column.userMessage" ) );
sink.tableHeaderCell_();
sink.tableRow_();

for ( SuppressedViolation suppressedViolation : suppressedViolations )
{
String filename = suppressedViolation.getFilename();
PmdFileInfo fileInfo = determineFileInfo( filename );
filename = shortenFilename( filename, fileInfo );

sink.tableRow();

sink.tableCell();
sink.text( filename );
sink.tableCell_();

sink.tableCell();
sink.text( suppressedViolation.getRuleMessage() );
sink.tableCell_();

sink.tableCell();
sink.text( suppressedViolation.getSuppressionType() );
sink.tableCell_();

sink.tableCell();
sink.text( suppressedViolation.getUserMessage() );
sink.tableCell_();

sink.tableRow_();
}

sink.table_();
sink.section1_();
}

private void processProcessingErrors() throws IOException
{
// sort the problem by filename first, since PMD is executed multi-threaded
Expand Down Expand Up @@ -492,6 +567,11 @@ public void render()
sink.paragraph_();
}

if ( !suppressedViolations.isEmpty() )
{
renderSuppressedViolations();
}

if ( !processingErrors.isEmpty() )
{
processProcessingErrors();
Expand Down
Expand Up @@ -32,6 +32,7 @@
import org.apache.maven.plugins.pmd.model.PmdErrorDetail;
import org.apache.maven.plugins.pmd.model.PmdFile;
import org.apache.maven.plugins.pmd.model.ProcessingError;
import org.apache.maven.plugins.pmd.model.SuppressedViolation;
import org.apache.maven.plugins.pmd.model.Violation;
import org.apache.maven.plugins.pmd.model.io.xpp3.PmdXpp3Reader;
import org.apache.maven.reporting.MavenReportException;
Expand All @@ -43,6 +44,7 @@ public class PmdResult
{
private final List<ProcessingError> processingErrors = new ArrayList<>();
private final List<Violation> violations = new ArrayList<>();
private final List<SuppressedViolation> suppressedViolations = new ArrayList<>();

public static final PmdResult EMPTY = new PmdResult();

Expand All @@ -68,6 +70,7 @@ private void loadResult( File pmdFile, String encoding ) throws MavenReportExcep
PmdXpp3Reader reader = new PmdXpp3Reader();
PmdErrorDetail details = reader.read( reader1, false );
processingErrors.addAll( details.getErrors() );
suppressedViolations.addAll( details.getSuppressedViolations() );

for ( PmdFile file : details.getFiles() )
{
Expand Down Expand Up @@ -147,6 +150,11 @@ public Collection<Violation> getViolations()
return violations;
}

public Collection<SuppressedViolation> getSuppressedViolations()
{
return suppressedViolations;
}

public Collection<ProcessingError> getErrors()
{
return processingErrors;
Expand Down
28 changes: 28 additions & 0 deletions src/main/mdo/pmd.mdo
Expand Up @@ -45,6 +45,13 @@ under the License.
<multiplicity>*</multiplicity>
</association>
</field>
<field>
<name>suppressedViolations</name>
<association xml.tagName="suppressedviolation" xml.itemsStyle="flat">
<type>SuppressedViolation</type>
<multiplicity>*</multiplicity>
</association>
</field>
<field>
<name>errors</name>
<association xml.tagName="error" xml.itemsStyle="flat">
Expand Down Expand Up @@ -126,6 +133,27 @@ under the License.
</codeSegment>
</codeSegments>
</class>
<class>
<name>SuppressedViolation</name>
<fields>
<field xml.attribute="true">
<name>filename</name>
<type>String</type>
</field>
<field xml.tagName="suppressiontype" xml.attribute="true">
<name>suppressionType</name>
<type>String</type>
</field>
<field xml.tagName="msg" xml.attribute="true">
<name>ruleMessage</name>
<type>String</type>
</field>
<field xml.tagName="usermsg" xml.attribute="true">
<name>userMessage</name>
<type>String</type>
</field>
</fields>
</class>
<class>
<name>ProcessingError</name>
<fields>
Expand Down
5 changes: 5 additions & 0 deletions src/main/resources/pmd-report.properties
Expand Up @@ -27,6 +27,11 @@ report.pmd.files=Files
report.pmd.violationsByPriority=Violations By Priority
report.pmd.priority=Priority
report.pmd.noProblems=PMD found no problems in your source code.
report.pmd.suppressedViolations.title=Suppressed Violations
report.pmd.suppressedViolations.column.filename=Filename
report.pmd.suppressedViolations.column.ruleMessage=Rule message
report.pmd.suppressedViolations.column.suppressionType=Suppression type
report.pmd.suppressedViolations.column.userMessage=Reason
report.pmd.processingErrors.title=Processing Errors
report.pmd.processingErrors.column.filename=Filename
report.pmd.processingErrors.column.problem=Problem
5 changes: 5 additions & 0 deletions src/main/resources/pmd-report_de.properties
Expand Up @@ -27,6 +27,11 @@ report.pmd.files=Dateien
report.pmd.violationsByPriority=Verst\u00f6\u00dfe nach Priorit\u00e4t
report.pmd.priority=Priorit\u00e4t
report.pmd.noProblems=PMD hat keine Probleme in dem Quellcode gefunden.
report.pmd.suppressedViolations.title=Unterdr\u00fcckte Verst\u00f6\u00dfe
report.pmd.suppressedViolations.column.filename=Datei
report.pmd.suppressedViolations.column.ruleMessage=Regel
report.pmd.suppressedViolations.column.suppressionType=Art der Unterdr\u00fcckung
report.pmd.suppressedViolations.column.userMessage=Grund
report.pmd.processingErrors.title=Verarbeitungsprobleme
report.pmd.processingErrors.column.filename=Datei
report.pmd.processingErrors.column.problem=Problem
45 changes: 45 additions & 0 deletions src/test/java/org/apache/maven/plugins/pmd/PmdReportTest.java
Expand Up @@ -530,6 +530,51 @@ public void testSuppressMarkerConfiguration()
String str = readFile( generatedFile );

// check that there is no violation reported for "unusedVar2" - as it is suppressed
assertFalse( str.contains( "Avoid unused private fields such as 'unusedVar2'.\n </violation>" ) );
// but it appears as suppressed
assertTrue( str.contains( "suppressiontype=\"nopmd\" msg=\"Avoid unused private fields such as 'unusedVar2'.\"" ));

generatedFile = new File( getBasedir(), "target/test/unit/default-configuration/target/site/pmd.html" );
renderer( mojo, generatedFile );
assertTrue( FileUtils.fileExists( generatedFile.getAbsolutePath() ) );

// check if there's a link to the JXR files
str = readFile( generatedFile );

assertTrue( str.contains( "/xref/def/configuration/AppSample.html#L27" ) );
// suppressed violation
assertTrue( str.contains( "Avoid unused private fields such as 'unusedVar2'." ) );
}

public void testSuppressMarkerConfigurationWithoutRendering()
throws Exception
{
File testPom =
new File( getBasedir(),
"src/test/resources/unit/default-configuration/pmd-with-suppressMarker-no-render-plugin-config.xml" );
PmdReport mojo = (PmdReport) lookupMojo( "pmd", testPom );
mojo.execute();

// check if the PMD files were generated
File generatedFile = new File( getBasedir(), "target/test/unit/default-configuration/target/pmd.xml" );
assertTrue( FileUtils.fileExists( generatedFile.getAbsolutePath() ) );

String str = readFile( generatedFile );

// check that there is no violation reported for "unusedVar2" - as it is suppressed
assertFalse( str.contains( "Avoid unused private fields such as 'unusedVar2'.\n </violation>" ) );
// but it appears as suppressed
assertTrue( str.contains( "suppressiontype=\"nopmd\" msg=\"Avoid unused private fields such as 'unusedVar2'.\"" ));

generatedFile = new File( getBasedir(), "target/test/unit/default-configuration/target/site/pmd.html" );
renderer( mojo, generatedFile );
assertTrue( FileUtils.fileExists( generatedFile.getAbsolutePath() ) );

// check if there's a link to the JXR files
str = readFile( generatedFile );

assertTrue( str.contains( "/xref/def/configuration/AppSample.html#L27" ) );
// suppressed violations are not rendered
assertFalse( str.contains( "Avoid unused private fields such as 'unusedVar2'." ) );
}

Expand Down