Skip to content

Commit 5d8988e

Browse files
Merge branch 'master' of github.com:microsoft/checkedc-clang
2 parents acc17f2 + 5999194 commit 5d8988e

14 files changed

+94
-73
lines changed

clang/include/clang/3C/ConstraintVariables.h

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -240,11 +240,19 @@ class PointerVariableConstraint : public ConstraintVariable {
240240
void addArrayAnnotations(std::stack<std::string> &CheckedArrs,
241241
std::deque<std::string> &EndStrs) const;
242242

243-
// Utility used by the constructor to extract string representation of the
244-
// base type that preserves macros where possible.
243+
// Utility used by the constructor to obtain a string representation of a
244+
// declaration's base type. To preserve macros, this we first try to take
245+
// the type directly from source code. Where that is not possible, the type
246+
// is regenerated from the type in the clang AST.
245247
static std::string extractBaseType(DeclaratorDecl *D, QualType QT,
246248
const Type *Ty, const ASTContext &C);
247249

250+
// Try to extract string representation of the base type for a declaration
251+
// from the source code. If the base type cannot be extracted from source, an
252+
// empty string is returned instead.
253+
static std::string tryExtractBaseType(DeclaratorDecl *D, QualType QT,
254+
const Type *Ty, const ASTContext &C);
255+
248256
// Flag to indicate that this constraint is a part of function prototype
249257
// e.g., Parameters or Return.
250258
bool PartOfFuncPrototype;

clang/include/clang/3C/RewriteUtils.h

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -90,13 +90,14 @@ class FunctionDeclReplacement
9090

9191
SourceRange getSourceRange(SourceManager &SM) const override {
9292
TypeSourceInfo *TSInfo = Decl->getTypeSourceInfo();
93-
if (!TSInfo)
93+
FunctionTypeLoc TypeLoc;
94+
if (TSInfo) {
95+
auto TSInfoLoc = TSInfo->getTypeLoc();
96+
TypeLoc = getBaseTypeLoc(TSInfoLoc).getAs<clang::FunctionTypeLoc>();
97+
}
98+
if (!TSInfo || TypeLoc.isNull())
9499
return SourceRange(Decl->getBeginLoc(),
95100
getFunctionDeclarationEnd(Decl, SM));
96-
FunctionTypeLoc TypeLoc =
97-
getBaseTypeLoc(TSInfo->getTypeLoc()).getAs<clang::FunctionTypeLoc>();
98-
99-
assert("FunctionDecl doesn't have function type?" && !TypeLoc.isNull());
100101

101102
// Function pointer are funky, and require special handling to rewrite the
102103
// return type.

clang/lib/3C/ArrayBoundsInferenceConsumer.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "clang/3C/ConstraintResolver.h"
1616
#include "clang/Analysis/Analyses/Dominators.h"
1717
#include "clang/Analysis/CFG.h"
18+
#include <cctype>
1819
#include <sstream>
1920

2021
static std::set<std::string> LengthVarNamesPrefixes = {"len", "count", "size",

clang/lib/3C/CMakeLists.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,13 @@ set( LLVM_LINK_COMPONENTS
99
DeclRewriter_5C.cpp
1010
)
1111
endif()
12+
13+
if (MSVC)
14+
set_source_files_properties(ArrayBoundsInferenceConsumer.cpp PROPERTIES COMPILE_FLAGS /bigobj)
15+
set_source_files_properties(ConstraintBuilder.cpp PROPERTIES COMPILE_FLAGS /bigobj)
16+
set_source_files_properties(RewriteUtils.cpp PROPERTIES COMPILE_FLAGS /bigobj)
17+
endif()
18+
1219
add_clang_library(clang3C
1320
ABounds.cpp
1421
AVarBoundsInfo.cpp

clang/lib/3C/ConstraintVariables.cpp

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -444,11 +444,10 @@ PointerVariableConstraint::PointerVariableConstraint(
444444
}
445445
}
446446

447-
std::string PointerVariableConstraint::extractBaseType(DeclaratorDecl *D,
448-
QualType QT,
449-
const Type *Ty,
450-
const ASTContext &C) {
451-
std::string BaseTypeStr;
447+
std::string PointerVariableConstraint::tryExtractBaseType(DeclaratorDecl *D,
448+
QualType QT,
449+
const Type *Ty,
450+
const ASTContext &C) {
452451
bool FoundBaseTypeInSrc = false;
453452
if (!QT->isOrContainsCheckedType() && !Ty->getAs<TypedefType>() && D &&
454453
D->getTypeSourceInfo()) {
@@ -459,32 +458,33 @@ std::string PointerVariableConstraint::extractBaseType(DeclaratorDecl *D,
459458
TL = getBaseTypeLoc(TL).getAs<FunctionTypeLoc>();
460459
// FunctionDecl that doesn't have function type? weird
461460
if (TL.isNull())
462-
FoundBaseTypeInSrc = false;
463-
else
464-
TL = TL.getAs<clang::FunctionTypeLoc>().getReturnLoc();
461+
return "";
462+
TL = TL.getAs<clang::FunctionTypeLoc>().getReturnLoc();
465463
} else {
466464
FoundBaseTypeInSrc = D->getType() == QT;
467465
}
468-
TypeLoc BaseLoc = getBaseTypeLoc(TL);
469-
if (!BaseLoc.getAs<TypedefTypeLoc>().isNull()) {
470-
FoundBaseTypeInSrc = false;
471-
} else {
472-
// Only use this type if the type passed as a parameter to this
473-
// constructor agrees with the actual type of the declaration.
474-
SourceRange SR = BaseLoc.getSourceRange();
475-
if (FoundBaseTypeInSrc && SR.isValid()) {
476-
BaseTypeStr = getSourceText(SR, C);
477-
478-
// getSourceText returns the empty string when there's a pointer level
479-
// inside a macro. Not sure how to handle this, so fall back to tyToStr.
480-
if (BaseTypeStr.empty())
481-
FoundBaseTypeInSrc = false;
482-
} else
483-
FoundBaseTypeInSrc = false;
466+
if (!TL.isNull()) {
467+
TypeLoc BaseLoc = getBaseTypeLoc(TL);
468+
// Only proceed if the base type location is not null, amd it is not a
469+
// typedef type location.
470+
if (!BaseLoc.isNull() && BaseLoc.getAs<TypedefTypeLoc>().isNull()) {
471+
SourceRange SR = BaseLoc.getSourceRange();
472+
if (FoundBaseTypeInSrc && SR.isValid())
473+
return getSourceText(SR, C);
474+
}
484475
}
485476
}
477+
478+
return "";
479+
}
480+
481+
std::string PointerVariableConstraint::extractBaseType(DeclaratorDecl *D,
482+
QualType QT,
483+
const Type *Ty,
484+
const ASTContext &C) {
485+
std::string BaseTypeStr = tryExtractBaseType(D, QT, Ty, C);
486486
// Fall back to rebuilding the base type based on type passed to constructor
487-
if (!FoundBaseTypeInSrc)
487+
if (BaseTypeStr.empty())
488488
BaseTypeStr = tyToStr(Ty);
489489

490490
return BaseTypeStr;

clang/lib/3C/Constraints.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -611,7 +611,7 @@ Geq *Constraints::createGeq(Atom *Lhs, Atom *Rhs, const std::string &Rsn,
611611
if (PL != nullptr && PL->valid()) {
612612
// Make this invalid, if the source location is not absolute path
613613
// this is to avoid crashes in clangd.
614-
if (PL->getFileName().c_str()[0] != '/')
614+
if (!llvm::sys::path::is_absolute(PL->getFileName()))
615615
PL = nullptr;
616616
}
617617
assert("Shouldn't be constraining WILD >= VAR" && Lhs != getWild());

clang/lib/3C/Utils.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,7 @@ bool isZeroBoundsExpr(BoundsExpr *BE, const ASTContext &C) {
417417
}
418418

419419
TypeLoc getBaseTypeLoc(TypeLoc T) {
420+
assert(!T.isNull() && "Can't get base location from Null.");
420421
while (!T.getNextTypeLoc().isNull() &&
421422
(!T.getAs<ParenTypeLoc>().isNull() ||
422423
T.getTypePtr()->isPointerType() || T.getTypePtr()->isArrayType()))

clang/lib/Sema/SemaBounds.cpp

Lines changed: 25 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -647,13 +647,12 @@ namespace {
647647
// that are currently known to be valid for the variable.
648648
using BoundsContextTy = llvm::DenseMap<const VarDecl *, BoundsExpr *>;
649649

650-
// EqualExprTy denotes a set of expressions that produce the same value
651-
// as an expression e.
652-
using EqualExprTy = SmallVector<Expr *, 4>;
650+
// ExprSetTy denotes a set of expressions.
651+
using ExprSetTy = SmallVector<Expr *, 4>;
653652

654653
// ExprEqualMapTy denotes a map of an expression e to the set of
655654
// expressions that produce the same value as e.
656-
using ExprEqualMapTy = llvm::DenseMap<Expr *, EqualExprTy>;
655+
using ExprEqualMapTy = llvm::DenseMap<Expr *, ExprSetTy>;
657656

658657
// CheckingState stores the outputs of bounds checking methods.
659658
// These members represent the state during bounds checking
@@ -686,7 +685,7 @@ namespace {
686685
// expression e once checking of e is complete.
687686
//
688687
// SameValue is named G in the Checked C spec.
689-
EqualExprTy SameValue;
688+
ExprSetTy SameValue;
690689

691690
// LostVariables maps a variable declaration V whose observed bounds
692691
// are unknown to a pair <B, W>, where the initial observed bounds B
@@ -932,7 +931,7 @@ namespace {
932931
}
933932
}
934933

935-
void DumpExprsSet(raw_ostream &OS, EqualExprTy Exprs) {
934+
void DumpExprsSet(raw_ostream &OS, ExprSetTy Exprs) {
936935
if (Exprs.size() == 0)
937936
OS << "{ }\n";
938937
else {
@@ -4270,7 +4269,7 @@ namespace {
42704269
// Adjust EquivExprs to account for any uses of V in PrevState.EquivExprs.
42714270
State.EquivExprs.clear();
42724271
for (auto I = PrevState.EquivExprs.begin(); I != PrevState.EquivExprs.end(); ++I) {
4273-
EqualExprTy ExprList;
4272+
ExprSetTy ExprList;
42744273
for (auto InnerList = (*I).begin(); InnerList != (*I).end(); ++InnerList) {
42754274
Expr *E = *InnerList;
42764275
Expr *AdjustedE = ReplaceVariableReferences(S, E, V, OriginalValue, CSS);
@@ -4387,8 +4386,8 @@ namespace {
43874386
// Val is an optional expression that may be contained in the updated
43884387
// SameValue set. If Val is not provided, e is used instead. If Val
43894388
// and e are null, SameValue is not updated.
4390-
void UpdateSameValue(Expr *E, const EqualExprTy SubExprSameValue,
4391-
EqualExprTy &SameValue, Expr *Val = nullptr) {
4389+
void UpdateSameValue(Expr *E, const ExprSetTy SubExprSameValue,
4390+
ExprSetTy &SameValue, Expr *Val = nullptr) {
43924391
Expr *SubExpr = dyn_cast<Expr>(*(E->child_begin()));
43934392
assert(SubExpr);
43944393
ExprEqualMapTy SubExprSameValueSets;
@@ -4412,7 +4411,7 @@ namespace {
44124411
// SameValue set. If Val is not provided, e is used instead. If Val
44134412
// and e are null, SameValue is not updated.
44144413
void UpdateSameValue(Expr *E, ExprEqualMapTy SubExprSameValueSets,
4415-
EqualExprTy &SameValue, Expr *Val = nullptr) {
4414+
ExprSetTy &SameValue, Expr *Val = nullptr) {
44164415
SameValue.clear();
44174416

44184417
if (!Val) Val = E;
@@ -4438,13 +4437,13 @@ namespace {
44384437
// the same value as Val.
44394438
else {
44404439
Expr *ValPrime = nullptr;
4441-
for (llvm::detail::DenseMapPair<Expr *, EqualExprTy> Pair : SubExprSameValueSets) {
4440+
for (llvm::detail::DenseMapPair<Expr *, ExprSetTy> Pair : SubExprSameValueSets) {
44424441
Expr *SubExpr_i = Pair.first;
44434442
// For any modifying subexpression SubExpr_i of e, try to set
44444443
// ValPrime to a nonmodifying expression from the set SameValue_i
44454444
// of expressions that produce the same value as SubExpr_i.
44464445
if (!CheckIsNonModifying(SubExpr_i)) {
4447-
EqualExprTy SameValue_i = Pair.second;
4446+
ExprSetTy SameValue_i = Pair.second;
44484447
for (auto I = SameValue_i.begin(); I != SameValue_i.end(); ++I) {
44494448
Expr *E_i = *I;
44504449
if (CheckIsNonModifying(E_i)) {
@@ -4500,7 +4499,7 @@ namespace {
45004499

45014500
// Check EQ for a variable w != v that produces the same value as v.
45024501
Expr *ValuePreservingV = nullptr;
4503-
EqualExprTy F = GetEqualExprSetContainingExpr(Target, EQ, ValuePreservingV);
4502+
ExprSetTy F = GetEqualExprSetContainingExpr(Target, EQ, ValuePreservingV);
45044503
for (auto I = F.begin(); I != F.end(); ++I) {
45054504
// Account for value-preserving operations on w when searching for
45064505
// a variable w in F. For example, if F contains (T)LValueToRValue(w),
@@ -4922,10 +4921,10 @@ namespace {
49224921
const EquivExprSets EQ2) {
49234922
EquivExprSets IntersectedEQ;
49244923
for (auto I1 = EQ1.begin(); I1 != EQ1.end(); ++I1) {
4925-
EqualExprTy Set1 = *I1;
4924+
ExprSetTy Set1 = *I1;
49264925
for (auto I2 = EQ2.begin(); I2 != EQ2.end(); ++I2) {
4927-
EqualExprTy Set2 = *I2;
4928-
EqualExprTy IntersectedExprSet = IntersectExprSets(Set1, Set2);
4926+
ExprSetTy Set2 = *I2;
4927+
ExprSetTy IntersectedExprSet = IntersectExprSets(Set1, Set2);
49294928
if (IntersectedExprSet.size() > 1)
49304929
IntersectedEQ.push_back(IntersectedExprSet);
49314930
}
@@ -4934,9 +4933,8 @@ namespace {
49344933
}
49354934

49364935
// IntersectExprSets returns the intersection of two sets of expressions.
4937-
EqualExprTy IntersectExprSets(const EqualExprTy Set1,
4938-
const EqualExprTy Set2) {
4939-
EqualExprTy IntersectedSet;
4936+
ExprSetTy IntersectExprSets(const ExprSetTy Set1, const ExprSetTy Set2) {
4937+
ExprSetTy IntersectedSet;
49404938
for (auto I = Set1.begin(); I != Set1.end(); ++I) {
49414939
Expr *E1 = *I;
49424940
if (EqualExprsContainsExpr(Set2, E1))
@@ -4965,11 +4963,11 @@ namespace {
49654963
// e1 may include value-preserving operations. For example, if a set F
49664964
// in EQ contains (T)e, where (T) is a value-preserving cast,
49674965
// ValuePreservingE will be set to (T)e.
4968-
EqualExprTy GetEqualExprSetContainingExpr(Expr *E, EquivExprSets EQ,
4969-
Expr *&ValuePreservingE) {
4966+
ExprSetTy GetEqualExprSetContainingExpr(Expr *E, EquivExprSets EQ,
4967+
Expr *&ValuePreservingE) {
49704968
ValuePreservingE = nullptr;
49714969
for (auto OuterList = EQ.begin(); OuterList != EQ.end(); ++OuterList) {
4972-
EqualExprTy F = *OuterList;
4970+
ExprSetTy F = *OuterList;
49734971
for (auto InnerList = F.begin(); InnerList != F.end(); ++InnerList) {
49744972
Expr *E1 = *InnerList;
49754973
if (EqualValue(S.Context, E, E1, nullptr)) {
@@ -4983,18 +4981,17 @@ namespace {
49834981

49844982
// If e appears in a set F in EQ, GetEqualExprSetContainingExpr
49854983
// returns F. Otherwise, it returns an empty set.
4986-
EqualExprTy GetEqualExprSetContainingExpr(Expr *E, EquivExprSets EQ) {
4984+
ExprSetTy GetEqualExprSetContainingExpr(Expr *E, EquivExprSets EQ) {
49874985
for (auto OuterList = EQ.begin(); OuterList != EQ.end(); ++OuterList) {
4988-
EqualExprTy F = *OuterList;
4986+
ExprSetTy F = *OuterList;
49894987
if (EqualExprsContainsExpr(F, E))
49904988
return F;
49914989
}
49924990
return { };
49934991
}
49944992

49954993
// IsEqualExprsSubset returns true if Exprs1 is a subset of Exprs2.
4996-
bool IsEqualExprsSubset(const EqualExprTy Exprs1,
4997-
const EqualExprTy Exprs2) {
4994+
bool IsEqualExprsSubset(const ExprSetTy Exprs1, const ExprSetTy Exprs2) {
49984995
for (auto I = Exprs1.begin(); I != Exprs1.end(); ++I) {
49994996
Expr *E = *I;
50004997
if (!EqualExprsContainsExpr(Exprs2, E))
@@ -5005,8 +5002,7 @@ namespace {
50055002

50065003
// DoExprSetsIntersect returns true if the intersection of Exprs1 and
50075004
// Exprs2 is nonempty.
5008-
bool DoExprSetsIntersect(const EqualExprTy Exprs1,
5009-
const EqualExprTy Exprs2) {
5005+
bool DoExprSetsIntersect(const ExprSetTy Exprs1, const ExprSetTy Exprs2) {
50105006
for (auto I = Exprs1.begin(); I != Exprs1.end(); ++I) {
50115007
Expr *E = *I;
50125008
if (EqualExprsContainsExpr(Exprs2, E))
@@ -5016,7 +5012,7 @@ namespace {
50165012
}
50175013

50185014
// EqualExprsContainsExpr returns true if the set Exprs contains E.
5019-
bool EqualExprsContainsExpr(const EqualExprTy Exprs, Expr *E) {
5015+
bool EqualExprsContainsExpr(const ExprSetTy Exprs, Expr *E) {
50205016
for (auto I = Exprs.begin(); I != Exprs.end(); ++I) {
50215017
if (EqualValue(S.Context, E, *I, nullptr))
50225018
return true;

clang/test/3C/definedType.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
// RUN: 3c -alltypes %S/definedType.checked.c | diff %S/definedType.checked.c -
66
// RUN: rm %S/definedType.checked.c
77

8-
#define size_t unsigned long
8+
#include <stddef.h>
99
_Itype_for_any(T) void *malloc(size_t size) : itype(_Array_ptr<T>) byte_count(size);
1010

1111
// From issue 204

clang/test/3C/dont_rewrite_header.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
// RUN: 3c -alltypes -addcr --output-postfix=checked %S/dont_rewrite_header.c %S/dont_rewrite_header.h
2-
// RUN: FileCheck -match-full-lines -check-prefixes="CHECK" %S/dont_rewrite_header.checked.c < %S/dont_rewrite_header.checked.c
1+
// RUN: 3c -base-dir=%S -alltypes -addcr -output-postfix=checked %S/dont_rewrite_header.c %S/dont_rewrite_header.h
2+
// RUN: FileCheck -match-full-lines -check-prefixes="CHECK" %S/dont_rewrite_header.checked.c --input-file %S/dont_rewrite_header.checked.c
33
// RUN: test ! -f %S/dont_rewrite_header.checked.h
44
// RUN: %clang -c -fcheckedc-extension -x c -o /dev/null %S/dont_rewrite_header.checked.c
5-
// RUN: 3c -alltypes -addcr --output-postfix=checked2 %S/dont_rewrite_header.checked.c %S/dont_rewrite_header.h
5+
// RUN: 3c -base-dir=%S -alltypes -addcr --output-postfix=checked2 %S/dont_rewrite_header.checked.c %S/dont_rewrite_header.h
66
// RUN: test ! -f %S/dont_rewrite_header.checked.checked2.h -a ! -f %S/dont_rewrite_header.checked.checked2.c
77
// RUN: rm %S/dont_rewrite_header.checked.c
88

clang/test/3C/extstructfields.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
// This regression test is for a 3C bug that affected vsftpd, which only builds
2+
// on Linux. Windows does not have `struct sigaction`, and we couldn't find a
3+
// reasonable way to write an analogous test that works on Windows.
4+
// UNSUPPORTED: system-windows
5+
16
// RUN: 3c -alltypes -addcr %s -- | FileCheck -match-full-lines -check-prefixes="CHECK_ALL","CHECK" %s
27
// RUN: 3c -addcr %s -- | FileCheck -match-full-lines -check-prefixes="CHECK_NOALL","CHECK" %s
38
// RUN: 3c -addcr %s -- | %clang -c -fcheckedc-extension -x c -o /dev/null -

clang/test/3C/multivardecls.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
// RUN: 3c -alltypes %S/multivardecls.checked.c -- | count 0
66
// RUN: rm %S/multivardecls.checked.c
77

8-
typedef unsigned long size_t;
8+
#include <stddef.h>
99
_Itype_for_any(T) void *malloc(size_t size) : itype(_Array_ptr<T>) byte_count(size);
1010

1111
void test() {

clang/test/3C/rewrite_header.c

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1-
// RUN: 3c -alltypes -addcr --output-postfix=checked %S/rewrite_header.c %S/rewrite_header.h
2-
// RUN: FileCheck -match-full-lines -check-prefixes="CHECK" %S/rewrite_header.checked.c < %S/rewrite_header.checked.c
3-
// RUN: FileCheck -match-full-lines -check-prefixes="CHECK" %S/rewrite_header.checked.h < %S/rewrite_header.checked.h
4-
// RUN: sed -i -e "s!rewrite_header\.h!%S/rewrite_header\.checked\.h!" %S/rewrite_header.checked.c
1+
// RUN: 3c -base-dir=%S -alltypes -addcr -output-postfix=checked %S/rewrite_header.c %S/rewrite_header.h
2+
// RUN: FileCheck -match-full-lines -check-prefixes="CHECK" %S/rewrite_header.checked.c --input-file %S/rewrite_header.checked.c
3+
// RUN: FileCheck -match-full-lines -check-prefixes="CHECK" %S/rewrite_header.checked.h --input-file %S/rewrite_header.checked.h
4+
// Avoid `sed -i` because GnuWin32's `sed -i` leaves its temporary file behind.
5+
// RUN: sed -e "s!rewrite_header\.h!rewrite_header\.checked\.h!" %S/rewrite_header.checked.c >%S/rewrite_header.checked.c.new
6+
// RUN: mv %S/rewrite_header.checked.c.new %S/rewrite_header.checked.c
57
// RUN: %clang -c -fcheckedc-extension -x c -o /dev/null %S/rewrite_header.checked.c
6-
// RUN: 3c -alltypes -addcr --output-postfix=checked2 %S/rewrite_header.checked.c %S/rewrite_header.checked.h
8+
// RUN: 3c -base-dir=%S -alltypes -addcr --output-postfix=checked2 %S/rewrite_header.checked.c %S/rewrite_header.checked.h
79
// RUN: test ! -f %S/rewrite_header.checked2.h -a ! -f %S/rewrite_header.checked2.c
810
// RUN: rm %S/rewrite_header.checked.c %S/rewrite_header.checked.h
911

clang/test/3C/root_cause.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: 3c -extra-arg="-Wno-everything" -alltypes -warn-root-cause %s 2>&1 1>%t.unused | FileCheck %s
1+
// RUN: 3c -extra-arg="-Wno-everything" -alltypes -warn-root-cause %s 2>&1 >%t.unused | FileCheck %s
22

33
// This test is unusual in that it checks for the errors in the code
44

0 commit comments

Comments
 (0)