-
Notifications
You must be signed in to change notification settings - Fork 307
Generalize the removal of compiler argument options during indexing #1314
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
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
//===----------------------------------------------------------------------===// | ||
// | ||
// This source file is part of the Swift.org open source project | ||
// | ||
// Copyright (c) 2014 - 2024 Apple Inc. and the Swift project authors | ||
// Licensed under Apache License v2.0 with Runtime Library Exception | ||
// | ||
// See https://swift.org/LICENSE.txt for license information | ||
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
@_spi(Testing) public struct CompilerCommandLineOption { | ||
/// Return value of `matches(argument:)`. | ||
public enum Match { | ||
/// The `CompilerCommandLineOption` matched the command line argument. The next element in the command line is a | ||
/// separate argument and should not be removed. | ||
case removeOption | ||
|
||
/// The `CompilerCommandLineOption` matched the command line argument. The next element in the command line is an | ||
/// argument to this option and should be removed as well. | ||
case removeOptionAndNextArgument | ||
} | ||
|
||
public enum DashSpelling { | ||
case singleDash | ||
case doubleDash | ||
} | ||
|
||
public enum ArgumentStyles { | ||
/// A command line option where arguments can be passed without a space such as `-MT/file.txt`. | ||
case noSpace | ||
/// A command line option where the argument is passed, separated by a space (eg. `--serialize-diagnostics /file.txt`) | ||
case separatedBySpace | ||
/// A command line option where the argument is passed after a `=`, eg. `-fbuild-session-file=`. | ||
case separatedByEqualSign | ||
} | ||
|
||
/// The name of the option, without any preceeding `-` or `--`. | ||
private let name: String | ||
|
||
/// Whether the option can be spelled with one or two dashes. | ||
private let dashSpellings: [DashSpelling] | ||
|
||
/// The ways that arguments can specified after the option. Empty if the option is a flag that doesn't take any | ||
/// argument. | ||
private let argumentStyles: [ArgumentStyles] | ||
|
||
public static func flag(_ name: String, _ dashSpellings: [DashSpelling]) -> CompilerCommandLineOption { | ||
precondition(!dashSpellings.isEmpty) | ||
return CompilerCommandLineOption(name: name, dashSpellings: dashSpellings, argumentStyles: []) | ||
} | ||
|
||
public static func option( | ||
_ name: String, | ||
_ dashSpellings: [DashSpelling], | ||
_ argumentStyles: [ArgumentStyles] | ||
) -> CompilerCommandLineOption { | ||
precondition(!dashSpellings.isEmpty) | ||
precondition(!argumentStyles.isEmpty) | ||
return CompilerCommandLineOption(name: name, dashSpellings: dashSpellings, argumentStyles: argumentStyles) | ||
} | ||
|
||
public func matches(argument: String) -> Match? { | ||
let argumentName: Substring | ||
if argument.hasPrefix("--") { | ||
if dashSpellings.contains(.doubleDash) { | ||
argumentName = argument.dropFirst(2) | ||
} else { | ||
return nil | ||
} | ||
} else if argument.hasPrefix("-") { | ||
if dashSpellings.contains(.singleDash) { | ||
argumentName = argument.dropFirst(1) | ||
} else { | ||
return nil | ||
} | ||
} else { | ||
return nil | ||
} | ||
guard argumentName.hasPrefix(self.name) else { | ||
// Fast path in case the argument doesn't match. | ||
return nil | ||
} | ||
|
||
// Examples: | ||
// - self.name: "emit-module", argument: "-emit-module", then textAfterArgumentName: "" | ||
// - self.name: "o", argument: "-o", then textAfterArgumentName: "" | ||
// - self.name: "o", argument: "-output-file-map", then textAfterArgumentName: "utput-file-map" | ||
// - self.name: "MT", argument: "-MT/path/to/depfile", then textAfterArgumentName: "/path/to/depfile" | ||
// - self.name: "fbuild-session-file", argument: "-fbuild-session-file=/path/to/file", then textAfterArgumentName: "=/path/to/file" | ||
let textAfterArgumentName: Substring = argumentName.dropFirst(self.name.count) | ||
|
||
if argumentStyles.isEmpty { | ||
if textAfterArgumentName.isEmpty { | ||
return .removeOption | ||
} | ||
// The command line option is a flag but there is text remaining after the argument name. Thus the flag didn't | ||
// match. Eg. self.name: "o" and argument: "-output-file-map" | ||
return nil | ||
} | ||
|
||
for argumentStyle in argumentStyles { | ||
switch argumentStyle { | ||
case .noSpace where !textAfterArgumentName.isEmpty: | ||
return .removeOption | ||
case .separatedBySpace where textAfterArgumentName.isEmpty: | ||
return .removeOptionAndNextArgument | ||
case .separatedByEqualSign where textAfterArgumentName.hasPrefix("="): | ||
return .removeOption | ||
default: | ||
break | ||
} | ||
} | ||
return nil | ||
} | ||
} | ||
|
||
extension Array<CompilerCommandLineOption> { | ||
func firstMatch(for argument: String) -> CompilerCommandLineOption.Match? { | ||
for optionToRemove in self { | ||
if let match = optionToRemove.matches(argument: argument) { | ||
return match | ||
} | ||
} | ||
return nil | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
56 changes: 56 additions & 0 deletions
56
Tests/SemanticIndexTests/CompilerCommandLineOptionMatchingTests.swift
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
//===----------------------------------------------------------------------===// | ||
// | ||
// This source file is part of the Swift.org open source project | ||
// | ||
// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors | ||
// Licensed under Apache License v2.0 with Runtime Library Exception | ||
// | ||
// See https://swift.org/LICENSE.txt for license information | ||
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
@_spi(Testing) import SemanticIndex | ||
import XCTest | ||
|
||
final class CompilerCommandLineOptionMatchingTests: XCTestCase { | ||
func testFlags() { | ||
assertOption(.flag("a", [.singleDash]), "-a", .removeOption) | ||
assertOption(.flag("a", [.doubleDash]), "--a", .removeOption) | ||
assertOption(.flag("a", [.singleDash, .doubleDash]), "-a", .removeOption) | ||
assertOption(.flag("a", [.singleDash, .doubleDash]), "--a", .removeOption) | ||
assertOption(.flag("a", [.singleDash]), "-another", nil) | ||
assertOption(.flag("a", [.singleDash]), "--a", nil) | ||
assertOption(.flag("a", [.doubleDash]), "-a", nil) | ||
} | ||
|
||
func testOptions() { | ||
assertOption(.option("a", [.singleDash], [.noSpace]), "-a/file.txt", .removeOption) | ||
assertOption(.option("a", [.singleDash], [.noSpace]), "-another", .removeOption) | ||
assertOption(.option("a", [.singleDash], [.separatedByEqualSign]), "-a=/file.txt", .removeOption) | ||
assertOption(.option("a", [.singleDash], [.separatedByEqualSign]), "-a/file.txt", nil) | ||
assertOption(.option("a", [.singleDash], [.separatedBySpace]), "-a", .removeOptionAndNextArgument) | ||
assertOption(.option("a", [.singleDash], [.separatedBySpace]), "-another", nil) | ||
assertOption(.option("a", [.singleDash], [.separatedBySpace]), "-a=/file.txt", nil) | ||
assertOption(.option("a", [.singleDash], [.noSpace, .separatedBySpace]), "-a/file.txt", .removeOption) | ||
assertOption(.option("a", [.singleDash], [.noSpace, .separatedBySpace]), "-a=/file.txt", .removeOption) | ||
assertOption(.option("a", [.singleDash], [.noSpace, .separatedBySpace]), "-a", .removeOptionAndNextArgument) | ||
assertOption(.option("a", [.singleDash], [.separatedByEqualSign, .separatedBySpace]), "-a/file.txt", nil) | ||
assertOption(.option("a", [.singleDash], [.separatedByEqualSign, .separatedBySpace]), "-a=file.txt", .removeOption) | ||
assertOption( | ||
.option("a", [.singleDash], [.separatedByEqualSign, .separatedBySpace]), | ||
"-a", | ||
.removeOptionAndNextArgument | ||
) | ||
} | ||
} | ||
|
||
fileprivate func assertOption( | ||
_ option: CompilerCommandLineOption, | ||
_ argument: String, | ||
_ expected: CompilerCommandLineOption.Match?, | ||
file: StaticString = #filePath, | ||
line: UInt = #line | ||
) { | ||
XCTAssertEqual(option.matches(argument: argument), expected, file: file, line: line) | ||
} |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.