|
25 | 25 | #include "swift/Parse/Lexer.h"
|
26 | 26 | #include "swift/Parse/Parser.h"
|
27 | 27 | #include "llvm/ADT/MapVector.h"
|
| 28 | +#include "llvm/ADT/StringSwitch.h" |
28 | 29 | #include "llvm/Support/SaveAndRestore.h"
|
29 | 30 | using namespace swift;
|
30 | 31 |
|
@@ -1810,6 +1811,9 @@ class AvailabilityWalker : public ASTWalker {
|
1810 | 1811 | const ApplyExpr *call = nullptr);
|
1811 | 1812 | bool diagnoseIncDecRemoval(const ValueDecl *D, SourceRange R,
|
1812 | 1813 | const AvailableAttr *Attr);
|
| 1814 | + bool diagnoseMemoryLayoutMigration(const ValueDecl *D, SourceRange R, |
| 1815 | + const AvailableAttr *Attr, |
| 1816 | + const ApplyExpr *call); |
1813 | 1817 |
|
1814 | 1818 | /// Walks up from a potential callee to the enclosing ApplyExpr.
|
1815 | 1819 | const ApplyExpr *getEnclosingApplyExpr() const {
|
@@ -1948,9 +1952,12 @@ bool AvailabilityWalker::diagAvailability(const ValueDecl *D, SourceRange R,
|
1948 | 1952 | if (!D)
|
1949 | 1953 | return false;
|
1950 | 1954 |
|
1951 |
| - if (auto *attr = AvailableAttr::isUnavailable(D)) |
| 1955 | + if (auto *attr = AvailableAttr::isUnavailable(D)) { |
1952 | 1956 | if (diagnoseIncDecRemoval(D, R, attr))
|
1953 | 1957 | return true;
|
| 1958 | + if (call && diagnoseMemoryLayoutMigration(D, R, attr, call)) |
| 1959 | + return true; |
| 1960 | + } |
1954 | 1961 |
|
1955 | 1962 | if (TC.diagnoseExplicitUnavailability(D, R, DC, call))
|
1956 | 1963 | return true;
|
@@ -2050,7 +2057,81 @@ bool AvailabilityWalker::diagnoseIncDecRemoval(const ValueDecl *D,
|
2050 | 2057 | return false;
|
2051 | 2058 | }
|
2052 | 2059 |
|
| 2060 | +/// If this is a call to an unavailable sizeof family functions, diagnose it |
| 2061 | +/// with a fixit hint and return true. If not, or if we fail, return false. |
| 2062 | +bool AvailabilityWalker::diagnoseMemoryLayoutMigration(const ValueDecl *D, |
| 2063 | + SourceRange R, |
| 2064 | + const AvailableAttr *Attr, |
| 2065 | + const ApplyExpr *call) { |
| 2066 | + |
| 2067 | + if (!D->getModuleContext()->isStdlibModule()) |
| 2068 | + return false; |
| 2069 | + |
| 2070 | + std::pair<StringRef, bool> KindValue |
| 2071 | + = llvm::StringSwitch<std::pair<StringRef, bool>>(D->getNameStr()) |
| 2072 | + .Case("sizeof", {"size", false}) |
| 2073 | + .Case("alignof", {"alignment", false}) |
| 2074 | + .Case("strideof", {"stride", false}) |
| 2075 | + .Case("sizeofValue", {"size", true}) |
| 2076 | + .Case("alignofValue", {"alignment", true}) |
| 2077 | + .Case("strideofValue", {"stride", true}) |
| 2078 | + .Default({}); |
| 2079 | + |
| 2080 | + if (KindValue.first.empty()) |
| 2081 | + return false; |
| 2082 | + |
| 2083 | + auto Kind = KindValue.first; |
| 2084 | + auto isValue = KindValue.second; |
2053 | 2085 |
|
| 2086 | + auto args = dyn_cast<ParenExpr>(call->getArg()); |
| 2087 | + if (!args) |
| 2088 | + return false; |
| 2089 | + |
| 2090 | + auto subject = args->getSubExpr(); |
| 2091 | + if (!isValue) { |
| 2092 | + // sizeof(x.dynamicType) is equivalent to sizeofValue(x) |
| 2093 | + if (auto DTE = dyn_cast<DynamicTypeExpr>(subject)) { |
| 2094 | + subject = DTE->getBase(); |
| 2095 | + isValue = true; |
| 2096 | + } |
| 2097 | + } |
| 2098 | + |
| 2099 | + EncodedDiagnosticMessage EncodedMessage(Attr->Message); |
| 2100 | + auto diag = TC.diagnose(R.Start, diag::availability_decl_unavailable_msg, |
| 2101 | + D->getFullName(), EncodedMessage.Message); |
| 2102 | + diag.highlight(R); |
| 2103 | + |
| 2104 | + StringRef Prefix = "MemoryLayout<"; |
| 2105 | + StringRef Suffix = ">."; |
| 2106 | + if (isValue) { |
| 2107 | + auto valueType = subject->getType()->getRValueType(); |
| 2108 | + if (!valueType || valueType->is<ErrorType>()) { |
| 2109 | + // If we dont have good argument, We cannot emit fix-it. |
| 2110 | + return true; |
| 2111 | + } |
| 2112 | + |
| 2113 | + // NOTE: We are destructively replacing the source text here. |
| 2114 | + // For instance, `sizeof(x.doSomethig())` => `MemoryLayout<T>.size` where |
| 2115 | + // T is return type of `doSomething()`. If that function have any |
| 2116 | + // side effects, it will break the source. |
| 2117 | + diag.fixItReplace(call->getSourceRange(), |
| 2118 | + (Prefix + valueType->getString() + Suffix + Kind).str()); |
| 2119 | + } else { |
| 2120 | + SourceRange PrefixRange(call->getStartLoc(), args->getLParenLoc()); |
| 2121 | + SourceRange SuffixRange(args->getRParenLoc()); |
| 2122 | + |
| 2123 | + // We must truncate `.self`. |
| 2124 | + // E.g. sizeof(T.self) => MemoryLayout<T>.size |
| 2125 | + if (auto *DSE = dyn_cast<DotSelfExpr>(subject)) |
| 2126 | + SuffixRange.Start = DSE->getDotLoc(); |
| 2127 | + |
| 2128 | + diag |
| 2129 | + .fixItReplace(PrefixRange, Prefix) |
| 2130 | + .fixItReplace(SuffixRange, (Suffix + Kind).str()); |
| 2131 | + } |
| 2132 | + |
| 2133 | + return true; |
| 2134 | +} |
2054 | 2135 |
|
2055 | 2136 | /// Diagnose uses of unavailable declarations.
|
2056 | 2137 | static void diagAvailability(TypeChecker &TC, const Expr *E,
|
|
0 commit comments