@@ -1825,7 +1825,8 @@ static bool plusEqualOneIncrementForConvertingCStyleForLoop(TypeChecker &TC, con
1825
1825
}
1826
1826
1827
1827
static void checkCStyleForLoop (TypeChecker &TC, const ForStmt *FS) {
1828
- // If we're missing semi-colons we'll already be erroring out, and this may not even have been intended as C-style.
1828
+ // If we're missing semi-colons we'll already be erroring out, and this may
1829
+ // not even have been intended as C-style.
1829
1830
if (FS->getFirstSemicolonLoc ().isInvalid () || FS->getSecondSemicolonLoc ().isInvalid ())
1830
1831
return ;
1831
1832
@@ -1835,15 +1836,16 @@ static void checkCStyleForLoop(TypeChecker &TC, const ForStmt *FS) {
1835
1836
1836
1837
// Verify that there is only one loop variable, and it is declared here.
1837
1838
auto initializers = FS->getInitializerVarDecls ();
1838
- PatternBindingDecl *loopVarDecl = initializers.size () == 2 ? dyn_cast<PatternBindingDecl>(initializers[0 ]) : nullptr ;
1839
+ PatternBindingDecl *loopVarDecl = initializers.size () == 2 ?
1840
+ dyn_cast<PatternBindingDecl>(initializers[0 ]) : nullptr ;
1839
1841
if (!loopVarDecl || loopVarDecl->getNumPatternEntries () != 1 )
1840
1842
return ;
1841
1843
1842
1844
VarDecl *loopVar = dyn_cast<VarDecl>(initializers[1 ]);
1843
1845
Expr *startValue = loopVarDecl->getInit (0 );
1844
1846
Expr *endValue = endConditionValueForConvertingCStyleForLoop (FS, loopVar);
1845
1847
bool strideByOne = unaryIncrementForConvertingCStyleForLoop (FS, loopVar) ||
1846
- plusEqualOneIncrementForConvertingCStyleForLoop (TC, FS, loopVar);
1848
+ plusEqualOneIncrementForConvertingCStyleForLoop (TC, FS, loopVar);
1847
1849
1848
1850
if (!loopVar || !startValue || !endValue || !strideByOne)
1849
1851
return ;
@@ -1859,16 +1861,78 @@ static void checkCStyleForLoop(TypeChecker &TC, const ForStmt *FS) {
1859
1861
return ;
1860
1862
}
1861
1863
1862
- SourceLoc loopPatternEnd = Lexer::getLocForEndOfToken (TC.Context .SourceMgr , loopVarDecl->getPattern (0 )->getEndLoc ());
1863
- SourceLoc endOfIncrementLoc = Lexer::getLocForEndOfToken (TC.Context .SourceMgr , FS->getIncrement ().getPtrOrNull ()->getEndLoc ());
1864
+ SourceLoc loopPatternEnd =
1865
+ Lexer::getLocForEndOfToken (TC.Context .SourceMgr ,
1866
+ loopVarDecl->getPattern (0 )->getEndLoc ());
1867
+ SourceLoc endOfIncrementLoc =
1868
+ Lexer::getLocForEndOfToken (TC.Context .SourceMgr ,
1869
+ FS->getIncrement ().getPtrOrNull ()->getEndLoc ());
1864
1870
1865
1871
diagnostic
1866
1872
.fixItRemoveChars (loopVarDecl->getLoc (), loopVar->getLoc ())
1867
1873
.fixItReplaceChars (loopPatternEnd, startValue->getStartLoc (), " in " )
1868
- .fixItReplaceChars (FS->getFirstSemicolonLoc (), endValue->getStartLoc (), " ..< " )
1874
+ .fixItReplaceChars (FS->getFirstSemicolonLoc (), endValue->getStartLoc (),
1875
+ " ..< " )
1869
1876
.fixItRemoveChars (FS->getSecondSemicolonLoc (), endOfIncrementLoc);
1870
1877
}
1871
1878
1879
+
1880
+ // Perform MiscDiagnostics on Switch Statements.
1881
+ static void checkSwitch (TypeChecker &TC, const SwitchStmt *stmt) {
1882
+ // We want to warn about "case .Foo, .Bar where 1 != 100:" since the where
1883
+ // clause only applies to the second case, and this is surprising.
1884
+ for (auto cs : stmt->getCases ()) {
1885
+ // The case statement can have multiple case items, each can have a where.
1886
+ // If we find a "where", and there is a preceding item without a where, and
1887
+ // if they are on the same source line, then warn.
1888
+ auto items = cs->getCaseLabelItems ();
1889
+
1890
+ // Don't do any work for the vastly most common case.
1891
+ if (items.size () == 1 ) continue ;
1892
+
1893
+ // Ignore the first item, since it can't have preceding ones.
1894
+ for (unsigned i = 1 , e = items.size (); i != e; ++i) {
1895
+ // Must have a where clause.
1896
+ auto where = items[i].getGuardExpr ();
1897
+ if (!where)
1898
+ continue ;
1899
+
1900
+ // Preceding item must not.
1901
+ if (items[i-1 ].getGuardExpr ())
1902
+ continue ;
1903
+
1904
+ // Must be on the same source line.
1905
+ auto prevLoc = items[i-1 ].getStartLoc ();
1906
+ auto thisLoc = items[i].getStartLoc ();
1907
+ if (prevLoc.isInvalid () || thisLoc.isInvalid ())
1908
+ continue ;
1909
+
1910
+ auto &SM = TC.Context .SourceMgr ;
1911
+ auto prevLineCol = SM.getLineAndColumn (prevLoc);
1912
+ if (SM.getLineNumber (thisLoc) != prevLineCol.first )
1913
+ continue ;
1914
+
1915
+ TC.diagnose (items[i].getWhereLoc (), diag::where_on_one_item)
1916
+ .highlight (items[i].getPattern ()->getSourceRange ())
1917
+ .highlight (where->getSourceRange ());
1918
+
1919
+ // Whitespace it out to the same column as the previous item.
1920
+ std::string whitespace (prevLineCol.second -1 , ' ' );
1921
+ TC.diagnose (thisLoc, diag::add_where_newline)
1922
+ .fixItInsert (thisLoc, " \n " +whitespace);
1923
+
1924
+ auto whereRange = SourceRange (items[i].getWhereLoc (),
1925
+ where->getEndLoc ());
1926
+ auto charRange = Lexer::getCharSourceRangeFromSourceRange (SM, whereRange);
1927
+ auto whereText = SM.extractText (charRange);
1928
+ TC.diagnose (prevLoc, diag::duplicate_where)
1929
+ .fixItInsertAfter (items[i-1 ].getEndLoc (), " " + whereText.str ())
1930
+ .highlight (items[i-1 ].getSourceRange ());
1931
+ }
1932
+ }
1933
+ }
1934
+
1935
+
1872
1936
static Optional<ObjCSelector>
1873
1937
parseObjCSelector (ASTContext &ctx, StringRef string) {
1874
1938
// Find the first colon.
@@ -2250,6 +2314,9 @@ void swift::performStmtDiagnostics(TypeChecker &TC, const Stmt *S) {
2250
2314
2251
2315
if (auto forStmt = dyn_cast<ForStmt>(S))
2252
2316
checkCStyleForLoop (TC, forStmt);
2317
+
2318
+ if (auto switchStmt = dyn_cast<SwitchStmt>(S))
2319
+ checkSwitch (TC, switchStmt);
2253
2320
}
2254
2321
2255
2322
// ===----------------------------------------------------------------------===//
0 commit comments