Skip to content

Commit

Permalink
Merge pull request #807 from Bertk/enh_checks
Browse files Browse the repository at this point in the history
Enhance checks
  • Loading branch information
guwirth committed Mar 13, 2016
2 parents df94afb + f7e874d commit 773b307
Show file tree
Hide file tree
Showing 10 changed files with 194 additions and 45 deletions.
63 changes: 51 additions & 12 deletions cxx-checks/src/main/java/org/sonar/cxx/checks/FileHeaderCheck.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,28 +35,41 @@
import org.sonar.squidbridge.annotations.ActivatedByDefault;
import org.sonar.squidbridge.annotations.SqaleConstantRemediation;
import org.sonar.squidbridge.annotations.SqaleSubCharacteristic;
import org.sonar.squidbridge.api.AnalysisException;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Rule(
key = "FileHeader",
name = "Copyright and license headers should be defined in all source files",
priority = Priority.BLOCKER)
priority = Priority.BLOCKER,
tags = {})
@ActivatedByDefault
@SqaleSubCharacteristic(RulesDefinition.SubCharacteristics.READABILITY)
@SqaleConstantRemediation("5min")
//similar Vera++ rule T013 "No copyright notice found"
public class FileHeaderCheck extends SquidCheck<Grammar> implements CxxCharsetAwareVisitor {

private static final String DEFAULT_HEADER_FORMAT = "";

private static final String MESSAGE = "Add or update the header of this file.";

@RuleProperty(
key = "headerFormat",
description = "Expected copyright and license header (plain text)",
type = "TEXT",
defaultValue = DEFAULT_HEADER_FORMAT)
public String headerFormat = DEFAULT_HEADER_FORMAT;

@RuleProperty(
key = "isRegularExpression",
description = "Whether the headerFormat is a regular expression",
defaultValue = "false")
public boolean isRegularExpression = false;

private Charset charset;
private String[] expectedLines;
private Pattern searchPattern = null;

@Override
public void setCharset(Charset charset) {
Expand All @@ -65,23 +78,49 @@ public void setCharset(Charset charset) {

@Override
public void init() {
expectedLines = headerFormat.split("(?:\r)?\n|\r");
if (isRegularExpression) {
if (searchPattern == null) {
try {
searchPattern = Pattern.compile(headerFormat, Pattern.DOTALL);
} catch (IllegalArgumentException e) {
throw new IllegalArgumentException("[" + getClass().getSimpleName() + "] Unable to compile the regular expression: " + headerFormat, e);
}
}
} else {
expectedLines = headerFormat.split("(?:\r)?\n|\r");
}
}

@Override
public void visitFile(AstNode astNode) {
List<String> lines;
try {
lines = Files.readLines(getContext().getFile(), charset);
} catch (IOException e) {
throw new IllegalStateException(e);
}
if (isRegularExpression) {
String fileContent;
try {
fileContent = Files.toString(getContext().getFile(), charset);
} catch (IOException e) {
throw new AnalysisException(e);
}
checkRegularExpression(fileContent);
} else { List<String> lines;
try {
lines = Files.readLines(getContext().getFile(), charset);
} catch (IOException e) {
throw new IllegalStateException(e);
}

if (!matches(expectedLines, lines)) {
getContext().createFileViolation(this, "Add or update the header of this file.");
if (!matches(expectedLines, lines)) {
getContext().createFileViolation(this, MESSAGE);
}
}
}

private void checkRegularExpression(String fileContent) {
Matcher matcher = searchPattern.matcher(fileContent);
if (!matcher.find() || matcher.start() != 0) {
getContext().createFileViolation(this, MESSAGE);
}
}

private static boolean matches(String[] expectedLines, List<String> lines) {
boolean result;

Expand All @@ -102,5 +141,5 @@ private static boolean matches(String[] expectedLines, List<String> lines) {

return result;
}

}

Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,20 @@
*/
package org.sonar.cxx.checks;

import java.util.List;

import org.sonar.api.server.rule.RulesDefinition;
import org.sonar.check.Priority;
import org.sonar.check.Rule;
import org.sonar.check.RuleProperty;
import org.sonar.cxx.api.CxxMetric;
import org.sonar.cxx.api.CppPunctuator;
import org.sonar.cxx.parser.CxxGrammarImpl;
import org.sonar.cxx.tag.Tag;
import org.sonar.squidbridge.annotations.ActivatedByDefault;
import org.sonar.squidbridge.annotations.SqaleConstantRemediation;
import org.sonar.squidbridge.annotations.SqaleSubCharacteristic;
import org.sonar.squidbridge.api.SourceFunction;

import com.sonar.sslr.api.AstNode;
import com.sonar.sslr.api.Grammar;
import org.sonar.squidbridge.checks.ChecksHelper;
import org.sonar.squidbridge.checks.SquidCheck;

@Rule(key = "TooManyLinesOfCodeInFunction",
Expand All @@ -44,37 +43,48 @@
@SqaleSubCharacteristic(RulesDefinition.SubCharacteristics.READABILITY)
@SqaleConstantRemediation("1h")
public class TooManyLinesOfCodeInFunctionCheck extends SquidCheck<Grammar> {
private static final int DEFAULT_MAXIMUM = 50;
private static final int DEFAULT_MAXIMUM = 200;

@RuleProperty(
key = "max",
description = "Maximum code lines allowed",
defaultValue = "" + DEFAULT_MAXIMUM)
@RuleProperty(
key = "max",
description = "Maximum code lines allowed",
defaultValue = "" + DEFAULT_MAXIMUM)

private int max = DEFAULT_MAXIMUM;
private int max = DEFAULT_MAXIMUM;

@Override
public void init() {
subscribeTo(CxxGrammarImpl.functionDefinition);
}

@Override
public void leaveNode(AstNode node) {
SourceFunction sourceFunction = (SourceFunction) getContext().peekSourceCode();
int lineCount = ChecksHelper.getRecursiveMeasureInt(sourceFunction, CxxMetric.LINES_OF_CODE);
// LINES_OF_CODE not correct == 7 and should be only 5
if (lineCount > max) {
getContext().createLineViolation(this,
"The number of code lines in this function is {0,number,integer} which is greater than {1,number,integer} authorized.",
node,
lineCount,
max);
}
}
@Override
public void init() {
subscribeTo(CxxGrammarImpl.functionBody);
}

public void setMax(int max) {
this.max = max;
}
@Override
public void leaveNode(AstNode node) {
int lineCount = getNumberOfLine(node);
if (lineCount > max) {
getContext().createLineViolation(this,
"The number of code lines in this function is {0,number,integer} which is greater than {1,number,integer} authorized.",
node, lineCount, max);
}
}

}
public static int getNumberOfLine(AstNode node) {
List<AstNode> allChilds = node.getDescendants(CxxGrammarImpl.statement, CppPunctuator.CURLBR_LEFT, CppPunctuator.CURLBR_RIGHT);
int lines = 1;
int firstLine = node.getTokenLine();
if (allChilds != null && !allChilds.isEmpty()) {
int previousLine = firstLine;
for (AstNode child : allChilds) {
if (child.getTokenLine()!= previousLine ) {
lines++;
previousLine = child.getTokenLine();
}
}
}
return lines;
}

public void setMax(int max) {
this.max = max;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,18 @@

import java.io.File;

import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.sonar.cxx.CxxAstScanner;
import org.sonar.squidbridge.api.SourceFile;
import org.sonar.squidbridge.checks.CheckMessagesVerifier;

public class FileHeaderCheckTest {

@Rule
public ExpectedException thrown = ExpectedException.none();

@Test
public void test() {
FileHeaderCheck check = new FileHeaderCheck();
Expand Down Expand Up @@ -101,4 +106,59 @@ public void test() {
.noMore();
}

@Test
public void regex() {
FileHeaderCheck check = new FileHeaderCheck();
check.headerFormat = "// copyright \\d\\d\\d";
check.isRegularExpression = true;
SourceFile file = CxxAstScanner.scanSingleFile(new File("src/test/resources/checks/FileHeaderCheck/Regex1.cc"), check);
CheckMessagesVerifier.verify(file.getCheckMessages()).next().atLine(null).withMessage("Add or update the header of this file.");
// Check that the regular expression is compiled once
check = new FileHeaderCheck();
file = CxxAstScanner.scanSingleFile(new File("src/test/resources/checks/FileHeaderCheck/Regex1.cc"), check);
CheckMessagesVerifier.verify(file.getCheckMessages()).next().atLine(null).withMessage("Add or update the header of this file.");

check = new FileHeaderCheck();
check.headerFormat = "// copyright \\d{4}\\n// mycompany";
check.isRegularExpression = true;

file = CxxAstScanner.scanSingleFile(new File("src/test/resources/checks/FileHeaderCheck/Regex2.cc"), check);
CheckMessagesVerifier.verify(file.getCheckMessages()).next().atLine(null).withMessage("Add or update the header of this file.");

check = new FileHeaderCheck();
check.headerFormat = "// copyright \\d{4}\\r?\\n// mycompany";
check.isRegularExpression = true;
file = CxxAstScanner.scanSingleFile(new File("src/test/resources/checks/FileHeaderCheck/Regex3.cc"), check);
CheckMessagesVerifier.verify(file.getCheckMessages()).noMore();

check = new FileHeaderCheck();
check.headerFormat = "// copyright \\d{4}\\n// mycompany";
check.isRegularExpression = true;
file = CxxAstScanner.scanSingleFile(new File("src/test/resources/checks/FileHeaderCheck/Regex4.cc"), check);
CheckMessagesVerifier.verify(file.getCheckMessages()).next().atLine(null).withMessage("Add or update the header of this file.");

check = new FileHeaderCheck();
check.headerFormat = "^(?=.*?\\bCopyright\\b)(?=.*?\\bVendor\\b)(?=.*?\\d{4}(-\\d{4})?).*$";
check.isRegularExpression = true;
file = CxxAstScanner.scanSingleFile(new File("src/test/resources/checks/FileHeaderCheck/Regex5.cc"), check);
CheckMessagesVerifier.verify(file.getCheckMessages()).noMore();

check = new FileHeaderCheck();
file = CxxAstScanner.scanSingleFile(new File("src/test/resources/checks/FileHeaderCheck/Regex6.cc"), check);
CheckMessagesVerifier.verify(file.getCheckMessages()).next().atLine(null).withMessage("Add or update the header of this file.");

}

@Test
public void should_fail_with_bad_regular_expression() {
thrown.expect(IllegalArgumentException.class);
thrown.expectMessage("[" + FileHeaderCheck.class.getSimpleName() + "] Unable to compile the regular expression: *");

FileHeaderCheck check = new FileHeaderCheck();
check.headerFormat = "*";
check.isRegularExpression = true;
check.init();
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public class TooManyLinesOfCodeInFunctionCheckTest {

@Test
public void test() {
check.setMax(7);
check.setMax(6);
SourceFile file = CxxAstScanner.scanSingleFile(new File("src/test/resources/checks/FunctionLength.cc"), check);
CheckMessagesVerifier.verify(file.getCheckMessages())
.next().atLine(21)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// bad copyright

public class Regex1 {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// copyright 2005
// mycompan

public class Regex2 {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// copyright 2005
// mycompany

public class Regex3 {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// prefix
// copyright 2005
// mycompany

public class Regex4 {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// prefix
// Copyright (c) Vendor AG 2015-2016
// All Rights Reserved.

public class Regex5 {

}

Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// prefix
// Copyright (c) AG 2015-2016
// All Rights Reserved.

public class Regex5 {

}

0 comments on commit 773b307

Please sign in to comment.