Skip to content

Commit

Permalink
Swift: Handle interpolating and non-interpolating strings.
Browse files Browse the repository at this point in the history
  • Loading branch information
amb1ent committed Aug 16, 2023
1 parent af9638d commit 5e10b0b
Show file tree
Hide file tree
Showing 9 changed files with 189 additions and 13 deletions.
4 changes: 2 additions & 2 deletions src/main/java/io/err0/client/core/ProjectPolicy.java
Original file line number Diff line number Diff line change
Expand Up @@ -501,7 +501,7 @@ public Pattern getReErrorNumber_rb_placeholder() {
private Pattern reErrorNumber_swift = null;
public Pattern getReErrorNumber_swift() {
if (null == reErrorNumber_swift) {
reErrorNumber_swift = Pattern.compile("^\"\\[" + getErrorPrefix() + "-(\\d+)\\]\\s*");
reErrorNumber_swift = Pattern.compile("^#?\"\\[" + getErrorPrefix() + "-(\\d+)\\]\\s*");
}
return reErrorNumber_swift;
}
Expand All @@ -510,7 +510,7 @@ public Pattern getReErrorNumber_swift() {
private Pattern reErrorNumber_swift_textblocks = null;
public Pattern getReErrorNumber_swift_textblocks() {
if (null == reErrorNumber_swift_textblocks) {
reErrorNumber_swift_textblocks = Pattern.compile("^\"\"\"(\\s*)\\[" + getErrorPrefix() + "-(\\d+)\\]\\s*");
reErrorNumber_swift_textblocks = Pattern.compile("^#?\"\"\"(\\s*)\\[" + getErrorPrefix() + "-(\\d+)\\]\\s*");
}
return reErrorNumber_swift_textblocks;
}
Expand Down
55 changes: 46 additions & 9 deletions src/main/java/io/err0/client/languages/SwiftSourceCodeParse.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import com.google.gson.JsonArray;
import io.err0.client.Main;
import io.err0.client.core.*;
import io.err0.client.languages.ext.SwiftExtendedInformation;

import java.util.Stack;
import java.util.regex.Matcher;
Expand Down Expand Up @@ -67,6 +68,7 @@ public static SwiftSourceCodeParse lex(final CodePolicy policy, final String sou
boolean inAnnotationString = false;
int commentBlockDepth = 0;
for (int i = 0, l = chars.length; i < l; ++i) {
SwiftExtendedInformation extendedInformation = null;
int depth = currentToken.depth;
final char ch = chars[i];
if (ch == '\n') {
Expand Down Expand Up @@ -143,10 +145,35 @@ public static SwiftSourceCodeParse lex(final CodePolicy policy, final String sou
currentToken.sourceCode.append(ch);
currentToken.depth = depth;
currentToken.startLineNumber = lineNumber;
} else if (ch == '#' && i < l - 1 && chars[i + 1] == '\"') {
parse.tokenList.add(currentToken.finish(lineNumber));
if (i < l - 3 && chars[i + 1] == '\"' && chars[i + 2] == '\"' && chars[i + 3] == '\"') {
currentToken = new Token(n++, currentToken);
extendedInformation = new SwiftExtendedInformation(currentToken);
extendedInformation.nonInterpolating = true;
currentToken.extendedInformation = extendedInformation;
currentToken.type = TokenType.QUOT3_LITERAL;
currentToken.sourceCode.append(ch);
currentToken.sourceCode.append(chars[++i]);
currentToken.sourceCode.append(chars[++i]);
currentToken.depth = depth;
currentToken.startLineNumber = lineNumber;
} else {
currentToken = new Token(n++, currentToken);
extendedInformation = new SwiftExtendedInformation(currentToken);
extendedInformation.nonInterpolating = true;
currentToken.extendedInformation = extendedInformation;
currentToken.type = TokenType.QUOT_LITERAL;
currentToken.sourceCode.append(ch);
currentToken.depth = depth;
currentToken.startLineNumber = lineNumber;
}
} else if (ch == '\"') {
parse.tokenList.add(currentToken.finish(lineNumber));
if (i < l - 2 && chars[i + 1] == '\"' && chars[i + 2] == '\"') {
currentToken = new Token(n++, currentToken);
extendedInformation = new SwiftExtendedInformation(currentToken);
currentToken.extendedInformation = extendedInformation;
currentToken.type = TokenType.QUOT3_LITERAL;
currentToken.sourceCode.append(ch);
currentToken.sourceCode.append(chars[++i]);
Expand All @@ -155,6 +182,8 @@ public static SwiftSourceCodeParse lex(final CodePolicy policy, final String sou
currentToken.startLineNumber = lineNumber;
} else {
currentToken = new Token(n++, currentToken);
extendedInformation = new SwiftExtendedInformation(currentToken);
currentToken.extendedInformation = extendedInformation;
currentToken.type = TokenType.QUOT_LITERAL;
currentToken.sourceCode.append(ch);
currentToken.depth = depth;
Expand Down Expand Up @@ -253,8 +282,12 @@ public static SwiftSourceCodeParse lex(final CodePolicy policy, final String sou
}
break;
case QUOT_LITERAL:
if (ch == '\"') {
extendedInformation = (SwiftExtendedInformation) currentToken.extendedInformation;
if (ch == '\"' && (!extendedInformation.nonInterpolating || (i + 1 < l && chars[i+1] == '#'))) {
currentToken.sourceCode.append(ch);
if (extendedInformation.nonInterpolating) {
currentToken.sourceCode.append(chars[++i]);
}
parse.tokenList.add(currentToken.finish(lineNumber));
currentToken = new Token(n++, currentToken);
currentToken.type = TokenType.SOURCE_CODE;
Expand All @@ -273,10 +306,14 @@ public static SwiftSourceCodeParse lex(final CodePolicy policy, final String sou
break;
case QUOT3_LITERAL:
if (ch == '\"') {
if (i < l-2 && chars[i+1] == '\"' && chars[i+2] == '\"') {
extendedInformation = (SwiftExtendedInformation) currentToken.extendedInformation;
if (i < l-2 && chars[i+1] == '\"' && chars[i+2] == '\"' && (!extendedInformation.nonInterpolating || (i < l - 3 && chars[i+3] == '#'))) {
currentToken.sourceCode.append(ch);
currentToken.sourceCode.append(chars[++i]);
currentToken.sourceCode.append(chars[++i]);
if (extendedInformation.nonInterpolating) {
currentToken.sourceCode.append(chars[++i]);
}
parse.tokenList.add(currentToken.finish(lineNumber));
currentToken = new Token(n++, currentToken);
currentToken.type = TokenType.SOURCE_CODE;
Expand Down Expand Up @@ -318,12 +355,12 @@ public void classifyForErrorCode(ApiProvider apiProvider, GlobalState globalStat
if (globalState.store(errorOrdinal, stateItem, token)) {
token.keepErrorCode = true;
token.errorOrdinal = errorOrdinal;
token.sourceNoErrorCode = token.source.substring(0, 1) + token.source.substring(matcherErrorNumber.end());
token.sourceNoErrorCode = token.source.substring(0, token.extendedInformation.getStringQuoteWidth()) + token.source.substring(matcherErrorNumber.end());
} else {
token.sourceNoErrorCode = token.source = token.source.substring(0,1) + token.source.substring(matcherErrorNumber.end());
token.sourceNoErrorCode = token.source = token.source.substring(0, token.extendedInformation.getStringQuoteWidth()) + token.source.substring(matcherErrorNumber.end());
}
} else {
token.sourceNoErrorCode = token.source = token.source.substring(0,1) + token.source.substring(matcherErrorNumber.end());
token.sourceNoErrorCode = token.source = token.source.substring(0, token.extendedInformation.getStringQuoteWidth()) + token.source.substring(matcherErrorNumber.end());
}
} else if (policy.getCodePolicy().getEnablePlaceholder()) {
Matcher matcherPlaceholder = policy.getReErrorNumber_swift_placeholder().matcher(token.source);
Expand Down Expand Up @@ -367,12 +404,12 @@ public void classifyForErrorCode(ApiProvider apiProvider, GlobalState globalStat
if (globalState.store(errorOrdinal, stateItem, token)) {
token.keepErrorCode = true;
token.errorOrdinal = errorOrdinal;
token.sourceNoErrorCode = "\"\"\"" + matcherErrorNumber.group(1) + token.source.substring(matcherErrorNumber.end());
token.sourceNoErrorCode = token.source.substring(0, token.extendedInformation.getStringQuoteWidth()) + matcherErrorNumber.group(1) + token.source.substring(matcherErrorNumber.end());
} else {
token.sourceNoErrorCode = token.source = "\"\"\"" + matcherErrorNumber.group(1) + token.source.substring(matcherErrorNumber.end());
token.sourceNoErrorCode = token.source = token.source.substring(0, token.extendedInformation.getStringQuoteWidth()) + matcherErrorNumber.group(1) + token.source.substring(matcherErrorNumber.end());
}
} else {
token.sourceNoErrorCode = token.source = "\"\"\"" + matcherErrorNumber.group(1) + token.source.substring(matcherErrorNumber.end());
token.sourceNoErrorCode = token.source = token.source.substring(0, token.extendedInformation.getStringQuoteWidth()) + matcherErrorNumber.group(1) + token.source.substring(matcherErrorNumber.end());
}
} else {
token.classification = Token.Classification.POTENTIAL_ERROR_NUMBER;
Expand Down Expand Up @@ -471,7 +508,7 @@ public void classifyForErrorCode(ApiProvider apiProvider, GlobalState globalStat
break;
case QUOT_LITERAL:
case QUOT3_LITERAL:
if (sourceCode.contains("\\(")) {
if (!((SwiftExtendedInformation)current.extendedInformation).nonInterpolating && sourceCode.contains("\\(")) {
staticLiteral = false;
}
cleaned.append(sourceCode);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package io.err0.client.languages.ext;

import io.err0.client.core.Token;
import io.err0.client.core.TokenExtendedInformation;
import io.err0.client.core.TokenType;

public class SwiftExtendedInformation implements TokenExtendedInformation {

public SwiftExtendedInformation(Token token) {
this.token = token;
}

final Token token;

public boolean nonInterpolating = false;

@Override
public String getStringLiteral() {
String s = null != token.sourceNoErrorCode ? token.sourceNoErrorCode : token.source;
if (null == s) return null;
return s.length() > 1 ? s.substring(this.getStringQuoteWidth(), s.length() - 1) : "";
}

@Override
public int getStringQuoteWidth() {
return (nonInterpolating ? 1 : 0) + (token.type == TokenType.QUOT3_LITERAL ? 3 : 1);
}

@Override
public Token getToken() {
return token;
}

@Override
public boolean escapeErrorCode() {
return false;
}
}
25 changes: 23 additions & 2 deletions src/test/java/io/err0/client/test/Test0054Swift.java
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,27 @@ public void t0001InjectErrorCodes() {
boolean static_literal = metaData.metaData.get("static_literal").getAsBoolean();
assertFalse(static_literal);
}

{
UnitTestApiProvider.MetaData metaData = previousState.metaDataStorage.get(3L);
assertNotNull(metaData);
boolean static_literal = metaData.metaData.get("static_literal").getAsBoolean();
assertTrue(static_literal);
}

{
UnitTestApiProvider.MetaData metaData = previousState.metaDataStorage.get(4L);
assertNotNull(metaData);
boolean static_literal = metaData.metaData.get("static_literal").getAsBoolean();
assertFalse(static_literal);
}

{
UnitTestApiProvider.MetaData metaData = previousState.metaDataStorage.get(5L);
assertNotNull(metaData);
boolean static_literal = metaData.metaData.get("static_literal").getAsBoolean();
assertTrue(static_literal);
}
}

// pass #2 - scan and report (no changes)
Expand Down Expand Up @@ -109,7 +130,7 @@ public void t0001InjectErrorCodes() {

previousState = apiProvider.getState();

assertEquals(2, previousState.metaDataStorage.size());
assertEquals(5, previousState.metaDataStorage.size());
}

// pass #3 - insert
Expand Down Expand Up @@ -149,7 +170,7 @@ public void t0001InjectErrorCodes() {

previousState = apiProvider.getState();

assertEquals(2, previousState.metaDataStorage.size());
assertEquals(5, previousState.metaDataStorage.size());
}
}
}
16 changes: 16 additions & 0 deletions src/test/testdata/0054/01-assert/a.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,20 @@ public struct Structure {
public func dynamicMessage<T>(_ object: T) -> Never where T : String {
fatalError("[E-2] This fatal error is dynamic \(object)")
}

public func notADynamicMessage<T>(_ object: T) -> Never where T : String {
fatalError(#"[E-3] This fatal error is static \(object)"#)
}

public func dynamicMultiLineMessage<T>(_ object: T) -> Never where T : String {
fatalError("""
[E-4] This fatal error is dynamic \(object)
""")
}

public func staticMultiLineMessage<T>(_ object: T) -> Never where T : String {
fatalError(#"""
[E-5] This fatal error is static \(object)
"""#)
}
}
16 changes: 16 additions & 0 deletions src/test/testdata/0054/01/a.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,20 @@ public struct Structure {
public func dynamicMessage<T>(_ object: T) -> Never where T : String {
fatalError("This fatal error is dynamic \(object)")
}

public func notADynamicMessage<T>(_ object: T) -> Never where T : String {
fatalError(#"This fatal error is static \(object)"#)
}

public func dynamicMultiLineMessage<T>(_ object: T) -> Never where T : String {
fatalError("""
This fatal error is dynamic \(object)
""")
}

public func staticMultiLineMessage<T>(_ object: T) -> Never where T : String {
fatalError(#"""
This fatal error is static \(object)
"""#)
}
}
16 changes: 16 additions & 0 deletions src/test/testdata/0054/02/a.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,20 @@ public struct Structure {
public func dynamicMessage<T>(_ object: T) -> Never where T : String {
fatalError("[E-2] This fatal error is dynamic \(object)")
}

public func notADynamicMessage<T>(_ object: T) -> Never where T : String {
fatalError(#"[E-3] This fatal error is static \(object)"#)
}

public func dynamicMultiLineMessage<T>(_ object: T) -> Never where T : String {
fatalError("""
[E-4] This fatal error is dynamic \(object)
""")
}

public func staticMultiLineMessage<T>(_ object: T) -> Never where T : String {
fatalError(#"""
[E-5] This fatal error is static \(object)
"""#)
}
}
16 changes: 16 additions & 0 deletions src/test/testdata/0054/03-assert/a.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,20 @@ public struct Structure {
public func dynamicMessage<T>(_ object: T) -> Never where T : String {
fatalError("[E-2] This fatal error is dynamic \(object)")
}

public func notADynamicMessage<T>(_ object: T) -> Never where T : String {
fatalError(#"[E-3] This fatal error is static \(object)"#)
}

public func dynamicMultiLineMessage<T>(_ object: T) -> Never where T : String {
fatalError("""
[E-4] This fatal error is dynamic \(object)
""")
}

public func staticMultiLineMessage<T>(_ object: T) -> Never where T : String {
fatalError(#"""
[E-5] This fatal error is static \(object)
"""#)
}
}
16 changes: 16 additions & 0 deletions src/test/testdata/0054/03/a.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,20 @@ public struct Structure {
public func dynamicMessage<T>(_ object: T) -> Never where T : String {
fatalError("[E-2] This fatal error is dynamic \(object)")
}

public func notADynamicMessage<T>(_ object: T) -> Never where T : String {
fatalError(#"[E-3] This fatal error is static \(object)"#)
}

public func dynamicMultiLineMessage<T>(_ object: T) -> Never where T : String {
fatalError("""
[E-4] This fatal error is dynamic \(object)
""")
}

public func staticMultiLineMessage<T>(_ object: T) -> Never where T : String {
fatalError(#"""
[E-5] This fatal error is static \(object)
"""#)
}
}

0 comments on commit 5e10b0b

Please sign in to comment.