Added support for Scala to CPD.
The Scala tokenizer was extracted from the Sonar Scala plugin. (https://github.com/SonarCommunity/sonar-scala) I copied the code because the Sonar Scala plugin has a lot of unwanted dependencies. I tried to keep the changes to the Scala Sonar plugin as minimal as possible. To tokenize the source files the official Scala compiler is used.
This commit is contained in:
92
pmd-scala/pom.xml
Normal file
92
pmd-scala/pom.xml
Normal file
@ -0,0 +1,92 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>pmd-scala</artifactId>
|
||||
<name>PMD Scala</name>
|
||||
|
||||
<parent>
|
||||
<groupId>net.sourceforge.pmd</groupId>
|
||||
<artifactId>pmd</artifactId>
|
||||
<version>5.2.4-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<scala.version>2.10.4</scala.version>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>net.alchim31.maven</groupId>
|
||||
<artifactId>scala-maven-plugin</artifactId>
|
||||
<version>3.2.0</version>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-resources-plugin</artifactId>
|
||||
<configuration>
|
||||
<useDefaultDelimiters>false</useDefaultDelimiters>
|
||||
<delimiters>
|
||||
<delimiter>${*}</delimiter>
|
||||
</delimiters>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>net.alchim31.maven</groupId>
|
||||
<artifactId>scala-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>scala-compile-first</id>
|
||||
<phase>process-resources</phase>
|
||||
<goals>
|
||||
<goal>add-source</goal>
|
||||
<goal>compile</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-site-plugin</artifactId>
|
||||
<configuration>
|
||||
<xdocDirectory>${project.build.directory}/generated-xdocs</xdocDirectory>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.scala-lang</groupId>
|
||||
<artifactId>scala-library</artifactId>
|
||||
<version>${scala.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.scala-lang</groupId>
|
||||
<artifactId>scala-compiler</artifactId>
|
||||
<version>${scala.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.sourceforge.pmd</groupId>
|
||||
<artifactId>pmd-core</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.sourceforge.pmd</groupId>
|
||||
<artifactId>pmd-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
@ -0,0 +1,19 @@
|
||||
/**
|
||||
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
|
||||
*/
|
||||
package net.sourceforge.pmd.cpd;
|
||||
|
||||
import org.sonar.plugins.scala.cpd.ScalaTokenizer;
|
||||
|
||||
/**
|
||||
* Language implementation for Scala
|
||||
*/
|
||||
public class ScalaLanguage extends AbstractLanguage {
|
||||
|
||||
/**
|
||||
* Creates a new Scala Language instance.
|
||||
*/
|
||||
public ScalaLanguage() {
|
||||
super("Scala", "scala", new ScalaTokenizer(), ".scala");
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
/**
|
||||
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
|
||||
*/
|
||||
package net.sourceforge.pmd.lang.scala;
|
||||
|
||||
import net.sourceforge.pmd.lang.BaseLanguageModule;
|
||||
|
||||
/**
|
||||
* Language Module for Scala
|
||||
*/
|
||||
public class ScalaLanguageModule extends BaseLanguageModule {
|
||||
|
||||
/** The name. */
|
||||
public static final String NAME = "Scala";
|
||||
/** The terse name. */
|
||||
public static final String TERSE_NAME = "scala";
|
||||
|
||||
/**
|
||||
* Create a new instance of Scala Language Module.
|
||||
*/
|
||||
public ScalaLanguageModule() {
|
||||
super(NAME, null, TERSE_NAME, null, "scala");
|
||||
addVersion("", null, true);
|
||||
}
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Sonar Scala Plugin
|
||||
* Copyright (C) 2011 - 2014 All contributors
|
||||
* dev@sonar.codehaus.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
|
||||
*/
|
||||
package org.sonar.plugins.scala.cpd;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import net.sourceforge.pmd.cpd.SourceCode;
|
||||
import net.sourceforge.pmd.cpd.TokenEntry;
|
||||
import net.sourceforge.pmd.cpd.Tokenizer;
|
||||
import net.sourceforge.pmd.cpd.Tokens;
|
||||
|
||||
import org.sonar.plugins.scala.compiler.Lexer;
|
||||
import org.sonar.plugins.scala.compiler.Token;
|
||||
|
||||
/**
|
||||
* Scala tokenizer for PMD CPD.
|
||||
*
|
||||
* @since 0.1
|
||||
*/
|
||||
public final class ScalaTokenizer implements Tokenizer {
|
||||
|
||||
public void tokenize(SourceCode source, Tokens cpdTokens) {
|
||||
String filename = source.getFileName();
|
||||
|
||||
Lexer lexer = new Lexer();
|
||||
List<Token> tokens = lexer.getTokensOfFile(filename);
|
||||
for (Token token : tokens) {
|
||||
String tokenVal =
|
||||
token.tokenVal() != null ? token.tokenVal() : Integer.toString(token.tokenType());
|
||||
|
||||
TokenEntry cpdToken = new TokenEntry(tokenVal, filename, token.line());
|
||||
cpdTokens.add(cpdToken);
|
||||
}
|
||||
|
||||
cpdTokens.add(TokenEntry.getEOF());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Sonar Scala Plugin
|
||||
* Copyright (C) 2011 - 2014 All contributors
|
||||
* dev@sonar.codehaus.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
|
||||
*/
|
||||
package org.sonar.plugins.scala.language;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.lang3.builder.EqualsBuilder;
|
||||
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.sonar.plugins.scala.util.StringUtils;
|
||||
|
||||
/**
|
||||
* This class implements a Scala comment and the computation
|
||||
* of several base metrics for a comment.
|
||||
*
|
||||
* @author Felix Müller
|
||||
* @since 0.1
|
||||
*/
|
||||
public class Comment {
|
||||
|
||||
private final CommentType type;
|
||||
private final List<String> lines;
|
||||
|
||||
public Comment(String content, CommentType type) throws IOException {
|
||||
lines = StringUtils.convertStringToListOfLines(content);
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public int getNumberOfLines() {
|
||||
return lines.size() - getNumberOfBlankLines() - getNumberOfCommentedOutLinesOfCode();
|
||||
}
|
||||
|
||||
public int getNumberOfBlankLines() {
|
||||
int numberOfBlankLines = 0;
|
||||
for (String comment : lines) {
|
||||
boolean isBlank = true;
|
||||
|
||||
for (int i = 0; isBlank && i < comment.length(); i++) {
|
||||
char character = comment.charAt(i);
|
||||
if (!Character.isWhitespace(character) && character != '*' && character != '/') {
|
||||
isBlank = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (isBlank) {
|
||||
numberOfBlankLines++;
|
||||
}
|
||||
}
|
||||
return numberOfBlankLines;
|
||||
}
|
||||
|
||||
public int getNumberOfCommentedOutLinesOfCode() {
|
||||
if (isDocComment()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int numberOfCommentedOutLinesOfCode = 0;
|
||||
for (String line : lines) {
|
||||
String strippedLine = org.apache.commons.lang3.StringUtils.strip(line, " /*");
|
||||
if (CodeDetector.hasDetectedCode(strippedLine)) {
|
||||
numberOfCommentedOutLinesOfCode++;
|
||||
}
|
||||
}
|
||||
return numberOfCommentedOutLinesOfCode;
|
||||
}
|
||||
|
||||
public boolean isDocComment() {
|
||||
return type == CommentType.DOC;
|
||||
}
|
||||
|
||||
public boolean isHeaderComment() {
|
||||
return type == CommentType.HEADER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return new HashCodeBuilder().append(type).append(lines).toHashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (!(obj instanceof Comment)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Comment other = (Comment) obj;
|
||||
return new EqualsBuilder().append(type, other.type).append(lines, other.lines).isEquals();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final String firstLine = lines.isEmpty() ? "" : lines.get(0);
|
||||
final String lastLine = lines.isEmpty() ? "" : lines.get(lines.size() - 1);
|
||||
return new ToStringBuilder(this).append("type", type)
|
||||
.append("firstLine", firstLine)
|
||||
.append("lastLine", lastLine)
|
||||
.append("numberOfLines", getNumberOfLines())
|
||||
.append("numberOfCommentedOutLinesOfCode", getNumberOfCommentedOutLinesOfCode())
|
||||
.toString();
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Sonar Scala Plugin
|
||||
* Copyright (C) 2011 - 2014 All contributors
|
||||
* dev@sonar.codehaus.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
|
||||
*/
|
||||
package org.sonar.plugins.scala.language;
|
||||
|
||||
/**
|
||||
* This enum is a helper to distinguish between the
|
||||
* different types of comments in Sonar.
|
||||
*
|
||||
* @author Felix Müller
|
||||
* @since 0.1
|
||||
*/
|
||||
public enum CommentType {
|
||||
|
||||
NORMAL,
|
||||
DOC,
|
||||
HEADER;
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Sonar Scala Plugin
|
||||
* Copyright (C) 2011 - 2014 All contributors
|
||||
* dev@sonar.codehaus.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
|
||||
*/
|
||||
package org.sonar.plugins.scala.util;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
public final class StringUtils {
|
||||
private StringUtils() {
|
||||
// to prevent instantiation
|
||||
}
|
||||
|
||||
public static List<String> convertStringToListOfLines(String string) throws IOException {
|
||||
final List<String> lines = new ArrayList<String>();
|
||||
BufferedReader reader = null;
|
||||
try {
|
||||
reader = new BufferedReader(new StringReader(string));
|
||||
String line = null;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
lines.add(line);
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
throw ioe;
|
||||
} finally {
|
||||
IOUtils.closeQuietly(reader);
|
||||
}
|
||||
return lines;
|
||||
}
|
||||
}
|
@ -0,0 +1 @@
|
||||
net.sourceforge.pmd.cpd.ScalaLanguage
|
@ -0,0 +1 @@
|
||||
net.sourceforge.pmd.lang.scala.ScalaLanguageModule
|
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Sonar Scala Plugin
|
||||
* Copyright (C) 2011 - 2014 All contributors
|
||||
* dev@sonar.codehaus.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
|
||||
*/
|
||||
package org.sonar.plugins.scala.compiler
|
||||
|
||||
import scala.tools.nsc.{Settings, Global}
|
||||
|
||||
/**
|
||||
* This is a wrapper for the Scala compiler. It is used to access
|
||||
* the compiler in a more convenient way.
|
||||
*
|
||||
* @author Felix Müller
|
||||
* @since 0.1
|
||||
*/
|
||||
object Compiler extends Global(new Settings()) {
|
||||
override def forScaladoc = true
|
||||
}
|
@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Sonar Scala Plugin
|
||||
* Copyright (C) 2011 - 2014 All contributors
|
||||
* dev@sonar.codehaus.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
|
||||
*/
|
||||
package org.sonar.plugins.scala.compiler
|
||||
|
||||
import collection.mutable.ListBuffer
|
||||
|
||||
import org.sonar.plugins.scala.language.{Comment, CommentType}
|
||||
import scala.reflect.io.AbstractFile
|
||||
import scala.reflect.internal.util.BatchSourceFile
|
||||
|
||||
/**
|
||||
* This class is a wrapper for accessing the lexer of the Scala compiler
|
||||
* from Java in a more convenient way.
|
||||
*
|
||||
* @author Felix Müller
|
||||
* @since 0.1
|
||||
*/
|
||||
class Lexer {
|
||||
|
||||
import scala.collection.JavaConversions._
|
||||
import Compiler._
|
||||
|
||||
def getTokens(code: String): java.util.List[Token] = {
|
||||
val unit = new CompilationUnit(new BatchSourceFile("", code.toCharArray))
|
||||
tokenize(unit)
|
||||
}
|
||||
|
||||
def getTokensOfFile(path: String): java.util.List[Token] = {
|
||||
val unit = new CompilationUnit(new BatchSourceFile(AbstractFile.getFile(path)))
|
||||
tokenize(unit)
|
||||
}
|
||||
|
||||
private def tokenize(unit: CompilationUnit): java.util.List[Token] = {
|
||||
val scanner = new syntaxAnalyzer.UnitScanner(unit)
|
||||
val tokens = ListBuffer[Token]()
|
||||
|
||||
scanner.init()
|
||||
while (scanner.token != scala.tools.nsc.ast.parser.Tokens.EOF) {
|
||||
val tokenVal =
|
||||
if (scala.tools.nsc.ast.parser.Tokens.isIdentifier(scanner.token)) scanner.name.toString() else null
|
||||
val linenr = scanner.parensAnalyzer.line(scanner.offset) + 1
|
||||
|
||||
tokens += Token(scanner.token, linenr, tokenVal)
|
||||
scanner.nextToken()
|
||||
}
|
||||
tokens
|
||||
}
|
||||
|
||||
def getComments(code: String): java.util.List[Comment] = {
|
||||
val unit = new CompilationUnit(new BatchSourceFile("", code.toCharArray))
|
||||
tokenizeComments(unit)
|
||||
}
|
||||
|
||||
def getCommentsOfFile(path: String): java.util.List[Comment] = {
|
||||
val unit = new CompilationUnit(new BatchSourceFile(AbstractFile.getFile(path)))
|
||||
tokenizeComments(unit)
|
||||
}
|
||||
|
||||
private def tokenizeComments(unit: CompilationUnit): java.util.List[Comment] = {
|
||||
val comments = ListBuffer[Comment]()
|
||||
val scanner = new syntaxAnalyzer.UnitScanner(unit) {
|
||||
|
||||
private var lastDocCommentRange: Option[Range] = None
|
||||
|
||||
private var foundToken = false
|
||||
|
||||
override def nextToken() {
|
||||
super.nextToken()
|
||||
foundToken = token != 0
|
||||
}
|
||||
|
||||
override def foundComment(value: String, start: Int, end: Int) = {
|
||||
super.foundComment(value, start, end)
|
||||
|
||||
def isHeaderComment(value: String) = {
|
||||
!foundToken && comments.isEmpty && value.trim().startsWith("/*")
|
||||
}
|
||||
|
||||
lastDocCommentRange match {
|
||||
|
||||
case Some(r: Range) => {
|
||||
if (r.start != start || r.end != end) {
|
||||
comments += new Comment(value, CommentType.NORMAL)
|
||||
}
|
||||
}
|
||||
|
||||
case None => {
|
||||
if (isHeaderComment(value)) {
|
||||
comments += new Comment(value, CommentType.HEADER)
|
||||
} else {
|
||||
comments += new Comment(value, CommentType.NORMAL)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override def foundDocComment(value: String, start: Int, end: Int) = {
|
||||
super.foundDocComment(value, start, end)
|
||||
comments += new Comment(value, CommentType.DOC)
|
||||
lastDocCommentRange = Some(Range(start, end))
|
||||
}
|
||||
}
|
||||
|
||||
scanner.init()
|
||||
while (scanner.token != scala.tools.nsc.ast.parser.Tokens.EOF) {
|
||||
scanner.nextToken()
|
||||
}
|
||||
|
||||
comments
|
||||
}
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Sonar Scala Plugin
|
||||
* Copyright (C) 2011 - 2014 All contributors
|
||||
* dev@sonar.codehaus.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
|
||||
*/
|
||||
package org.sonar.plugins.scala.compiler
|
||||
|
||||
import scala.reflect.internal.util.{ScriptSourceFile, BatchSourceFile}
|
||||
import scala.reflect.io.AbstractFile
|
||||
|
||||
/**
|
||||
* This class is a wrapper for accessing the parser of the Scala compiler
|
||||
* from Java in a more convenient way.
|
||||
*
|
||||
* @author Felix Müller
|
||||
* @since 0.1
|
||||
*/
|
||||
class Parser {
|
||||
|
||||
import Compiler._
|
||||
|
||||
def parse(code: String): Tree = {
|
||||
val batchSourceFile = new BatchSourceFile("", code.toCharArray)
|
||||
parse(batchSourceFile, code.toCharArray)
|
||||
}
|
||||
|
||||
def parseFile(path: String): Tree = {
|
||||
val batchSourceFile = new BatchSourceFile(AbstractFile.getFile(path))
|
||||
parse(batchSourceFile, batchSourceFile.content.array)
|
||||
}
|
||||
|
||||
private def parse(batchSourceFile: BatchSourceFile, code: Array[Char]): Tree = {
|
||||
try {
|
||||
val parser = new syntaxAnalyzer.SourceFileParser(new ScriptSourceFile(batchSourceFile, code, 0))
|
||||
val tree = parser.templateStatSeq(false)._2
|
||||
parser.makePackaging(0, parser.atPos(0, 0, 0)(Ident(nme.EMPTY_PACKAGE_NAME)), tree)
|
||||
} catch {
|
||||
case _: Throwable => {
|
||||
val unit = new CompilationUnit(batchSourceFile)
|
||||
val unitParser = new syntaxAnalyzer.UnitParser(unit) {
|
||||
override def showSyntaxErrors() {}
|
||||
}
|
||||
unitParser.smartParse()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Sonar Scala Plugin
|
||||
* Copyright (C) 2011 - 2014 All contributors
|
||||
* dev@sonar.codehaus.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
|
||||
*/
|
||||
package org.sonar.plugins.scala.compiler
|
||||
|
||||
/**
|
||||
* Represent a token. Lines must start at 1.
|
||||
*
|
||||
* @since 0.1
|
||||
*/
|
||||
case class Token(tokenType: Int, line: Int, tokenVal: String)
|
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Sonar Scala Plugin
|
||||
* Copyright (C) 2011 - 2014 All contributors
|
||||
* dev@sonar.codehaus.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
|
||||
*/
|
||||
package org.sonar.plugins.scala.language
|
||||
|
||||
import org.sonar.plugins.scala.compiler.{ Compiler, Parser }
|
||||
|
||||
/**
|
||||
* This object is a helper object for detecting valid Scala code
|
||||
* in a given piece of source code.
|
||||
*
|
||||
* @author Felix Müller
|
||||
* @since 0.1
|
||||
*/
|
||||
object CodeDetector {
|
||||
|
||||
import Compiler._
|
||||
|
||||
private lazy val parser = new Parser()
|
||||
|
||||
def hasDetectedCode(code: String) = {
|
||||
|
||||
def lookingForSyntaxTreesWithCode(tree: Tree) : Boolean = tree match {
|
||||
|
||||
case PackageDef(identifier: RefTree, content) =>
|
||||
if (!identifier.name.equals(nme.EMPTY_PACKAGE_NAME)) {
|
||||
true
|
||||
} else {
|
||||
content.exists(lookingForSyntaxTreesWithCode)
|
||||
}
|
||||
|
||||
case Apply(function, args) =>
|
||||
args.exists(lookingForSyntaxTreesWithCode)
|
||||
|
||||
case ClassDef(_, _, _, _)
|
||||
| ModuleDef(_, _, _)
|
||||
| ValDef(_, _, _, _)
|
||||
| DefDef(_, _, _, _, _, _)
|
||||
| Function(_ , _)
|
||||
| Assign(_, _)
|
||||
| LabelDef(_, _, _) =>
|
||||
true
|
||||
|
||||
case _ =>
|
||||
false
|
||||
}
|
||||
|
||||
lookingForSyntaxTreesWithCode(parser.parse(code))
|
||||
}
|
||||
}
|
3
pmd-scala/src/site/markdown/index.md
Normal file
3
pmd-scala/src/site/markdown/index.md
Normal file
@ -0,0 +1,3 @@
|
||||
# PMD Scala
|
||||
|
||||
Only CPD is supported. There are no PMD rules for Scala.
|
12
pmd-scala/src/site/site.xml
Normal file
12
pmd-scala/src/site/site.xml
Normal file
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project
|
||||
xmlns="http://maven.apache.org/DECORATION/1.1.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/DECORATION/1.1.0 http://maven.apache.org/xsd/decoration-1.1.0.xsd"
|
||||
name="PMD Scala">
|
||||
|
||||
<body>
|
||||
<menu ref="parent"/>
|
||||
<menu ref="reports"/>
|
||||
</body>
|
||||
</project>
|
@ -0,0 +1,27 @@
|
||||
/**
|
||||
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
|
||||
*/
|
||||
package net.sourceforge.pmd;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
||||
import net.sourceforge.pmd.lang.LanguageRegistry;
|
||||
import net.sourceforge.pmd.lang.LanguageVersion;
|
||||
import net.sourceforge.pmd.lang.scala.ScalaLanguageModule;
|
||||
|
||||
import org.junit.runners.Parameterized.Parameters;
|
||||
|
||||
public class LanguageVersionTest extends AbstractLanguageVersionTest {
|
||||
|
||||
public LanguageVersionTest(String name, String terseName, String version, LanguageVersion expected) {
|
||||
super(name, terseName, version, expected);
|
||||
}
|
||||
|
||||
@Parameters
|
||||
public static Collection<Object[]> data() {
|
||||
return Arrays.asList(new Object[][] {
|
||||
{ ScalaLanguageModule.NAME, ScalaLanguageModule.TERSE_NAME, "", LanguageRegistry.getLanguage(ScalaLanguageModule.NAME).getDefaultVersion() }
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
/**
|
||||
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
|
||||
*/
|
||||
package net.sourceforge.pmd.cpd;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import net.sourceforge.pmd.testframework.AbstractTokenizerTest;
|
||||
import net.sourceforge.pmd.testframework.StreamUtil;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.sonar.plugins.scala.cpd.ScalaTokenizer;
|
||||
|
||||
|
||||
public class ScalaTokenizerTest extends AbstractTokenizerTest {
|
||||
|
||||
private static final String FILENAME = "sample-LiftActor.scala";
|
||||
|
||||
private File tempFile;
|
||||
|
||||
@Before
|
||||
@Override
|
||||
public void buildTokenizer() {
|
||||
createTempFileOnDisk();
|
||||
|
||||
this.tokenizer = new ScalaTokenizer();
|
||||
this.sourceCode = new SourceCode(new SourceCode.FileCodeLoader(tempFile, "UTF-8"));
|
||||
}
|
||||
|
||||
private void createTempFileOnDisk() {
|
||||
try {
|
||||
this.tempFile = File.createTempFile("scala-tokenizer-test-", ".scala");
|
||||
|
||||
FileUtils.writeStringToFile(tempFile, getSampleCode(), "UTF-8");
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Unable to create temporary file on disk for Scala tokenizer test", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSampleCode() {
|
||||
return StreamUtil.toString(ScalaTokenizer.class.getResourceAsStream(FILENAME));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void tokenizeTest() throws IOException {
|
||||
this.expectedTokenCount = 2591;
|
||||
super.tokenizeTest();
|
||||
}
|
||||
|
||||
@After
|
||||
public void cleanUp() {
|
||||
FileUtils.deleteQuietly(this.tempFile);
|
||||
this.tempFile = null;
|
||||
}
|
||||
|
||||
public static junit.framework.Test suite() {
|
||||
return new junit.framework.JUnit4TestAdapter(ScalaTokenizerTest.class);
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user