diff --git a/clang/lib/Analysis/CFG.cpp b/clang/lib/Analysis/CFG.cpp --- a/clang/lib/Analysis/CFG.cpp +++ b/clang/lib/Analysis/CFG.cpp @@ -2839,11 +2839,30 @@ /// DeclStmts and initializers in them. CFGBlock *CFGBuilder::VisitDeclSubExpr(DeclStmt *DS) { assert(DS->isSingleDecl() && "Can handle single declarations only."); + + if (const auto *TND = dyn_cast(DS->getSingleDecl())) { + // If we encounter a VLA, process its size expressions. + const Type *T = TND->getUnderlyingType().getTypePtr(); + if (!T->isVariablyModifiedType()) + return Block; + + autoCreateBlock(); + appendStmt(Block, DS); + + CFGBlock *LastBlock = Block; + for (const VariableArrayType *VA = FindVA(T); VA != nullptr; + VA = FindVA(VA->getElementType().getTypePtr())) { + if (CFGBlock *newBlock = addStmt(VA->getSizeExpr())) + LastBlock = newBlock; + } + return LastBlock; + } + VarDecl *VD = dyn_cast(DS->getSingleDecl()); if (!VD) { - // Of everything that can be declared in a DeclStmt, only VarDecls impact - // runtime semantics. + // Of everything that can be declared in a DeclStmt, only VarDecls and the + // exceptions above impact runtime semantics. return Block; } @@ -2905,6 +2924,8 @@ } // If the type of VD is a VLA, then we must process its size expressions. + // FIXME: This does not find the VLA if it is embedded in other types, + // like here: `int (*p_vla)[x];` for (const VariableArrayType* VA = FindVA(VD->getType().getTypePtr()); VA != nullptr; VA = FindVA(VA->getElementType().getTypePtr())) { if (CFGBlock *newBlock = addStmt(VA->getSizeExpr())) @@ -3997,6 +4018,11 @@ } // VLA types have expressions that must be evaluated. + // Evaluation is done only for `sizeof`. + + if (E->getKind() != UETT_SizeOf) + return Block; + CFGBlock *lastBlock = Block; if (E->isArgumentType()) { diff --git a/clang/test/Analysis/cfg.c b/clang/test/Analysis/cfg.c new file mode 100644 --- /dev/null +++ b/clang/test/Analysis/cfg.c @@ -0,0 +1,120 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=debug.DumpCFG -triple x86_64-apple-darwin12 -fheinous-gnu-extensions %s > %t 2>&1 +// RUN: FileCheck --input-file=%t -check-prefixes=CHECK,WARNINGS %s + +// This file is the C version of cfg.cpp. +// Tests that are C-specific should go into this file. + +// CHECK-LABEL: void checkWrap(int i) +// CHECK: ENTRY +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B1] +// CHECK: Succs (21): B2 B3 B4 B5 B6 B7 B8 B9 +// CHECK: B10 B11 B12 B13 B14 B15 B16 B17 B18 B19 +// CHECK: B20 B21 B0 +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (21): B2 B3 B4 B5 B6 B7 B8 B9 +// CHECK-NEXT: B10 B11 B12 B13 B14 B15 B16 B17 B18 B19 +// CHECK-NEXT: B20 B21 B1 +void checkWrap(int i) { + switch(i) { + case 0: break; + case 1: break; + case 2: break; + case 3: break; + case 4: break; + case 5: break; + case 6: break; + case 7: break; + case 8: break; + case 9: break; + case 10: break; + case 11: break; + case 12: break; + case 13: break; + case 14: break; + case 15: break; + case 16: break; + case 17: break; + case 18: break; + case 19: break; + } +} + +// CHECK-LABEL: void checkGCCAsmRValueOutput() +// CHECK: [B2 (ENTRY)] +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B1] +// CHECK-NEXT: 1: int arg +// CHECK-NEXT: 2: arg +// CHECK-NEXT: 3: (int)[B1.2] (CStyleCastExpr, NoOp, int) +// CHECK-NEXT: 4: asm ("" : "=r" ([B1.3])); +// CHECK-NEXT: 5: arg +// CHECK-NEXT: 6: asm ("" : "=r" ([B1.5])); +void checkGCCAsmRValueOutput() { + int arg; + __asm__("" : "=r"((int)arg)); // rvalue output operand + __asm__("" : "=r"(arg)); // lvalue output operand +} + +// CHECK-LABEL: int overlap_compare(int x) +// CHECK: [B2] +// CHECK-NEXT: 1: 1 +// CHECK-NEXT: 2: return [B2.1]; +// CHECK-NEXT: Preds (1): B3(Unreachable) +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B3] +// CHECK-NEXT: 1: x +// CHECK-NEXT: 2: [B3.1] (ImplicitCastExpr, LValueToRValue, int) +// CHECK-NEXT: 3: 5 +// CHECK-NEXT: 4: [B3.2] > [B3.3] +// CHECK-NEXT: T: if [B4.5] && [B3.4] +// CHECK-NEXT: Preds (1): B4 +// CHECK-NEXT: Succs (2): B2(Unreachable) B1 +int overlap_compare(int x) { + if (x == -1 && x > 5) + return 1; + + return 2; +} + +// CHECK-LABEL: void vla_simple(int x) +// CHECK: [B1] +// CHECK-NEXT: 1: x +// CHECK-NEXT: 2: [B1.1] (ImplicitCastExpr, LValueToRValue, int) +// CHECK-NEXT: 3: int vla[x]; +void vla_simple(int x) { + int vla[x]; +} + +// CHECK-LABEL: void vla_typedef(int x) +// CHECK: [B1] +// CHECK-NEXT: 1: x +// CHECK-NEXT: 2: [B1.1] (ImplicitCastExpr, LValueToRValue, int) +// CHECK-NEXT: 3: typedef int VLA[x]; +void vla_typedef(int x) { + typedef int VLA[x]; +} + +// CHECK-LABEL: void vla_typedef_multi(int x, int y) +// CHECK: [B1] +// CHECK-NEXT: 1: y +// CHECK-NEXT: 2: [B1.1] (ImplicitCastExpr, LValueToRValue, int) +// CHECK-NEXT: 3: x +// CHECK-NEXT: 4: [B1.3] (ImplicitCastExpr, LValueToRValue, int) +// CHECK-NEXT: 5: typedef int VLA[x][y]; +void vla_typedef_multi(int x, int y) { + typedef int VLA[x][y]; +} + +// CHECK-LABEL: void vla_type_indirect(int x) +// CHECK: [B1] +// CHECK-NEXT: 1: int (*p_vla)[x]; +// CHECK-NEXT: 2: void (*fp_vla)(int *); +void vla_type_indirect(int x) { + // Should evaluate x + // FIXME: does not work + int (*p_vla)[x]; + + // Do not evaluate x + void (*fp_vla)(int[x]); +} diff --git a/clang/test/Analysis/cfg.cpp b/clang/test/Analysis/cfg.cpp --- a/clang/test/Analysis/cfg.cpp +++ b/clang/test/Analysis/cfg.cpp @@ -12,42 +12,6 @@ // off. Feel free to add tests that test only one of the CFG flavors if you're // not sure how the other flavor is supposed to work in your case. -// CHECK-LABEL: void checkWrap(int i) -// CHECK: ENTRY -// CHECK-NEXT: Succs (1): B1 -// CHECK: [B1] -// CHECK: Succs (21): B2 B3 B4 B5 B6 B7 B8 B9 -// CHECK: B10 B11 B12 B13 B14 B15 B16 B17 B18 B19 -// CHECK: B20 B21 B0 -// CHECK: [B0 (EXIT)] -// CHECK-NEXT: Preds (21): B2 B3 B4 B5 B6 B7 B8 B9 -// CHECK-NEXT: B10 B11 B12 B13 B14 B15 B16 B17 B18 B19 -// CHECK-NEXT: B20 B21 B1 -void checkWrap(int i) { - switch(i) { - case 0: break; - case 1: break; - case 2: break; - case 3: break; - case 4: break; - case 5: break; - case 6: break; - case 7: break; - case 8: break; - case 9: break; - case 10: break; - case 11: break; - case 12: break; - case 13: break; - case 14: break; - case 15: break; - case 16: break; - case 17: break; - case 18: break; - case 19: break; - } -} - // CHECK-LABEL: void checkDeclStmts() // CHECK: ENTRY // CHECK-NEXT: Succs (1): B1 @@ -84,24 +48,6 @@ static_assert(1, "abc"); } - -// CHECK-LABEL: void checkGCCAsmRValueOutput() -// CHECK: [B2 (ENTRY)] -// CHECK-NEXT: Succs (1): B1 -// CHECK: [B1] -// CHECK-NEXT: 1: int arg -// CHECK-NEXT: 2: arg -// CHECK-NEXT: 3: (int)[B1.2] (CStyleCastExpr, NoOp, int) -// CHECK-NEXT: 4: asm ("" : "=r" ([B1.3])); -// CHECK-NEXT: 5: arg -// CHECK-NEXT: 6: asm ("" : "=r" ([B1.5])); -void checkGCCAsmRValueOutput() { - int arg; - __asm__("" : "=r"((int)arg)); // rvalue output operand - __asm__("" : "=r"(arg)); // lvalue output operand -} - - // CHECK-LABEL: void F(EmptyE e) // CHECK: ENTRY // CHECK-NEXT: Succs (1): B1 @@ -136,7 +82,6 @@ (void)__builtin_object_size(dummy(), 0); } - class A { public: A() {} @@ -355,7 +300,6 @@ return x; } - // CHECK-LABEL: void test_placement_new() // CHECK: [B2 (ENTRY)] // CHECK-NEXT: Succs (1): B1 @@ -547,25 +491,88 @@ } } // namespace statement_expression_in_return -// CHECK-LABEL: int overlap_compare(int x) -// CHECK: [B2] -// CHECK-NEXT: 1: 1 -// CHECK-NEXT: 2: return [B2.1]; -// CHECK-NEXT: Preds (1): B3(Unreachable) -// CHECK-NEXT: Succs (1): B0 -// CHECK: [B3] +// CHECK-LABEL: void vla_simple(int x) +// CHECK: [B1] // CHECK-NEXT: 1: x -// CHECK-NEXT: 2: [B3.1] (ImplicitCastExpr, LValueToRValue, int) -// CHECK-NEXT: 3: 5 -// CHECK-NEXT: 4: [B3.2] > [B3.3] -// CHECK-NEXT: T: if [B4.5] && [B3.4] -// CHECK-NEXT: Preds (1): B4 -// CHECK-NEXT: Succs (2): B2(Unreachable) B1 -int overlap_compare(int x) { - if (x == -1 && x > 5) - return 1; - - return 2; +// CHECK-NEXT: 2: [B1.1] (ImplicitCastExpr, LValueToRValue, int) +// CHECK-NEXT: 3: int vla[x]; +void vla_simple(int x) { + int vla[x]; +} + +// CHECK-LABEL: void vla_typedef(int x) +// CHECK: [B1] +// CHECK-NEXT: 1: x +// CHECK-NEXT: 2: [B1.1] (ImplicitCastExpr, LValueToRValue, int) +// CHECK-NEXT: 3: typedef int VLA[x]; +void vla_typedef(int x) { + typedef int VLA[x]; +} + +// CHECK-LABEL: void vla_typealias(int x) +// CHECK: [B1] +// CHECK-NEXT: 1: x +// CHECK-NEXT: 2: [B1.1] (ImplicitCastExpr, LValueToRValue, int) +// CHECK-NEXT: 3: using VLA = int [x]; +void vla_typealias(int x) { + using VLA = int[x]; +} + +// CHECK-LABEL: void vla_typedef_multi(int x, int y) +// CHECK: [B1] +// CHECK-NEXT: 1: y +// CHECK-NEXT: 2: [B1.1] (ImplicitCastExpr, LValueToRValue, int) +// CHECK-NEXT: 3: x +// CHECK-NEXT: 4: [B1.3] (ImplicitCastExpr, LValueToRValue, int) +// CHECK-NEXT: 5: typedef int VLA[x][y]; +void vla_typedef_multi(int x, int y) { + typedef int VLA[x][y]; +} + +// CHECK-LABEL: void vla_typedefname_multi(int x, int y) +// CHECK: [B1] +// CHECK-NEXT: 1: x +// CHECK-NEXT: 2: [B1.1] (ImplicitCastExpr, LValueToRValue, int) +// CHECK-NEXT: 3: typedef int VLA[x]; +// CHECK-NEXT: 4: y +// CHECK-NEXT: 5: [B1.4] (ImplicitCastExpr, LValueToRValue, int) +// CHECK-NEXT: 6: typedef VLA VLA1[y]; +// CHECK-NEXT: 7: 3 +// CHECK-NEXT: 8: using VLA2 = VLA1 [3]; +// CHECK-NEXT: 9: 4 +// CHECK-NEXT: 10: VLA2 vla[4]; +void vla_typedefname_multi(int x, int y) { + typedef int VLA[x]; + typedef VLA VLA1[y]; + using VLA2 = VLA1[3]; + VLA2 vla[4]; +} + +// CHECK-LABEL: int vla_evaluate(int x) +// CHECK: [B1] +// CHECK-NEXT: 1: x +// CHECK-NEXT: 2: ++[B1.1] +// CHECK-NEXT: 3: [B1.2] (ImplicitCastExpr, LValueToRValue, int) +// CHECK-NEXT: 4: typedef int VLA[++x]; +// CHECK-NEXT: 5: x +// CHECK-NEXT: 6: ++[B1.5] +// CHECK-NEXT: 7: [B1.6] (ImplicitCastExpr, LValueToRValue, int) +// CHECK-NEXT: 8: sizeof(int [++x]) +// CHECK-NEXT: 9: alignof(int [++x]) +// CHECK-NEXT: 10: 0 +// CHECK-NEXT: 11: x +// CHECK-NEXT: 12: [B1.11] (ImplicitCastExpr, LValueToRValue, int) +// CHECK-NEXT: 13: return [B1.12]; +int vla_evaluate(int x) { + // Evaluates the ++x + typedef int VLA[++x]; + sizeof(int[++x]); + + // Do not evaluate the ++x + _Alignof(int[++x]); + _Generic((int(*)[++x])0, default : 0); + + return x; } // CHECK-LABEL: template<> int *PR18472()