@@ -42,6 +42,8 @@ This is useful to find duplicated sections in XML files.
|
||||
* [#2288](https://github.com/pmd/pmd/issues/2288): \[java] JUnitTestsShouldIncludeAssert: Add support for Hamcrest MatcherAssert.assertThat
|
||||
* java-errorprone
|
||||
* [#2477](https://github.com/pmd/pmd/issues/2477): \[java] JUnitSpelling false-positive for JUnit5/4 tests
|
||||
* swift
|
||||
* [#2473](https://github.com/pmd/pmd/issues/2473): \[swift] Swift 5 (up to 5.2) support for CPD
|
||||
|
||||
### API Changes
|
||||
|
||||
@@ -67,6 +69,7 @@ definitive API.
|
||||
* [#2452](https://github.com/pmd/pmd/pull/2452): \[doc] Fix "Making Rulesets" doc sample code indentation - [Artur Dryomov](https://github.com/arturdryomov)
|
||||
* [#2457](https://github.com/pmd/pmd/pull/2457): \[xml] Adding XML to CPD supported languages - [Fernando Cosso](https://github.com/xnYi9wRezm)
|
||||
* [#2469](https://github.com/pmd/pmd/pull/2469): \[apex] fix false positive unused variable if only a method is called - [Gwilym Kuiper](https://github.com/gwilymatgearset)
|
||||
* [#2475](https://github.com/pmd/pmd/pull/2475): \[swift] Swift 4.2-5.2 support - [kenji21](https://github.com/kenji21)
|
||||
* [#2478](https://github.com/pmd/pmd/pull/2478): \[java] New rule: LiteralsFirstInComparisons - [John-Teng](https://github.com/John-Teng)
|
||||
* [#2479](https://github.com/pmd/pmd/pull/2479): \[java] False positive with Hamcrest's assertThat - [andreoss](https://github.com/andreoss)
|
||||
* [#2481](https://github.com/pmd/pmd/pull/2481): \[java] Fix JUnitSpellingRule false positive - [Artem Krosheninnikov](https://github.com/KroArtem)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Downloaded on 2016/03/02 from https://github.com/sleekbyte/tailor/blob/master/src/main/antlr/com/sleekbyte/tailor/antlr/Swift.g4
|
||||
|
||||
// https://github.com/apple/swift/blob/master/CHANGELOG.md
|
||||
/*
|
||||
* [The "BSD license"]
|
||||
* Copyright (c) 2014 Terence Parr
|
||||
@@ -871,7 +871,7 @@ classRequirement: 'class' ;
|
||||
|
||||
// GRAMMAR OF A COMPILER CONTROL STATEMENT
|
||||
|
||||
compilerControlStatement: conditionalCompilationBlock | lineControlStatement ;
|
||||
compilerControlStatement: conditionalCompilationBlock | lineControlStatement | warningCompilationStatement ;
|
||||
|
||||
// GRAMMAR OF A CONDITIONAL COMPILATION BLOCK
|
||||
|
||||
@@ -908,6 +908,8 @@ lineControlStatement: '#sourceLocation' '(' 'file' ':' fileName ',' 'line' ':' l
|
||||
lineNumber: integerLiteral ;
|
||||
fileName: SingleStringLiteral ;
|
||||
|
||||
warningCompilationStatement: '#warning' | '#error' '(' SingleStringLiteral ')' ;
|
||||
|
||||
// ---------- Lexical Structure -----------
|
||||
|
||||
BooleanLiteral: 'true' | 'false' ;
|
||||
@@ -948,7 +950,7 @@ grammarString:
|
||||
'red' | 'blue' | 'green' | 'alpha' | 'resourceName' | 'of' | 'type' ;
|
||||
|
||||
OperatorHead
|
||||
: '/' | '=' | '-' | '+' | '!' | '*' | '%' | '<' | '>' | '&' | '|' | '^' | '~' | '?'
|
||||
: '/' | '=' | '-' | '+' | '!' | '*' | '%' | '<' | '>' | '&' | '|' | '^' | '~' | '?' | '$'
|
||||
| [\u00A1-\u00A7]
|
||||
| [\u00A9\u00AB\u00AC\u00AE]
|
||||
| [\u00B0-\u00B1\u00B6\u00BB\u00BF\u00D7\u00F7]
|
||||
|
||||
@@ -6,32 +6,54 @@ package net.sourceforge.pmd.cpd;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
|
||||
import net.sourceforge.pmd.testframework.AbstractTokenizerTest;
|
||||
|
||||
@RunWith(Parameterized.class)
|
||||
public class SwiftTokenizerTest extends AbstractTokenizerTest {
|
||||
|
||||
private static final String FILENAME = "BTree.swift";
|
||||
private final String filename;
|
||||
private final int nExpectedTokens;
|
||||
|
||||
public SwiftTokenizerTest(String filename, int nExpectedTokens) {
|
||||
this.filename = filename;
|
||||
this.nExpectedTokens = nExpectedTokens;
|
||||
}
|
||||
|
||||
@Parameterized.Parameters
|
||||
public static Collection<Object[]> data() {
|
||||
return Arrays.asList(
|
||||
new Object[] { "Swift5.2.swift", 90 },
|
||||
new Object[] { "Swift5.1.swift", 242 },
|
||||
new Object[] { "Swift5.0.swift", 172 },
|
||||
new Object[] { "Swift4.2.swift", 91 },
|
||||
new Object[] { "BTree.swift", 4239 }
|
||||
);
|
||||
}
|
||||
|
||||
@Before
|
||||
@Override
|
||||
public void buildTokenizer() throws IOException {
|
||||
this.tokenizer = new SwiftTokenizer();
|
||||
this.sourceCode = new SourceCode(new SourceCode.StringCodeLoader(this.getSampleCode(), FILENAME));
|
||||
this.sourceCode = new SourceCode(new SourceCode.StringCodeLoader(this.getSampleCode(), this.filename));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSampleCode() throws IOException {
|
||||
return IOUtils.toString(SwiftTokenizer.class.getResourceAsStream(FILENAME), StandardCharsets.UTF_8);
|
||||
return IOUtils.toString(SwiftTokenizer.class.getResourceAsStream(this.filename), StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void tokenizeTest() throws IOException {
|
||||
this.expectedTokenCount = 4239;
|
||||
this.expectedTokenCount = nExpectedTokens;
|
||||
super.tokenizeTest();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
// file for supporting swift 4.2 changes : https://github.com/apple/swift/blob/master/CHANGELOG.md#swift-42
|
||||
// can be compiled with: swift pmd-swift/src/test/resources/net/sourceforge/pmd/cpd/Swift4.2.swift
|
||||
|
||||
let diceRoll = Int.random(in: 1 ... 6)
|
||||
let randomUnit = Double.random(in: 0 ..< 1)
|
||||
let randomBool = Bool.random()
|
||||
|
||||
// https://github.com/apple/swift-evolution/blob/master/proposals/0193-cross-module-inlining-and-specialization.md
|
||||
public class C {
|
||||
public func f() {}
|
||||
}
|
||||
|
||||
public class Cbis {
|
||||
@usableFromInline internal class D {
|
||||
@usableFromInline internal func f() {}
|
||||
|
||||
@inlinable internal func g() {}
|
||||
}
|
||||
}
|
||||
|
||||
// https://github.com/apple/swift-evolution/blob/master/proposals/0196-diagnostic-directives.md
|
||||
#warning("this is incomplete")
|
||||
|
||||
#if MY_BUILD_CONFIG && MY_OTHER_BUILD_CONFIG
|
||||
#error("MY_BUILD_CONFIG and MY_OTHER_BUILD_CONFIG cannot both be set")
|
||||
#endif
|
||||
@@ -0,0 +1,40 @@
|
||||
// file for supporting swift 5.0 changes : https://github.com/apple/swift/blob/master/CHANGELOG.md#swift-5
|
||||
|
||||
// https://github.com/apple/swift-evolution/blob/master/proposals/0235-add-result.md
|
||||
enum Result<Success, Failure: Error> {
|
||||
case success(Success)
|
||||
case failure(Failure)
|
||||
}
|
||||
|
||||
// https://github.com/apple/swift-evolution/blob/master/proposals/0228-fix-expressiblebystringinterpolation.md
|
||||
struct MyType {}
|
||||
#if compiler(<5.0)
|
||||
extension MyType : _ExpressibleByStringInterpolation { }
|
||||
#else
|
||||
extension MyType : ExpressibleByStringInterpolation { }
|
||||
#endif
|
||||
|
||||
//
|
||||
func foo(_ fn: @autoclosure () -> Int) {}
|
||||
func bar(_ fn: @autoclosure () -> Int) {
|
||||
//foo(fn) // Incorrect, `fn` can't be forwarded and has to be called
|
||||
foo(fn()) // Ok
|
||||
}
|
||||
|
||||
// https://github.com/apple/swift-evolution/blob/master/proposals/0216-dynamic-callable.md
|
||||
@dynamicCallable
|
||||
struct ToyCallable {
|
||||
func dynamicallyCall(withArguments: [Int]) {}
|
||||
func dynamicallyCall(withKeywordArguments: KeyValuePairs<String, Int>) {}
|
||||
}
|
||||
let toy = ToyCallable()
|
||||
toy(1, 2, 3) // desugars to `x.dynamicallyCall(withArguments: [1, 2, 3])`
|
||||
toy(label: 1, 2) // desugars to `x.dynamicallyCall(withKeywordArguments: ["label": 1, "": 2])`
|
||||
|
||||
// https://github.com/apple/swift-evolution/blob/master/proposals/0227-identity-keypath.md
|
||||
let id = \Int.self
|
||||
|
||||
var x = 2
|
||||
print(x[keyPath: id]) // prints 2
|
||||
x[keyPath: id] = 3
|
||||
print(x[keyPath: id]) // prints 3
|
||||
@@ -0,0 +1,73 @@
|
||||
// file for supporting swift 5.1 changes : https://github.com/apple/swift/blob/master/CHANGELOG.md#swift-5
|
||||
|
||||
// https://github.com/apple/swift-evolution/blob/master/proposals/0258-property-wrappers.md#property-wrapper-types-in-the-wild
|
||||
// https://developer.apple.com/documentation/combine/published
|
||||
// Publishing a property with the @Published attribute creates a publisher of this type. You access the publisher with the $ operator, as shown here:
|
||||
import Combine
|
||||
class Weather {
|
||||
@Published var temperature: Double
|
||||
init(temperature: Double) {
|
||||
self.temperature = temperature
|
||||
}
|
||||
}
|
||||
|
||||
let weather = Weather(temperature: 20)
|
||||
let cancellable = weather.$temperature
|
||||
.sink() {
|
||||
print ("Temperature now: \($0)")
|
||||
}
|
||||
weather.temperature = 25
|
||||
|
||||
// Prints:
|
||||
// Temperature now: 20.0
|
||||
// Temperature now: 25.0
|
||||
|
||||
// https://github.com/apple/swift-evolution/blob/master/proposals/0244-opaque-result-types.md
|
||||
func makeMeACollection() -> some Collection {
|
||||
return [1, 2, 3]
|
||||
}
|
||||
|
||||
// https://github.com/apple/swift-evolution/blob/master/proposals/0252-keypath-dynamic-member-lookup.md
|
||||
@dynamicMemberLookup
|
||||
struct Lens<T> {
|
||||
let getter: () -> T
|
||||
let setter: (T) -> Void
|
||||
|
||||
var value: T {
|
||||
get {
|
||||
return getter()
|
||||
}
|
||||
set {
|
||||
setter(newValue)
|
||||
}
|
||||
}
|
||||
|
||||
subscript<U>(dynamicMember keyPath: WritableKeyPath<T, U>) -> Lens<U> {
|
||||
return Lens<U>(
|
||||
getter: { self.value[keyPath: keyPath] },
|
||||
setter: { self.value[keyPath: keyPath] = $0 })
|
||||
}
|
||||
}
|
||||
|
||||
// https://github.com/apple/swift-evolution/blob/master/proposals/0242-default-values-memberwise.md
|
||||
struct Dog {
|
||||
var name = "Generic dog name"
|
||||
var age = 0
|
||||
|
||||
// The synthesized memberwise initializer
|
||||
init(name: String = "Generic dog name", age: Int = 0) {}
|
||||
}
|
||||
|
||||
let sparky = Dog(name: "Sparky") // Dog(name: "Sparky", age: 0)
|
||||
|
||||
|
||||
// https://bugs.swift.org/browse/SR-7799
|
||||
enum Foo { case zero, one }
|
||||
|
||||
let foo: Foo? = .zero
|
||||
|
||||
switch foo {
|
||||
case .zero: break
|
||||
case .one: break
|
||||
case .none: break
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// file for supporting swift 5.2 changes : https://github.com/apple/swift/blob/master/CHANGELOG.md#swift-52
|
||||
|
||||
// https://github.com/apple/swift-evolution/blob/master/proposals/0253-callable.md
|
||||
struct Adder {
|
||||
var base: Int
|
||||
func callAsFunction(_ x: Int) -> Int {
|
||||
return x + base
|
||||
}
|
||||
}
|
||||
var adder = Adder(base: 3)
|
||||
adder(10) // returns 13, same as `adder.callAsFunction(10)`
|
||||
|
||||
// https://github.com/apple/swift-evolution/blob/master/proposals/0249-key-path-literal-function-expressions.md
|
||||
struct User {
|
||||
let email: String
|
||||
let isAdmin: Bool
|
||||
}
|
||||
|
||||
users.map(\.email) // this is equivalent to: users.map { $0[keyPath: \User.email] }
|
||||
|
||||
// https://bugs.swift.org/browse/SR-6118
|
||||
struct Subscriptable {
|
||||
subscript(x: Int, y: Int = 0) {
|
||||
...
|
||||
}
|
||||
}
|
||||
|
||||
let s = Subscriptable()
|
||||
print(s[0])
|
||||
Reference in New Issue
Block a user